PDA

View Full Version : Beamos + Fireball Script MkI



eXodus
01-19-2007, 02:33 AM
Finally, Filespace is back up again, and I can get to a net connection at the same time. Well I've been meaning to put these up for a while now, so here is the current state of my Beamos script...

http://www.filespace.org/coolest_rm_ever/Beamos.z

// Beamos MkI - those annoying one-eyed turrets by eXodus
//
// This is a basic Beamos script that turns around while
// scanning for Link each frame. If found, the Beamos will
// fire the FFC with ID linkFFC - or at least it should. At
// the moment it launches a stream of 5 FFCs with no regard
// for what they are, but I hope to put a standard firing
// interface on the thing at some point. You will need to
// set up the combos after the first one to be facing in
// each of numSprites directions, going clockwise from right
//
// int thisFFC - just a workaround for the Multiple FFC Bug;
// set thisFFC to the FFC's id on this screen
// int linkFFC - the (first) FFC to be fired by this Beamos
// int numSprites - the number of combos to use for when
// facing in each of the 360 directions
// bool fireSprite - if true, will display a second set of
// combos while the Beamos is firing
// int initDir - the direction to face on entering the room
// float turnRate - the number of degrees this Beamos will
// turn clockwise each frame
//

import "std.zh"

ffc script Beamos {
// detect whether Link is standing on a given point
// if !large, then only check for Link's base;
bool LinkAt(int x, int y, bool large) {
if (large) {
if ( ( x >= Link->X && x < (Link->X + 16) ) && ( y >= Link->Y && y < (Link->Y + 16) ) ) return true;
} else {
if ( ( x >= Link->X && x < (Link->X + 16) ) && ( y >= (Link->Y + 8) && y < (Link->Y + 16) ) ) return true;
}
return false;
}

// simple loop that launches numFFC FFCs in the direction
// of fireDir, starting with FFC number firstFFC; ideally
// all of the shots should be Fireballs of some kind...
void beam(int firstFFC, int numFFC, float startX, float startY, float fireDir, float fireSpeed) {
ffc shot;
for (int i = 0; i < numFFC; i++) {
shot = Screen->LoadFFC(firstFFC + i);
shot->X = startX;
shot->Y = startY;
shot->Vx = fireSpeed * Cos(fireDir);
shot->Vy = fireSpeed * Sin(fireDir);
Waitframe();
}
}

// utility routine; add two degree values and return 0-359
float degAdd(float degA, float degB) {
float temp = degA + degB;
while(temp < 0) temp += 360;
while(temp >= 360) temp -= 360;
return temp;
}

// returns the offset to the correct facing combo
int currCombo(int initCombo, int numFrames, float currDir) {
float spriteStep = 360 / numFrames;
if (currDir >= ( 360 - ( spriteStep / 2 ) ) ) return initCombo;
else return ( initCombo + ( ( currDir + ( spriteStep / 2 ) ) / spriteStep ) );
}

void run(int thisFFC, int linkFFC, int numSprites, bool fireSprite, int initDir, float turnRate) {
// anywhere you see this1-> being used, it can be replaced
// with this-> once that stupid Multiple FFC Bug is gone!
// then all these Load statements will be unnecessary...
ffc this1 = Screen->LoadFFC(thisFFC);
int initCombo = this1->Data;
int initCSet = this1->CSet;

float currDir;
float scanX;
float scanY;
float scanD;
float diffX;
float diffY;
bool found;

// initialise this Beamos' direction by adding it to 0
currDir = degAdd(0, initDir);
this1->Data = currCombo(initCombo, numSprites, currDir);

while(true) {
// calculate the X/Y intervals to scan across
this1 = Screen->LoadFFC(thisFFC);
scanX = this1->X + 8;
scanY = this1->Y + 8;
scanD = 0;
diffX = 8 * Cos(currDir);
diffY = 8 * Sin(currDir);
found = false;

while(scanD < 256 && !found) {
// increment the X/Y coordinates that get checked
scanX += diffX;
scanY += diffY;
scanD += 8;

// stop searching if a wall is hit (could be better)
if ( (Screen->ComboS[ComboAt(scanX, scanY)] > 0) && (scanD > 20) ) scanD = 256;

// if Link is in sight, set found to true
if ( LinkAt(scanX, scanY, true) ) {
scanX = (Link->X + 8);
scanY = (Link->Y + 8);
found = true;
}
}

// if Link was spotted this frame, prepare to fire...
if (found) {
this1 = Screen->LoadFFC(thisFFC);
if (fireSprite) this1->Data += numSprites;
for (int i = 0; i < 30; i++) Waitframe();
this1 = Screen->LoadFFC(thisFFC);
// firing code; should really call a function of linkFFC
beam(linkFFC, 5, this1->X, this1->Y, currDir, 8);
for (int i = 0; i < 30; i++) Waitframe();
this1 = Screen->LoadFFC(thisFFC);
if (fireSprite) this1->Data -= numSprites;
}

// after turning, change combos to face the correct way;
currDir = degAdd(currDir, turnRate);
this1->Data = currCombo(initCombo, numSprites, currDir);
Waitframe();
}
}
}


... and my fireball replacement which is still missing much of its functionality...

http://www.filespace.org/coolest_rm_ever/Fireball.z

// Fireball MkI - a customisable fireball script by eXodus
//
// This is a modular projectile script mostly intended for
// recreating fireballs. Once they're set up for a screen,
// you just have to change the FFC's Vx/Vy to activate it -
// after that they can be moved around in any way necessary.
//
// int thisFFC - just a workaround for the Multiple FFC Bug;
// set thisFFC to the FFC's id on this screen
// int size - size of the fireball's hitbox (collision area)
// int blockLvl - minimum Shield level needed to block this;
// blockLvl < 0 means Link can't block it
// int reflectLvl - Shield level required to reflect this;
// reflectLvl < 0 means mirrors affect it
// float damageLvl - the amount of damage this FFC deals
// in increments of quarter hearts
//

import "std.zh"

ffc script Fireball {
// detect whether Link is standing in an arbitrary area
// if !large, then only check for Link's base;
bool LinkIn(int areaX, int areaY, int width, int height, bool large) {
// if Area(left) is east of Link(right) then false;
if ( areaX >= (Link->X + 16) ) return false;
// if Area(top) is south of Link(bottom) then false;
if ( areaY >= (Link->Y + 16) ) return false;
// if Area(right) is west of Link(left) then false;
if ( (areaX + width) < Link->X ) return false;
// if Area(bottom) is north of Link(top) then false;
if (large) {
if ( (areaY + height) < Link->Y ) return false;
} else {
if ( (areaY + height) < (Link->Y + 8) ) return false;
}
return true;
}

// separating the code for collisions from the detection
// this one is for hitting Link; damage < 0 == no effect;
void CollisionLink(ffc shot, float damage) {
// basic code to stop a fireball
shot->Vx = 0;
shot->Vy = 0;
shot->Data = 0;

// where anything GFX/SFX related would go

if ( damage >= 0 ) {
// code for hitting Link; just deals damage now...
Link->HP -= Min(4 * damage, Link->HP);
}
}

// this one is for hitting NPCs; targetNPC == 0 == no effect;
void CollisionNPC(ffc shot, float damage, int targetNPC) {
// basic code to stop a fireball
shot->Vx = 0;
shot->Vy = 0;
shot->Data = 0;

// where anything GFX/SFX related would go

if ( targetNPC > 0 ) {
// code for hitting an NPC; just deals damage now...
npc target = Screen->LoadNPC(targetNPC);
target->HP -= Min(damage, target->HP);
}
}

void run(int thisFFC, int size, int blockLvl, int reflectLvl, float damageLvl) {
// anywhere you see this1-> being used, it can be replaced
// with this-> once that stupid Multiple FFC Bug is gone!
// then all these Load statements will be unnecessary...
ffc this1 = Screen->LoadFFC(thisFFC);
int initCombo = this1->Data;
int initCSet = this1->CSet;
// use these lines in b15+, as TileWidth/Height work now
//int offsetX = ( Div(16 * TileWidth, 2) - (size / 2) );
//int offsetY = ( Div(16 * TileHeight, 2) - (size / 2) );
int offsetX = ( Div(16, 2) - (size / 2) );
int offsetY = ( Div(16, 2) - (size / 2) );

//
//itemclass shield = Game->LoadItemClass(IC_SHIELD);
//int shieldLvl = shield->Level;

while(true) {
// (re)activate if this FFC has started moving (again)
this1 = Screen->LoadFFC(thisFFC);
if (this1->Data == 0) {
if ( ( Abs(this1->Vx) + Abs(this1->Vy) ) > 0 ) this1->Data = initCombo;
}

while(this1->Data > 0) {
// if Link is within the fireball's size*size hitbox...
if ( LinkIn((this1->X + offsetX), (this1->Y + offsetY), size, size, true) ) {
// this should actually be checking the shield, but
// that's a feature I won't be tackling just yet...
CollisionLink(this1, damageLvl);
}

// deactivate if this FFC is leaving the screen
if ( this1->X < 0 || this1->X >= 256 || this1->Y < 0 || this1->Y >= 176 ) {
CollisionNPC(this1, 0, 0);
}

Waitframe();
this1 = Screen->LoadFFC(thisFFC);
}

Waitframe();
}
}
}

... plus a simple test room to show them working in both betas 14 and 15 (I haven't had a chance to mess around with 16 yet):

http://www.filespace.org/coolest_rm_ever/BeamosTest14.qst
http://www.filespace.org/coolest_rm_ever/BeamosTest15.qst

Now, the intention of these is to make them as modular as possible, so they could easily be tidied up a lot if they remain constant throughout an entire quest. The parts that really have me stumped though are the Beamos' firing interface (calling a known, standard procedure of any linkFFC) and the fireball's shield checking - both of which have clearly been noted in the comments.

beefster09
01-19-2007, 10:10 AM
Awwwww! You kinda beat me to it!! I was doing a more complex one, though.

C-Dawg
01-19-2007, 10:27 AM
How fast does this run? It looks, at a glance, like you're solving the problem of finding Link's location on a rotating line-of-sight by checking regular intervals on that line of sight. That sounds like a TON of calculations each cycle. I guess Jman said he solved the slowdown problem, but still...

When I was trying something like this, instead of checking each individual point on the line of sight each frame, what I was trying was to define a linear function for x and y in terms of the current angle of the line of sight, then checking to see if Link was close enough to that linear function. I wasn't able to get it to work, but if you did, it should be alot more efficent because it only performs one check on Link.

Grayswandir
01-19-2007, 06:28 PM
When I was trying something like this, instead of checking each individual point on the line of sight each frame, what I was trying was to define a linear function for x and y in terms of the current angle of the line of sight, then checking to see if Link was close enough to that linear function. I wasn't able to get it to work, but if you did, it should be alot more efficent because it only performs one check on Link.



// stop searching if a wall is hit (could be better)
if ( (Screen->ComboS[ComboAt(scanX, scanY)] > 0) && (scanD > 20) ) scanD = 256;

But the current way allows for checking intervening obstacles, so you can push blocks in front of them to block. I don't think having a linear function will account for that.