Zelda Classic Tutorials
 
 

Magnetic Gloves

The Oracle sub-series introduced a number of cool new items to the Zelda series which used Link's top-down perspective in fun and new ways. One of these items was the Magnetic Gloves, which allowed Link to push/pull himself away from/towards poles. The gloves changed alignment with each use, meaning that you had to be careful while using them, lest you shoot off in the wrong direction. They also allowed Link to manipulate large iron balls to solve puzzles, such as hitting switches remotely. The magnetic force travelled through blocks.

This script implements an item that functions in the same way. Currently, it just allows for Link to fly across pits. Later, I will add the iron balls portion.

Script

This script uses the Global OnStart Script. Please refer to that page for more details on how to use this.

These are the global variables:

bool magna_gloves_active = false;
bool magna_gloves_positive = false;

const int magna_flag = 99;
const int magna_positive = 144;

This section is the script body:

Show/Hide < code block hidden >
//Courtesy of Saffith/beefster09
  bool isSolid(int x, int y) {
   
    if(x<0 || x>255 || y<0 || y>175) return false;
    int mask=1111b;
     
    if(x % 16 < 8)
      mask &= 0011b;
    else
      mask &= 1100b;
   
    if(y % 16 < 8)
      mask &= 0101b;
    else
      mask &= 1010b;
   
    int ret = Screen->ComboS[ComboAt(x, y)] & mask;
    return (ret!=0);
 
  }
 
  //functions go here
  void MagnaGlovesWork() {
    if(Link->InputB) {
     
      //Link->Action = LA_FROZEN;
     
      int lc = 0;
      bool yes = false;
     
     
      if(Link->Dir == DIR_LEFT) {
        for(int i = Floor(Link->X / 16)  - 1; i >= 0; i--) {
          lc = ComboAt(i * 16, Link->Y + 8);
          if(Screen->ComboF[lc] == magna_flag || Screen->ComboI[lc] == magna_flag) {
            yes = true;
            break;
          }
        }
        if(yes) {
          Link->Z = 5;
          Link->Jump = 0;
          Link->Y = Floor((Link->Y + 8) / 16) * 16;
          KillLinkInput();
          if(magna_gloves_positive) {
            if(!isSolid(Link->X + 16, Link->Y)) {
              Link->X += 1;
            }
            if(!isSolid(Link->X + 16, Link->Y)) {
              Link->X += 1;
            }
          } else {
            if(!isSolid(Link->X - 1, Link->Y)) {
              Link->X -= 1;
            }
            if(!isSolid(Link->X - 1, Link->Y)) {
              Link->X -= 1;
            }
          }
        }
      } else if(Link->Dir == DIR_RIGHT) {
        for(int i = Floor(Link->X / 16) + 1; i <= 16; i++) {
          lc = ComboAt(i * 16, Link->Y + 8);
         
          if(Screen->ComboF[lc] == magna_flag || Screen->ComboI[lc] == magna_flag) {
            yes = true;
            break;
          }
        }
        if(yes) {
          Link->Z = 5;
          Link->Jump = 0;
          Link->Y = Floor((Link->Y + 8) / 16) * 16;
          KillLinkInput();
          if(magna_gloves_positive) {
            if(!isSolid(Link->X - 1, Link->Y)) {
              Link->X -= 1;
            }
            if(!isSolid(Link->X - 1, Link->Y)) {
              Link->X -= 1;
            }

          } else {
            if(!isSolid(Link->X + 16, Link->Y)) {
              Link->X += 1;
            }
            if(!isSolid(Link->X + 16, Link->Y)) {
              Link->X += 1;
            }
          }
         
        }
      } else if(Link->Dir == DIR_UP) {
        for(int i = Floor((Link->Y + 9) / 16) - 1; i >= 0; i--) {
          lc = ComboAt(Link->X + 8, i * 16);
          if(Screen->ComboF[lc] == magna_flag || Screen->ComboI[lc] == magna_flag) {
            yes = true;
            break;
          }
        }
        if(yes) {
          Link->Z = 5;
          Link->Jump = 0;
          Link->X = Floor((Link->X + 8) / 16) * 16;
          KillLinkInput();
          if(!magna_gloves_positive) {
            if(!isSolid(Link->X, Link->Y - 1)) {
              Link->Y -= 1;
            }
            if(!isSolid(Link->X, Link->Y - 1)) {
              Link->Y -= 1;
            }
          } else {
           
            if(!isSolid(Link->X, Link->Y + 16)) {
              Link->Y += 1;
            }
            if(!isSolid(Link->X, Link->Y + 16)) {
              Link->Y += 1;
            }
          }
        }
      } else if(Link->Dir == DIR_DOWN) {
        for(int i = Floor(Link->Y / 16) + 1; i <= 16; i++) {
          lc = ComboAt(Link->X + 8, i * 16);
         
          if(Screen->ComboF[lc] == magna_flag || Screen->ComboI[lc] == magna_flag) {
            yes = true;
            break;
          }
        }
        if(yes) {
          Link->Z = 5;
          Link->Jump = 0;
          Link->X = Floor((Link->X + 8) / 16) * 16;
          KillLinkInput();
          if(!magna_gloves_positive) {
            if(!isSolid(Link->X, Link->Y + 16)) {
              Link->Y += 1;
            }
            if(!isSolid(Link->X, Link->Y + 16)) {
              Link->Y += 1;
            }
          } else {
            if(!isSolid(Link->X, Link->Y - 1)) {
              Link->Y -= 1;
            }
            if(!isSolid(Link->X, Link->Y - 1)) {
              Link->Y -= 1;
            }
          }
         
        }
      }
     
      if(!yes) {
        int d = Link->Dir;
        //well, we're not attached to anything.
        //so, let's move around
        if(Link->InputUp) {
          Link->InputUp = false;
          if(!isSolid(Link->X, Link->Y - 1)) Link->Y -= 1;
        }
        if(Link->InputDown) {
          Link->InputDown = false;
          if(!isSolid(Link->X, Link->Y + 16)) Link->Y += 1;
        }
        if(Link->InputLeft) {
          Link->InputLeft = false;
          if(!isSolid(Link->X - 1, Link->Y)) Link->X -= 1;
        }
        if(Link->InputRight) {
          Link->InputRight = false;
          if(!isSolid(Link->X + 16, Link->Y)) Link->X += 1;
        }
        //Link->Dir = d;
      }
    } else {
      magna_gloves_active = false;
      magna_gloves_positive = !magna_gloves_positive;
      //Link->Action = LA_NONE;
      Link->Item[magna_positive] = !Link->Item[magna_positive];
    }
  }
 
  void KillLinkInput() {
    Link->InputUp = false;
    Link->InputDown = false;
    Link->InputLeft = false;
    Link->InputRight = false;
  }

This section is the function call:

if(magna_gloves_active) MagnaGlovesWork();

It also comes with a couple of FFC scripts, that go outside the global script:

Show/Hide < code block hidden >
//balls are negative
//they are also inert
ffc script IronBall {
  void run() {
    lweapon lw;
    while(true) {
      //we only need to care if the gloves are active
      if(magna_gloves_active) {
        //first, is link aligned with us?
        if((Link->X + 8 > this->X && Link->X + 8 < this->X + 16) || (Link->Y + 8 > this->Y && Link->Y + 8 < this->Y + 16)) {
          //yes he is!
          //is he facing us?
          int d = link_direction(this);
          if(d == DIR_LEFT && Link->Dir == DIR_RIGHT ||
             d == DIR_RIGHT && Link->Dir == DIR_LEFT ||
             d == DIR_UP && Link->Dir == DIR_DOWN ||
             d == DIR_DOWN && Link->Dir == DIR_UP) {
             
            //he is! But, we're not done yet...
            //is there a pole in the way?
            bool ok = true;
            if(d == DIR_LEFT) {
              for(int i = Floor(this->X / 16) - 1; i > Floor(Link->X / 16); i--) {
                if(Screen->ComboF[ComboAt(i * 16, this->Y)] == magna_flag || Screen->ComboI[ComboAt(i * 16, this->Y)] == magna_flag) {
                  ok = false;
                }
              }
            } else if(d == DIR_RIGHT) {
              for(int i = Floor(this->X / 16) + 1; i < Floor(Link->X / 16); i++) {
                if(Screen->ComboF[ComboAt(i * 16, this->Y)] == magna_flag || Screen->ComboI[ComboAt(i * 16, this->Y)] == magna_flag) {
                  ok = false;
                }
              }
            } else if(d == DIR_UP) {
              for(int i = Floor(this->Y / 16) - 1; i > Floor(Link->Y / 16); i--) {
                if(Screen->ComboF[ComboAt(this->X, i * 16)] == magna_flag || Screen->ComboI[ComboAt(this->X, i * 16)] == magna_flag) {
                  ok = false;
                }
              }
            } else if(d == DIR_DOWN) {
              for(int i = Floor(this->Y / 16) + 1; i < Floor(Link->Y / 16); i++) {
                if(Screen->ComboF[ComboAt(this->X, i * 16)] == magna_flag || Screen->ComboI[ComboAt(this->X, i * 16)] == magna_flag) {
                  ok = false;
                }
              }
            }
           
            if(ok) {
              //yes! we get to do something!
              if(d == DIR_UP) {
                //we need to align ourselves to Link
                moveH(Link->X - this->X, this);
                //... and either move towards him...
                if(magna_gloves_positive) {
                  if(this->Y > Link->Y + 17) moveV(-2,this);
                } else {
                  // or away from him
                  moveV(2, this);
                }
              } else if(d == DIR_DOWN) {
                moveH(Link->X - this->X, this);
                if(magna_gloves_positive) {
                  if(this->Y < Link->Y - 2) moveV(2, this);
                } else {
                  moveV(-2, this);
                }
              } else if(d == DIR_LEFT) {
                moveV(Link->Y - this->Y, this);
                if(magna_gloves_positive) {
                  if(this->X > Link->X + 17) moveH(-2, this);
                } else {
                  moveH(2, this);
                }
              } else if(d == DIR_RIGHT) {
                moveV(Link->Y - this->Y, this);
                if(magna_gloves_positive) {
                  if(this->X < Link->X - 2) moveH(2, this);
                } else {
                  moveH(-2, this);
                }
              }
             
              //also, we need to damage enemies. so, we'll spawn little lweapons that are invisible
              if(lw->isValid()) lw->DeadState = WDS_DEAD;
             
              lw = Screen->CreateLWeapon(LW_SCRIPT1);
              lw->X = this->X;
              lw->Y = this->Y;
              lw->Damage = 1;
              lw->OriginalTile = 41;
              lw->Tile = 41;
            } else {
              //well, that was a wasted effort.
            }
          }
        }
      }
     
      Waitframe();
    }
   
  }
 
  void moveV(int amt, ffc this) {
    if(amt == 0) return;
    if(amt < 0) {
      for(int i = 1; i <= Abs(amt); i++) {
        if(isSolid(this->X + 8, this->Y - 1) || isSolid(this->X + 12, this->Y - 1)) break;
        this->Y--;
      }
    } else {
      for(int i = 1; i <= amt; i++) {
        if(isSolid(this->X + 4, this->Y + 1 + 15) || isSolid(this->X + 12, this->Y + 1 + 15)) break;
        this->Y++;
      }
    }
  }
 
  void moveH(int amt, ffc this) {
    if(amt == 0) return;
    if(amt < 0) {
      for(int i = 1; i <= Abs(amt); i++) {
        if(isSolid(this->X - 1, this->Y + 4) || isSolid(this->X - 1, this->Y + 12)) break;
        this->X--;
      }
    } else {
      for(int i = 1; i <= amt; i++) {
        if(isSolid(this->X + 1 + 15, this->Y + 4) || isSolid(this->X + 1 + 15, this->Y + 12)) break;
        this->X++;
      }
    }
  }
 
  int link_direction(ffc this) {
    int d_x = this->X - Link->X;
    int d_y = this->Y - Link->Y;
    int a_x = Abs(d_x);
    int a_y = Abs(d_y);
   
    if(a_x <= a_y) {
      if(d_y >= 0) {
        return DIR_UP;
      } else {
        return DIR_DOWN;
      }
    } else {
      if(d_x >= 0) {
        return DIR_LEFT;
      } else {
        return DIR_RIGHT;
      }
    }
  }
}

ffc script RotatingPole {
  void run() {
    while(true) {
      Waitframes(39);
     
      //now we rotate!
      if(Link->X >= this->X + 16 && Link->X < this->X + 18 && Link->Y >= this->Y - 4 && Link->Y <= this->Y + 8 && Link->Dir == DIR_LEFT) {
        //link is to the right
        Link->X = this->X;
        Link->Y = this->Y + 16;
        Link->Dir = DIR_UP;
      } else if(Link->X >= this->X - 18 && Link->X <= this->X - 16 && Link->Y >= this->Y - 4 && Link->Y <= this->Y + 8 && Link->Dir == DIR_RIGHT) {
        //link is to the left
        Link->X = this->X;
        Link->Y = this->Y - 16;
        Link->Dir = DIR_DOWN;
      } else if(Link->X  >= this->X - 2 && Link->X <= this->X + 8 && Link->Y >= this->Y - 18 && Link->Y <= this->Y - 16 && Link->Dir == DIR_DOWN) {
        //link is to the top
        Link->Y = this->Y;
        Link->X = this->X + 16;
        Link->Dir = DIR_LEFT;
      } else if(Link->X  >= this->X - 2 && Link->X <= this->X + 8 && Link->Y >= this->Y + 16 && Link->Y <= this->Y + 18 && Link->Dir == DIR_UP) {
        //link is to the bottom
        Link->Y = this->Y;
        Link->X = this->X - 16;
        Link->Dir = DIR_RIGHT;
      }
     
      Waitframes(9);
    }
  }
}

Function Call Arguments

There are no arguments

Updates

  • August 12, 2008 - Added the Iron Ball FFC script and the Rotating Pole FFC script. Fixed several bugs, and made the code 85% more robust.

Notes

  • This script also requires two items to be created. The first item is a "Magna Glove (-)", which should be level 0 in its item class. The second is a "Magna Glove (+)", which should be level 1 in the same item class. They both must be set to use this item script:
    item script MagnaGloves {
      void run() {
        magna_gloves_active = true;
      }
    }
  • Also, ensure that the "Keep all lower level items" flag is checked on the second item.
  • You must change the "magna_positive" constant to the number of the second item. To give the gloves to Link in the game, give him the first item. Just trust me on this.
  • You also need to change the "magna_flag" constant to the value of the flag to be used for a "magnetic pole". The script flags 98-102 are recommended. In the listing above, I used 99.
  • While in use, Link is basically flying. He'll pass right over items, enemies and combos. However, other scripts that check his position directly will still be affected.
  • Also, Link is frozen while using the gloves.
  • The Iron Ball FFC can damage enemies! You might use this to remotely hurt enemies, or for an Oracle of Seasons style Digdogger.
  • I plan for the ball to be able to push buttons. But, as triggering secrets manually is impossible, it will only be useful for scripted buttons (the likes of which I will be posting soon)
  • The rotating pole FFC should be placed on top of a combo which is marked with the magnetic flag. The script itself rotates Link around the pole (clockwise) only if he is A) facing the pole, and B) using the gloves

Screen shots

Now, how am I going to get across?

Oh, that's how!

Look ma, no floor!

Oops, sailed right over the Heart Container!