PDA

View Full Version : Fairy Partner!



pkmnfrk
12-29-2008, 12:59 AM
UPDATED: December 31, 2008 @ 10:04 pm


import "std.zh"

int fairy_r = 0;
int fairy_x = 0;
int fairy_y = 0;
int fairy_tx = 0;
int fairy_ty = 0;
int fairy_timer = 0;
bool fairy_hidden = false;
int prev_screen = 0;

const int fairy_tile = 940;
const int fairy_cset = 7;
const int fairy_timer_cnt = 240;//60 * 4; //four seconds, or so

int curScreen() {
return Game->GetCurDMapScreen()/1000 + Game->GetCurDMap();
}

global script slot2 {
void run() {

fairy_x = Link->X;
fairy_y = Link->Y;
fairy_tx = Link->X;
fairy_ty = Link->Y;
fairy_timer = fairy_timer_cnt;

while(true) {
if(Link->Item[123]) fairy();



prev_screen = curScreen();
Waitframe();
}
}

void fairy() {
//what is the fairy following?

bool yes = false;
int draw_cset = fairy_cset;

if(curScreen() != prev_screen) {
//we've warped or scrolled or something
//jump to position
fairy_x = Link->X + 8;
fairy_y = Link->Y + 8;
}

//is there an enemy close by?
if(!yes) {
int lowest_id = -1;
int lowest_dist = 1024;
for(int i = 1; i <= Screen->NumNPCs(); i++) {
npc nme = Screen->LoadNPC(i);
//all this is basically "everything that isn't *really* a monster to be killed"
if( !(nme->ID <= NPC_ABEI2 || nme->ID == NPC_ENEMYFIRE || (nme->ID >= NPC_GLEEOK1FIRE && nme->ID <= NPC_GLEEOK4FIRE) || nme->ID == NPC_GOHMAFIRE || nme->ID == NPC_ITEMFAIRY || nme->ID == NPC_SHOOTFBALL || (nme->ID >= NPC_SHOOTMAGIC && nme->ID <= NPC_SHOOTFLAME2) || nme->ID == NPC_TRAP || (nme->ID >= NPC_TRAPHORIZLOS && nme->ID <= NPC_TRAPVERTC) || (nme->ID >= NPC_TRAP8WAY && nme->ID <= NPC_TRAPCCLOCKWISELOS))) {
int d = Distance(Link->X, Link->Y, nme->X, nme->Y);
if(d < 96 && d < lowest_dist) {
lowest_dist = d;
lowest_id = i;
}
}
}

if(lowest_id != -1) {
npc nme = Screen->LoadNPC(lowest_id);
fairy_tx = nme->X + 8;
fairy_ty = nme->Y + 8;
yes = true;
draw_cset = fairy_cset + 1;
fairy_timer = fairy_timer_cnt;
}
}

//what about an item?
if(!yes) {
int lowest_id = -1;
int lowest_dist = 1024;
for(int i = 1; i <= Screen->NumItems(); i++) {
item itm = Screen->LoadItem(i);
int d = Distance(Link->X, Link->Y, itm->X, itm->Y);
if(d < 96 && d < lowest_dist) {
lowest_dist = d;
lowest_id = i;
}
}

if(lowest_id != -1) {
item itm = Screen->LoadItem(lowest_id);
fairy_tx = itm->X + 8;
fairy_ty = itm->Y + 8;
fairy_timer = fairy_timer_cnt;
yes = true;
}
}

//perhaps there's a secret flag?
if(!yes) {
int lowest_id = -1;
int lowest_dist = 1024;
for(int i = 0; i < 160; i++) {
int f;
if(Screen->ComboF[i] != 0) {
f = Screen->ComboF[i];
if((f >= 1 && f <= 6) || (f >= 9 && f <= 11) || f == 13 || (f >= 47 && f <= 51) || (f >= 68 && f <= 90)) {
int d = Distance(Link->X, Link->Y, (i % 16) * 16, Floor(i / 16) * 16);
if(d < lowest_dist) {
lowest_dist = d;
lowest_id = i;
}
}
}

if(Screen->ComboI[i] != 0) {
f = Screen->ComboI[i];
if((f >= 3 && f <= 6) || f == 11 || f == 13 || (f >= 68 && f <= 90)) {
int d = Distance(Link->X, Link->Y, (i % 16) * 16, Floor(i / 16) * 16);
if(d < lowest_dist) {
lowest_dist = d;
lowest_id = i;
}
}
}
}

if(lowest_id != -1) {
fairy_tx = (lowest_id % 16) * 16 + 8;
fairy_ty = Floor(lowest_id / 16) * 16 + 8;
draw_cset = fairy_cset + (fairy_r % 3);
fairy_timer = fairy_timer_cnt;
yes = true;
}
}

//dungeon door?
if(!yes) {
//I COULD do it closest, but I'd rather do this instead...
int side = Rand(3);
for(int i = 0; i < 4; i++) {
if(Screen->Door[side] == D_BOMB || Screen->Door[side] == D_WALKTHRU) {
yes = true;
fairy_timer = fairy_timer_cnt;
draw_cset = fairy_cset + (fairy_r % 3);
break;
}

side++;
if(side == 4) {
side = 0;
}
}

if(yes) {
if(side == DIR_UP) {
fairy_tx = 120; fairy_ty = 16;
} else if(side == DIR_DOWN) {
fairy_tx = 120; fairy_ty = 144;
} else if(side == DIR_LEFT) {
fairy_tx = 16; fairy_ty = 80;
} else if(side == DIR_RIGHT) {
fairy_tx = 224; fairy_ty = 80;
}
}


}

if(!yes) { //default to Link
fairy_tx = Link->X + 8;
fairy_ty = Link->Y + 8;
}

if(!yes) {
if(fairy_timer > 0) {
fairy_timer--;
}
}

if(fairy_timer > 0 && fairy_hidden) {
fairy_hidden = false;
}

if(fairy_x < fairy_tx) {
fairy_x++;
} else if(fairy_x > fairy_tx){
fairy_x--;
}

if(fairy_y < fairy_ty) {
fairy_y++;
} else if(fairy_y > fairy_ty){
fairy_y--;
}

int fx = 0;fairy_x;
int fy = 0;fairy_y;



fx = Sin(fairy_r * 4) * 16;
fy = Cos((fairy_r * 3)) * 16;

if(fairy_timer < 60) {
float mod = fairy_timer;
mod /= 60;
fx *= mod;
fy *= mod;
}

fairy_r += 1;

if(fairy_timer == 0 && fx == 0 && fy == 0) { //(fx == 45 || fairy_r == 225)) {
fairy_hidden = true;
}

if(!fairy_hidden) {
if(fairy_r >= 360) fairy_r = 0;

Screen->DrawTile(4, fairy_x + fx - 8, fairy_y + fy - 8, fairy_tile + (fairy_r % 2),1, 1, draw_cset, 1, 0, 0, 0, 0, true, 64);
}
}
}

YouTube video demo (http://www.youtube.com/watch?v=Ne8Vef9UR1s)

This script gives Link a new partner in crime, the Fairy! This Fairy isn't very good at healing, and she doesn't know much about enemies, but she's pretty good at finding things!

She will flit about, pointing out enemies and items that Link may have missed, and if the coast is clear, she'll lock on to any secret combos that may be near by. She'll get Link's attention not by yelling, but by blinking rapidly. Or, if there's nothing interesting going on, she'll just hang out around Link.

Technical details

The fairy is not an FFC. She's drawn in a global script, and so doesn't "actually" exist. She looks for things on the screen in a specific order, and will fly towards them constantly, until her target changes.

She will only appear if Link has a certain item in his inventory. To choose what, modify this line:


if(Link->Item[123]) fairy();

A note about valid targets: In order, she will go after:

1. Enemies
2. Items
3. Secrets
4. Link

She will go after the closest instance of whatever it is she finds. If something else becomes closer, she will change targets. Her "visibility" range is 96 pixels, and it's measured from Link. However, the range does not apply to secrets (i.e., she'll go after secrets no matter how far away you are).

Note that if there's a higher priority thing in range, she will totally ignore closer, but lower priority things. Eg, if there's an enemy near by, she will ignore all items and secrets, even if they're closer.

A note about secrets: She only looks at combo flags (regular and inherited). Even then, she only goes after "secret" flags (i.e., the ones that react to items). She will not go after other flags. She will however go after dive items.

Another thing she will not go after is dungeon secrets, such as bombable or walk-thru walls. These are not done by flags, and so aren't picked up. With enough demand, though, I might invest the time into looking for another way of doing this however...

Other details
If I get bored, I might modify her to vanish under Link's cap if there's no targets near by for a certain time.

Other than that, let me know if you have any problems!

----------

Update: December 29, 2008 @ 8:07 pm

New things:

* She will notice bombable and walk-thru dungeon walls! (If there's more than one on a screen, she'll pick one at random)
* She'll go to sleep if there's nothing for her to do! By default, she waits four seconds before vanishing underneath Link's cap.
* She now orbits around her target in a slightly more varied way, making her less annoying!

If there was a way to check where the Stairs tile was set, I'd include that as well...

----------

Update: December 31, 2008 @ 10:04 pm

New things:

* There are many enemies that Link can't hurt, either because they're not really enemies (certain projectiles), or because they're invincible (traps, etc). The fairy will ignore these NPCs.
* The fairy smartened up, and will now point out a bunch more Flags, such as push blocks and Armos Secrets.

Elmensajero
12-29-2008, 12:49 PM
This looks pretty cool, and seems like it could be pretty useful! As far as the dungeon door things go, couldn't you do something like the following after the check for secret flags?



for(int door=0;door<4;door++) //north,south,west,east
{
if(Screen->Door[door]==D_BOMB || Screen->Door[door]==D_WALKTHRU)
{
//do fairy stuff
}
}

pkmnfrk
12-29-2008, 08:03 PM
Yes, although it won't pick up custom doors and stuff. And, I only spent about 40 minutes throwing this thing together.

I'll spend some time to make the improvements I mentioned before...

Edit: I've updated the first post with a new version of the script.

ShadowTiger
12-29-2008, 09:37 PM
If I get bored, I might modify her to vanish under Link's cap if there's no targets near by for a certain time.Sorry, this can't be done.

Link's hat is stapled to his head.

Why do you think it never flies off during a spin attack? :blah:


Awesome script though. Very extensively useful, and I am absolutely, incredibly impressed with the thought you put into it with the priorities. Truly exceptional work. :thumbsup:

Russ
12-29-2008, 10:05 PM
Couldn't you just use parts from Joe's fairy script to make the fairy float around Link when there's no enemies/secrets around?

pkmnfrk
12-30-2008, 02:02 AM
Sorry, this can't be done.
Awesome script though. Very extensively useful, and I am absolutely, incredibly impressed with the thought you put into it with the priorities. Truly exceptional work. :thumbsup:

Thank you!


Couldn't you just use parts from Joe's fairy script to make the fairy float around Link when there's no enemies/secrets around?

Well, I haven't looked at Joe's script, but the fairy already floats around Link for a short while if she doesn't have anything to do. I made her disappear because she would be kinda annoying to look at if she's just buzzing around Link like a bee :P

pkmnfrk
12-31-2008, 11:07 PM
New version of the script.