PDA

View Full Version : Full-Screen Pegasus Boots



CJC
01-10-2007, 12:28 AM
Pegasus Boots, or rather a Dash script...



import "std.zh"

// ==========================================
// Dash - This script handles the dash boots.
// ==========================================

ffc script Dash{

int dashforce_maximum; // The maximum force of the player's Dash.
int dashforce_current = 0; // The current force
int i = 0; // This counter slows the rate at which
// dashforce decays, to smooth the dash.

int dash_pause = 0; // This counter prevents the player from
// holding down R for continuous dashing.

void run(){

int savedLinkX;
int savedLinkY;

while(true){



if(Link->Dir==DIR_RIGHT || Link->Dir==DIR_LEFT || Link->Dir==DIR_UP || Link->Dir==DIR_DOWN){

dashforce_maximum = 7;

}

else{

dashforce_maximum = 0;

}




if( (Link->InputR) && (dash_pause == 0) &&



(!canMove(Link->Y+17, Link->X+16)) ||
(!canMove(Link->Y+17, Link->X)) ||
(!canMove(Link->Y+17, Link->X+8)));


{

dashforce_current = dashforce_maximum;
dash_pause = 20;

}

if(Link->Dir==DIR_LEFT){
if(dashforce_current > 0){


if( (!canMove(Link->X+4, Link->Y+7)) ||
(!canMove(Link->X+8, Link->Y-7)) ||
(!canMove(Link->X+16, Link->Y))) {
dashforce_current = 0;
}
else {
Link->X = Link->X - dashforce_current;
savedLinkX = Link->X;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}
if(Link->Dir==DIR_UP){
if(dashforce_current > 0){


if( (!canMove(Link->X, Link->Y+7)) ||
(!canMove(Link->X+4, Link->Y+16)) ||
(!canMove(Link->X-4, Link->Y+16)) ||
(!canMove(Link->X+7, Link->Y+16)) ||
(!canMove(Link->X-7, Link->Y+16))) {
dashforce_current = 0;
}
else {
Link->Y = Link->Y - dashforce_current;
savedLinkY = Link->Y;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}
if(Link->Dir==DIR_RIGHT){
if(dashforce_current > 0){
if( (!canMove(Link->X+4, Link->Y+7)) ||
(!canMove(Link->X+8, Link->Y-7)) ||
(!canMove(Link->X+12, Link->Y+4)) ||
(!canMove(Link->X+16, Link->Y-4))) {
dashforce_current = 0;
}
else {
Link->X = Link->X + dashforce_current;
savedLinkX = Link->X;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}
if(Link->Dir==DIR_DOWN){
if(dashforce_current > 0){
if( (!canMove(Link->X, Link->Y+7)) ||
(!canMove(Link->X+4, Link->Y+16)) ||
(!canMove(Link->X-4, Link->Y+16)) ||
(!canMove(Link->X+7, Link->Y+16)) ||
(!canMove(Link->X-7, Link->Y+16))) {
dashforce_current = 0;
}
else {
Link->Y = Link->Y + dashforce_current;
savedLinkY = Link->Y;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}

Link->InputR = false; // This is necessary to stop R from
// flipping your selected item.


// Decrement dash_pause as long as you're on the ground.


dash_pause--;
if (dash_pause < 1) { dash_pause = 0; }



Waitframe();
} // end of while loop
} // end of void run


// ===================================
// Collision detection function
// ===================================
bool canMove(int x, int y){

if(Link->InputR){


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



This script will make your player dash across the screen as long as 'R' is held. If they hit a wall, they will move inside of the unwalkable combo slightly (Some !canMove errors I can't repair, shouldn't cause any problems though.) If stuck in a wall, simply move in the opposite direction to get out.

If you put the walls of your rooms over Link's head (With a layer), he could look like he's stuck there on purpose (Smash!). Other than that, enjoy.

I'm sure there are people out there who can improve on this, but I've done all I can for it.

This post has been edited for the purpose of posting the updated script, as well as summarizing what it does.

EDIT2: I've updated the collisions... they should be a bit better than before with wall walking. You may still encounter minor problems, but nothing too serious or permenant.

Saffith
01-10-2007, 05:42 PM
Those 'mask&' seem to be the cause of the problem. It's a binary addition... unfortunately I don't know how to add binary.
It's not binary addition, it's bitwise AND. It works like this:

10100111
& 11001010
10000010

When you AND two numbers, each bit in the result is 1 if the corresponding bits in both of the original numbers were 1. If either or both were 0, the resulting bit is 0.


Which doesn't matter, because I can't tell if the 'on' (1) is "walkable" or "unwalkable".
1 is unwalkable. The function reverses it, though, returning true if the point is walkable and false if it's not.



if( (Link->InputR) && (dash_pause == 0) &&


(!canMove(Link->X+17, Link->Y+16)) ||
(!canMove(Link->X+17, Link->Y)) ||
(!canMove(Link->X+17, Link->Y+8)) ||
(!canMove(Link->Y+17, Link->X+16)) ||
(!canMove(Link->Y+17, Link->X)) ||
(!canMove(Link->Y+17, Link->X+8)));
Now that's going to cause problems. First, the arguments to canMove are an X and Y location, respectively. Reversing them will give unpredictable results.
Also, you shouldn't put a semicolon after if(...). That'll basically make it ignore that statement altogether and execute the following block unconditionally.

That's also got to be part of the reason you're having trouble with up and down...


if(Link->Dir==DIR_DOWN){
if(dashforce_current > 0){
if(!canMove(Link->Y+4, Link->X+8)) {
[...]
if(Link->Dir==DIR_UP){
if(dashforce_current > 0){
if(!canMove(Link->Y+4, Link->X+8)) {
The arguments are reversed there. You probably would want canMove(Link->X, Link->Y+7) and canMove(Link->X+8, Link->Y+16) for up, and canMove(Link->X, Link->Y+16) and canMove(Link->X+8, Link->Y+16) for down. That'll check the points directly above and below the small hitbox. You'll also want to throw in his right edge if you want to handle Z3-style movement.


But then, you've changed the canMove() function, and I don't really see why. Originally, it tested whether a given point was walkable, which doesn't depend on Link's direction at all. Why is it you're using different masks for different directions?

CJC
01-10-2007, 06:00 PM
Well, initailly when I was doing the sidescroller boots, Link would get stuck farther in a combo when dashing at it to the right than to the left. When I flipped the X masks, it seemed to make both directions get trapped the same distance (4 pixels into the unwalkable).

I figured it was handling combos based on the direction he hit them, so if I flipped the coordinates when moving a different direction it would fix the problems.

Unfortunately, I don't know much about scripting and so most of what I was doing was just guessing. Since it was modified from a jumping script, I had figured taking the inverse equations (Switching X and Y) would convert it to a dashing function.

But I'll give it another shot, with your recommendations.


Oh... and does that mean the canMove() function will react differently to a large Link hit box?

Saffith
01-10-2007, 06:14 PM
Ah, I see. Well, that's not really quite what the function does. The name "canMove" is a little misleading (which is why it was changed to "WalkableAt" later). It doesn't actually check whether Link can move to a given location; it checks whether a single pixel is walkable or not. If you want to see whether Link can move in a given direction, you need to test one, two, or three points (depending on the direction, hitbox size, and whether Z3-movement will be allowed) in the direction he'll be moving.
So, it doesn't actually work differently with a large hitbox, but you have to use it differently. Unfortunately, there's no way to tell from the script which he's using, so the best you can do is handle both and let the user pick which to use.

C-Dawg
01-10-2007, 06:22 PM
Why would it be necessary to check three points for Z3 movement? Even at a diagonal, you only need to check whether the combo North of the player is walkable, or East of the player is walkable, at any one time.

Saffith
01-10-2007, 06:39 PM
You have to consider that Link may not be centered on a tile and that individual quarters of combos can be solid or walkable. If Link's moving upward, four pixels to the left of being centered, his left side, right side, and center are moving toward three different solidity zones.

CJC
01-10-2007, 07:11 PM
I reworked the collision function so that it's not direction-stressed, and now I'm working on the collision parameters themselves. I think I'm on to something...


Nah, it doesn't work. I thought maybe if I made it check every four pixels it would stop him, but he just keeps on running.


I'll try again, but I wish I could just have him stop whenever he hits an unwalkable combo.



import "std.zh"

// ==========================================
// Dash - This script handles the dash boots.
// ==========================================

ffc script Dash{

int dashforce_maximum; // The maximum force of the player's Dash.
int dashforce_current = 0; // The current force
int i = 0; // This counter slows the rate at which
// dashforce decays, to smooth the dash.

int dash_pause = 0; // This counter prevents the player from
// holding down R for continuous dashing.

void run(){

int savedLinkX;
int savedLinkY;

while(true){



if(Link->Dir==DIR_RIGHT || Link->Dir==DIR_LEFT || Link->Dir==DIR_UP || Link->Dir==DIR_DOWN){

dashforce_maximum = 7;

}

else{

dashforce_maximum = 0;

}




if( (Link->InputR) && (dash_pause == 0) &&



(!canMove(Link->Y+17, Link->X+16)) ||
(!canMove(Link->Y+17, Link->X)) ||
(!canMove(Link->Y+17, Link->X+8)));


{

dashforce_current = dashforce_maximum;
dash_pause = 20;

}

if(Link->Dir==DIR_LEFT){
if(dashforce_current > 0){


if( (!canMove(Link->X, Link->Y+7)) ||
(!canMove(Link->X+8, Link->Y+16)) ||
(!canMove(Link->X-8, Link->Y+16))) {
dashforce_current = 0;
}
else {
Link->X = Link->X - dashforce_current;
savedLinkX = Link->X;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}
if(Link->Dir==DIR_UP){
if(dashforce_current > 0){


if( (!canMove(Link->X, Link->Y+7)) ||
(!canMove(Link->X+8, Link->Y+16)) ||
(!canMove(Link->X-8, Link->Y+16))) {
dashforce_current = 0;
}
else {
Link->Y = Link->Y - dashforce_current;
savedLinkY = Link->Y;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}
if(Link->Dir==DIR_RIGHT){
if(dashforce_current > 0){
if( (!canMove(Link->X, Link->Y+7)) ||
(!canMove(Link->X+8, Link->Y+16)) ||
(!canMove(Link->X-8, Link->Y+16))) {
dashforce_current = 0;
}
else {
Link->X = Link->X + dashforce_current;
savedLinkX = Link->X;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}
if(Link->Dir==DIR_DOWN){
if(dashforce_current > 0){
if( (!canMove(Link->X, Link->Y+7)) ||
(!canMove(Link->X+8, Link->Y+16)) ||
(!canMove(Link->X-8, Link->Y+16))){
dashforce_current = 0;
}
else {
Link->Y = Link->Y + dashforce_current;
savedLinkY = Link->Y;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}

Link->InputR = false; // This is necessary to stop R from
// flipping your selected item.


// Decrement dash_pause as long as you're on the ground.


dash_pause--;
if (dash_pause < 1) { dash_pause = 0; }



Waitframe();
} // end of while loop
} // end of void run


// ===================================
// Collision detection function
// ===================================
bool canMove(int x, int y){

if(Link->InputR){


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





if(Link->Dir==DIR_UP){
if(dashforce_current > 0){


if( (!canMove(Link->X, Link->Y+7)) ||
(!canMove(Link->X+8, Link->Y+16)) ||
(!canMove(Link->X-8, Link->Y+16))) {
dashforce_current = 0;
}
else {
Link->Y = Link->Y - dashforce_current;
savedLinkY = Link->Y;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}

and



}
if(Link->Dir==DIR_RIGHT){
if(dashforce_current > 0){
if( (!canMove(Link->X, Link->Y+7)) ||
(!canMove(Link->X+8, Link->Y+16)) ||
(!canMove(Link->X-8, Link->Y+16))) {
dashforce_current = 0;
}
else {
Link->X = Link->X + dashforce_current;
savedLinkX = Link->X;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}


are giving me problems. When he dashes right or up, Link goes inside the unwalkable combo before stopping. Any suggestions? I know I need to change the !canMove conditions, but I don't know what I should change them to.
Currently, all four directions are using the same !canMove conditions.

Left and Down seem to be working fine, even on offcenter positions.



EDIT: Funny thing, if you use this code on a side-scroller you can dash in midair. And if you dash up, it's a double-jump! Dashing down doesn't do much, though.

C-Dawg
01-10-2007, 07:28 PM
I wish there was a way to add to Link's VELOCITY without adding to his actual X-Y coordinates. That would let us use the game's natural collision-detection and solve all of these problems.

And you know what? There's no reason why there shouldn't be a functions that do this. I'll see if I can't program up some functions:

void set_player_velocity(int direction, int speed);
Gives the player "velocity." Sets a direction and a speed. Since the player can only move 1 pixel, minimum, not portions of a pixel, speeds below 1 will be simulated by loading a delay value into a variable. The effect will be that the player will move 1 pixel per number of tics equal to the delay value. Passing 0,0 clears all variables.

void move_player_velocity();
Moves the player in the given direction at a given speed, provided that it doesn't cause walkability issues. Call it once before Waitframe(); in your while loop. Will do nothing if set_player_velocity is not set, or is set to 0.

CJC
01-10-2007, 09:34 PM
I tried something with those codes, C-Dawg, but I couldn't get it to do anything.

Anyways, I've sort of fixed the collision detection on my script (At lest to the degree that I'm willing to attempt.)

He still phases partially through some portions of a walkable combo, but not enough to get stuck or skip areas (Hopefully). It's a little less than a quarter of a tile.



import "std.zh"

// ==========================================
// Dash - This script handles the dash boots.
// ==========================================

ffc script Dash{

int dashforce_maximum; // The maximum force of the player's Dash.
int dashforce_current = 0; // The current force
int i = 0; // This counter slows the rate at which
// dashforce decays, to smooth the dash.

int dash_pause = 0; // This counter prevents the player from
// holding down R for continuous dashing.

void run(){

int savedLinkX;
int savedLinkY;

while(true){



if(Link->Dir==DIR_RIGHT || Link->Dir==DIR_LEFT || Link->Dir==DIR_UP || Link->Dir==DIR_DOWN){

dashforce_maximum = 7;

}

else{

dashforce_maximum = 0;

}




if( (Link->InputR) && (dash_pause == 0) &&



(!canMove(Link->Y+17, Link->X+16)) ||
(!canMove(Link->Y+17, Link->X)) ||
(!canMove(Link->Y+17, Link->X+8)));


{

dashforce_current = dashforce_maximum;
dash_pause = 20;

}

if(Link->Dir==DIR_LEFT){
if(dashforce_current > 0){


if( (!canMove(Link->X+4, Link->Y+7)) ||
(!canMove(Link->X+8, Link->Y+9))) {
dashforce_current = 0;
}
else {
Link->X = Link->X - dashforce_current;
savedLinkX = Link->X;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}
if(Link->Dir==DIR_UP){
if(dashforce_current > 0){


if( (!canMove(Link->X, Link->Y+7)) ||
(!canMove(Link->X+4, Link->Y+16)) ||
(!canMove(Link->X-4, Link->Y+16)) ||
(!canMove(Link->X+7, Link->Y+16)) ||
(!canMove(Link->X-7, Link->Y+16))) {
dashforce_current = 0;
}
else {
Link->Y = Link->Y - dashforce_current;
savedLinkY = Link->Y;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}
if(Link->Dir==DIR_RIGHT){
if(dashforce_current > 0){
if( (!canMove(Link->X+4, Link->Y+7)) ||
(!canMove(Link->X+8, Link->Y+9)) ||
(!canMove(Link->X+12, Link->Y+4))) {
dashforce_current = 0;
}
else {
Link->X = Link->X + dashforce_current;
savedLinkX = Link->X;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}
if(Link->Dir==DIR_DOWN){
if(dashforce_current > 0){
if( (!canMove(Link->X, Link->Y+7)) ||
(!canMove(Link->X+4, Link->Y+16)) ||
(!canMove(Link->X-4, Link->Y+16)) ||
(!canMove(Link->X+7, Link->Y+16)) ||
(!canMove(Link->X-7, Link->Y+16))) {
dashforce_current = 0;
}
else {
Link->Y = Link->Y + dashforce_current;
savedLinkY = Link->Y;
if(i == 2){
i = 0;
}
else{
dashforce_current--;
i++;
}
}
}
}

Link->InputR = false; // This is necessary to stop R from
// flipping your selected item.


// Decrement dash_pause as long as you're on the ground.


dash_pause--;
if (dash_pause < 1) { dash_pause = 0; }



Waitframe();
} // end of while loop
} // end of void run


// ===================================
// Collision detection function
// ===================================
bool canMove(int x, int y){

if(Link->InputR){


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



So this is the final script for now. I've updated the first post as well.
Press R and run across the screen!

{Do note, this code also functions in side-scrollers and works almost exactly the same as my 'Side Scroller' pegasus boots script. However, if you dash upward while jumping (With C-Dawg's jump, hasn't been tested with Roc's Feather), you'll fly up until you leave the screen or hit a combo. Unless you want to be superman, make a suplementary FFC code that'll disable R when facing up or down, and place it on side-scroller screens.)

(Also note that this script will not make Link run with his sword out, so if you dash into monsters you will take damage. Take care where you allow the dashing, too, because it ignores gravity and has the potential to pass through half-walkable combos.)

Master_of_Power
01-12-2007, 07:51 PM
how would I modify it to react only if you have a certain item?

CJC
01-12-2007, 07:54 PM
I'm currently working on doing just that. It involves an item and a global script instead of an FFC to achieve, though. I'll get back to you when I've got more.


Currently, I'm encountering some issues with a global variable (Mainly... I don't know how to designate a variable as a global variable.)
Once I solve that, it should be easy enough.