namespace PegasusBoots
{
bool UserInterruptDash()
{
// Extra things that interrupt the dash go here
return false;
}
bool UserDisableDash()
{
// Things that should disable the dash go here
return UserInterruptDash(); // Things that interrupt the dash should also probably disable it
}
float UserModifyDashSpeed(int oldspeed)
{
// Any special logic here for changing dash speed based on other scripts
return oldspeed;
}
bool UserEndDash(int dir, int speed)
{
// Special dash handling goes here. Return true if it should replace the normal halting period
return false;
}
bool IsDashing()
{
genericdata gd = Game->LoadGenericData(Game->GetGenericScript("PegasusBoots_Generic"));
return gd->Data[DDI_CURRENTLYDASHING];
}
// Ancient Stone Tablet
const int I_DASH_TURN_UPGRADE = 0; // If >0, this item grants the ability to turn 90 degrees while dashing
// Dash tiles
const int TIL_LINK_DASHFRAMES = 33260;
const int ASPEED_DASH1 = 2; // A.Speed for dash windup
const int ASPEED_DASH2 = 3; // A.Speed for dash
// SFX
const int SFX_DASH = 70;
const int SFX_DASH_BOUNCE = 71;
const int FREQ_SFX_DASH_WINDUP = 6;
const int FREQ_SFX_DASH = 8;
// Sprites
const int SPR_DASH_DUST = 105; // Sprite for dust particles trailing behind Link
const int FREQ_DASH_DUST = 8;
const int STEP_DASH_DUST = 50; // The particles move away from Link slightly with this step
// Bouncing
const int DASH_BOUNCE_JUMP = 1.2; // Height when bouncing off a wall
// Speed settings
const float DASH_BASE_SPEED = 0.0; // Speed at the start of the dash
const float DASH_TOP_SPEED = 4.0; // Speed at the end of the dash
const int DASH_BOUNCE_SPEED = 1.0; // Speed when bouncing off a wall
const float DASH_STEERING_MULTIPLIER = 0.25; // Multiplier for strafing while dashing
const float DASH_COLLISION_MULTIPLIER = 1.0; // Multiplier for dampening speed when dashing through crystals (1.0 is disabled)
// Durations
const int DASH_WINDUP_FRAMES = 16; // Frames before Link starts moving
const int DASH_ACCEL_FRAMES = 32; // Frames to reach top speed
const int DASH_HALT_FRAMES = 16; // Frames to reach a stop after letting go
const int DASH_COLLISION_FRAMES = 4; // Frames Link's speed is changed after a collision
const int DASH_COLLISION_FRAMES2 = 8; // Frames it takes for his speed to return to normal after
// Fake sword settings
const bool DASH_ENABLE_SWORD = true; // If true, can hold the sword during the dash
const bool DASH_REQUIRE_SWORD_EQUIPPED = true; // If true, the sword has to be equipped to a button in order to use it with the dash
const bool DASH_CRYSTAL_REQUIRES_SWORD = true; // If true, smashing crystals requires the sword be equipped
const int FAKESWORD_DIST = 12; // Distance from Link the sword extends
const int FAKESWORD_SIDE_OFFSET = 4; // Distance to the left/right of Link the sword is offset when facing up/down
const int FAKESWORD_OFFSET_UPDOWN = 0; // Distance right from Link the sword is offset when facing up/down
const int FAKESWORD_OFFSET_LEFTRIGHT = 2; // Distance down from Link the sword is offset when facing left/right
const int LW_FAKESWORD = LW_SCRIPT10;
// Misc dash settings
const bool DASH_USES_LTM = false; // If true, Link's tile modifiers get added to the dash tiles
const bool DASH_LOWERS_SHIELD = false; // If true, dashing puts Link in the "attacking" state
const bool DASH_CAN_SIDE_SMASH = true; // If true, dashing into crystals from the side can still break them
const bool DASH_ALLOW_FEATHER = true; // If true, the feather can still be used while dashing
enum DashDataIndices
{
DDI_ITEMLAUNCHED,
DDI_ITEMID,
DDI_CURRENTLYDASHING,
DDI_SIZE
};
enum DashStates
{
DST_WINDUP,
DST_DASHING,
DST_BOUNCE,
DST_DASHTHROUGH
};
enum FrameCountIdx
{
FCI_ANIMTIMER,
FCI_ANIMFRAME,
FCI_SFXTIMER,
FCI_SPRITETIMER,
FCI_SIZE
};
enum DashCollision
{
DC_COLLIDEDCRYSTAL,
DC_FORCEBOUNCE,
DC_CANBEAKCRYSTAL,
DC_SIZE
};
itemdata script PegasusBoots
{
void run()
{
if(IsSideview()&&Link->Dir<DIR_LEFT)
Quit();
if(UserDisableDash())
Quit();
// Because of timing issues, most of this item runs through a generic script
genericdata gd = Game->LoadGenericData(Game->GetGenericScript("PegasusBoots_Generic"));
gd->Running = false;
gd->DataSize = DDI_SIZE;
gd->Data[DDI_ITEMLAUNCHED] = true;
gd->Data[DDI_ITEMID] = this->ID;
gd->Running = true;
}
}
generic script PegasusBoots_Generic
{
void run()
{
this->DataSize = DDI_SIZE;
// The script reloads on F6 so it knows to quit at those times
// If it was started by anything but the item script, it'll quit
this->ReloadState[GENSCR_ST_RELOAD] = true;
this->ReloadState[GENSCR_ST_CONTINUE] = true;
Link->ScriptTile = 0;
// Quit if not launched by the item
if(!this->Data[DDI_ITEMLAUNCHED])
{
this->Data[DDI_CURRENTLYDASHING] = false;
Quit();
}
// And turn off the flag for next time
this->Data[DDI_ITEMLAUNCHED] = false;
int itemID = this->Data[DDI_ITEMID];
int frameCount[FCI_SIZE];
int dashState = DST_WINDUP;
int dashTimer = DASH_WINDUP_FRAMES;
int dashDampenTimer;
int dashDir = Link->Dir;
float dashSpeed = DASH_BASE_SPEED;
int swordID = GetSwordID();
// We're loading a script slot here to know how to identify PegasusCrystal combos
// All the actual logic is in this script
int crystalSlot = Game->GetComboScript("PegasusCrystal");
WaitTo(SCR_TIMING_POST_GLOBAL_ACTIVE);
// ReleaseTimer is a janky workaround to an issue with Link inputs and scrolling
// Because scrolling stops input polling, we need to give some
// leeway for letting go of the button. This way the frame when scroll begins
// won't count as the dash ending
int releaseTimer;
if(UsingItem(itemID))
releaseTimer = 2;
while((releaseTimer||Game->Scrolling[SCROLL_DIR]!=-1||dashState==DST_BOUNCE)&&!InterruptDash())
{
if(!UsingItem(itemID)&&Game->Scrolling[SCROLL_DIR]==-1)
--releaseTimer;
else
releaseTimer = 2;
bool swordEquipped = SwordEquipped(swordID);
// Update the dashing animation
// This also plays sounds and creates dust sprites
switch(dashState)
{
case DST_WINDUP:
UpdateAnim(frameCount, ASPEED_DASH1, FREQ_SFX_DASH_WINDUP, swordEquipped);
break;
case DST_DASHING:
UpdateAnim(frameCount, ASPEED_DASH2, FREQ_SFX_DASH, swordEquipped);
break;
case DST_BOUNCE:
UpdateAnim(frameCount, ASPEED_DASH2, 0, swordEquipped);
break;
}
//Movement logic. Doesn't happen during scrolling.
if(Game->Scrolling[SCROLL_DIR]==-1)
{
switch(dashState)
{
// Windup just has Link stand in place for a bit while animating
case DST_WINDUP:
{
if(dashTimer)
--dashTimer;
else
{
this->Data[DDI_CURRENTLYDASHING] = true;
dashState = DST_DASHING;
dashTimer = DASH_ACCEL_FRAMES;
}
break;
}
// The part where he actually moves forward
case DST_DASHING:
{
dashSpeed = Lerp(DASH_TOP_SPEED, DASH_BASE_SPEED, dashTimer/DASH_ACCEL_FRAMES);
// Dampen speed after hitting a crystal
if(dashDampenTimer)
{
if(dashDampenTimer<DASH_COLLISION_FRAMES2)
dashSpeed *= Lerp(1.0, DASH_COLLISION_MULTIPLIER, dashDampenTimer/DASH_COLLISION_FRAMES2);
else
dashSpeed *= DASH_COLLISION_MULTIPLIER;
--dashDampenTimer;
}
dashSpeed = UserModifyDashSpeed(dashSpeed);
Link->MoveXY(DirX(dashDir)*dashSpeed, DirY(dashDir)*dashSpeed);
if(I_DASH_TURN_UPGRADE)
{
if(Link->Item[I_DASH_TURN_UPGRADE])
{
if(Link->InputUp)
dashDir = DIR_UP;
else if(Link->InputDown)
dashDir = DIR_DOWN;
else if(Link->InputLeft)
dashDir = DIR_LEFT;
else if(Link->InputRight)
dashDir = DIR_RIGHT;
}
}
else if(DASH_STEERING_MULTIPLIER)
{
if(dashDir<DIR_LEFT)
{
Link->MoveXY(((Link->InputLeft?-1:0)+(Link->InputRight?1:0)) * dashSpeed * DASH_STEERING_MULTIPLIER, 0);
}
else
{
Link->MoveXY(0, ((Link->InputUp?-1:0)+(Link->InputDown?1:0)) * dashSpeed * DASH_STEERING_MULTIPLIER);
}
}
if(dashTimer)
--dashTimer;
untyped collData[DC_SIZE];
collData[DC_CANBEAKCRYSTAL] = true;
if(DASH_CRYSTAL_REQUIRES_SWORD)
{
collData[DC_CANBEAKCRYSTAL] = swordEquipped;
}
if(!CanDash(Round(Link->X), Round(Link->Y), dashDir, crystalSlot, collData)||collData[DC_FORCEBOUNCE])
{
int collideX = Link->X;
int collideY = Link->Y;
if(!collData[DC_FORCEBOUNCE])
{
// Try moving Link to see if he can slip around a corner. Only bonk if he can't
for(int i=0; i<4; ++i)
{
Link->MoveXY(DirX(dashDir), DirY(dashDir));
}
}
if(collData[DC_FORCEBOUNCE]||!CanDash(Round(Link->X), Round(Link->Y), dashDir, crystalSlot, collData))
{
this->Data[DDI_CURRENTLYDASHING] = false;
dashState = DST_BOUNCE;
Link->Jump = DASH_BOUNCE_JUMP;
Game->PlaySound(SFX_DASH_BOUNCE);
}
Link->X = collideX;
Link->Y = collideY;
}
if(collData[DC_COLLIDEDCRYSTAL])
dashDampenTimer = DASH_COLLISION_FRAMES + DASH_COLLISION_FRAMES2;
break;
}
// And the part where he bonks into a wall
case DST_BOUNCE:
{
if(Link->Jump>0||Link->Z>0)
{
Link->MoveXY(-DirX(dashDir)*DASH_BOUNCE_SPEED, -DirY(dashDir)*DASH_BOUNCE_SPEED);
NoAction();
}
else
{
// When the bounce ends, restore Link's tile and end the script
Link->ScriptTile = 0;
Quit();
}
break;
}
}
if(DASH_LOWERS_SHIELD)
{
// To make Link lower his shield, we use a janky method of
// switching in and out of the attacking state
if(!Link->Falling&&!Link->Drowning&&(Link->Action!=LA_FALLING&&Link->Action!=LA_DROWNING&&Link->Action!=LA_HOPPING&&Link->Action!=LA_SWIMMING))
{
Link->Action = LA_NONE;
Link->Action = LA_ATTACKING;
// Because the attacking state disables feather, we need a workaround to use it
TryImitateFeather();
}
}
else
{
NoActionPegasus();
}
}
WaitTo(SCR_TIMING_POST_GLOBAL_WAITDRAW);
// Setting direction stops drowning for some reason
if(Link->Action!=LA_FALLING&&Link->Action!=LA_DROWNING)
Link->Dir = dashDir;
// Draw swords post waitdraw so their positions during scrolling are more accurate
switch(dashState)
{
case DST_WINDUP:
DrawSword(swordID, true);
break;
case DST_DASHING:
DrawSword(swordID, false);
break;
case DST_BOUNCE:
DrawSword(swordID, true);
break;
}
WaitTo(SCR_TIMING_POST_GLOBAL_ACTIVE);
}
this->Data[DDI_CURRENTLYDASHING] = false;
// This is a short skid animation at the end of the dash
if(!UserEndDash(dashDir, dashSpeed))
{
for(int i=0; i<DASH_HALT_FRAMES&&!UserInterruptDash()&&Link->Action!=LA_DROWNING&&!Link->Falling; ++i)
{
UpdateAnim(frameCount, ASPEED_DASH2, FREQ_SFX_DASH, SwordEquipped(swordID));
Link->MoveXY(DirX(dashDir)*Lerp(dashSpeed, 0, i/DASH_HALT_FRAMES), DirY(dashDir)*Lerp(dashSpeed, 0, i/DASH_HALT_FRAMES));
WaitTo(SCR_TIMING_POST_GLOBAL_WAITDRAW);
WaitTo(SCR_TIMING_POST_GLOBAL_ACTIVE);
}
}
// Always revert ScriptTile when the script ends
Link->ScriptTile = 0;
}
void UpdateAnim(int frameCount, int aspeed, int dashfreq, bool swordEquipped)
{
// Animation frame count. Will always go for 4 frames of animation, one of which is recycled
++frameCount[FCI_ANIMTIMER];
if(frameCount[FCI_ANIMTIMER]>aspeed)
{
frameCount[FCI_ANIMTIMER] = FCI_ANIMTIMER;
++frameCount[FCI_ANIMFRAME];
frameCount[FCI_ANIMFRAME] %= 4;
}
// Here we handle the recycled animation frame
int til = TIL_LINK_DASHFRAMES+3*Link->Dir;
switch(frameCount[FCI_ANIMFRAME])
{
case 1:
til += 1;
break;
case 3:
til += 2;
break;
}
// Add Link tile modifiers if needed
if(DASH_USES_LTM)
{
til += Link->TileMod;
}
if(Link->Z>0&&!swordEquipped)
Link->ScriptTile = 0;
else
Link->ScriptTile = til;
// Frame counter for SFX
if(dashfreq)
{
if(frameCount[FCI_SFXTIMER]==0)
{
Game->PlaySound(SFX_DASH);
}
++frameCount[FCI_SFXTIMER];
frameCount[FCI_SFXTIMER] %= dashfreq;
}
// And for sprites
if(frameCount[FCI_SPRITETIMER]==0&&Game->Scrolling[SCROLL_DIR]==-1)
{
bool onGround;
// Don't create dust if not on the ground
if(IsSideview())
onGround = Link->Standing&&Link->Jump==0&&Link->FakeJump==0;
else
onGround = Link->Z==0&&Link->FakeZ==0;
if(onGround)
{
lweapon dust = CreateLWeaponAt(LW_SPARKLE, Link->X+Rand(-6, 6), Link->Y+8+Rand(-4, 4));
dust->UseSprite(SPR_DASH_DUST);
dust->CollDetection = false;
dust->Dir = OppositeDir(Link->Dir);
dust->Step = STEP_DASH_DUST;
}
}
++frameCount[FCI_SPRITETIMER];
frameCount[FCI_SPRITETIMER] %= FREQ_DASH_DUST;
}
bool InterruptDash()
{
if(Link->Falling)
return true;
if(UserInterruptDash())
return true;
// This is just a set of actions during which Link is able to keep dashing
// All others will end the dash early
switch(Link->Action)
{
case LA_NONE:
case LA_WALKING:
case LA_SCROLLING:
case LA_ATTACKING:
return false;
}
return true;
}
int GetSwordID()
{
int highestLevel = -1;
int highestID = -1;
// This finds the highest level sword in Link's inventory
// It will then be used to get tiles and damage values for the fake one
for(int i=0; i<256; ++i)
{
if(Link->Item[i])
{
itemdata id = Game->LoadItemData(i);
if(id->Type==IC_SWORD)
{
if(id->Level>=highestLevel)
{
highestID = i;
highestLevel = id->Level;
}
}
}
}
return highestID;
}
bool SwordEquipped(int swordID)
{
return Link->ItemA==swordID||Link->ItemB==swordID||Link->ItemX==swordID||Link->ItemY==swordID;
}
void DrawSword(int swordID, bool nocoll)
{
// Sword disabled
if(!DASH_ENABLE_SWORD)
return;
// No sword item
if(swordID==-1)
return;
if(DASH_REQUIRE_SWORD_EQUIPPED)
{
// If sword must be equipped on the button
if(!SwordEquipped(swordID))
return;
}
itemdata id = Game->LoadItemData(swordID);
int damage = id->Power * 2;
spritedata sd = Game->LoadSpriteData(id->Sprites[0]);
int til = sd->Tile;
int cs = sd->CSet;
int x = Link->X+Link->DrawXOffset;
int y = Link->Y+Link->DrawYOffset-Link->Z-Link->FakeZ;
// Offset the sword for scrolling transitions
if(Game->Scrolling[SCROLL_DIR]>-1)
{
x += Game->Scrolling[SCROLL_NX];
y += Game->Scrolling[SCROLL_NY];
}
int flip = 0;
int lyr = SPLAYER_PLAYER_DRAW;
switch(Link->Dir)
{
case DIR_UP:
x += FAKESWORD_OFFSET_UPDOWN - FAKESWORD_SIDE_OFFSET;
y -= FAKESWORD_DIST;
lyr = SPLAYER_PUSHBLOCK;
// For whatever reason the push block layer doesn't draw during scrolling
if(Game->Scrolling[SCROLL_DIR]!=-1)
{
lyr = IsBackgroundLayer(2)?1:2;
}
break;
case DIR_DOWN:
x += FAKESWORD_OFFSET_UPDOWN + FAKESWORD_SIDE_OFFSET;
y += FAKESWORD_DIST;
flip = 2;
break;
case DIR_LEFT:
x -= FAKESWORD_DIST;
y += FAKESWORD_OFFSET_LEFTRIGHT;
++til;
flip = 1;
break;
case DIR_RIGHT:
x += FAKESWORD_DIST;
y += FAKESWORD_OFFSET_LEFTRIGHT;
++til;
break;
}
Screen->DrawTile(lyr, x, y, til, 1, 1, cs, -1, -1, 0, 0, 0, flip, true, 128);
if(!nocoll&&Game->Scrolling[SCROLL_DIR]==-1)
{
lweapon hitbox = FireLWeaponDir(LW_FAKESWORD, x, y+Link->Z, Link->Dir, 0, damage);
hitbox->Z = Link->Z;
hitbox->Timeout = 2;
hitbox->Weapon = LW_SWORD;
hitbox->DrawYOffset = -1000;
}
}
bool CanDash(int x, int y, int dir, int crystalSlot, untyped collData)
{
bool ret = true;
int ht = 8;
if(Game->FFRules[qr_LTTPCOLLISION])
ht = 0;
switch(dir)
{
case DIR_UP:
for(int i=0; i<=15; i=Min(i+8, 15))
{
if(!CanDashPixel(x+i, y+ht-1, crystalSlot, collData))
ret = false;
if(i==15)
break;
}
// Break crystals from the side when able to steer dash
if(DASH_CAN_SIDE_SMASH)
{
// We're using these for their secondary effects, not return value
if(Link->InputLeft)
CanDashPixel(x-1, y+ht, crystalSlot, collData);
if(Link->InputRight)
CanDashPixel(x+16, y+ht, crystalSlot, collData);
}
break;
case DIR_DOWN:
for(int i=0; i<=15; i=Min(i+8, 15))
{
if(!CanDashPixel(x+i, y+16, crystalSlot, collData))
ret = false;
if(i==15)
break;
}
// Break crystals from the side when able to steer dash
if(DASH_CAN_SIDE_SMASH)
{
// We're using these for their secondary effects, not return value
if(Link->InputLeft)
CanDashPixel(x-1, y+15, crystalSlot, collData);
if(Link->InputRight)
CanDashPixel(x+16, y+15, crystalSlot, collData);
}
break;
case DIR_LEFT:
for(int i=ht; i<=15; i=Min(i+8, 15))
{
if(!CanDashPixel(x-1, y+i, crystalSlot, collData))
ret = false;
if(i==15)
break;
}
// Break crystals from the side when able to steer dash
if(DASH_CAN_SIDE_SMASH)
{
// We're using these for their secondary effects, not return value
if(Link->InputUp)
CanDashPixel(x, y+ht-1, crystalSlot, collData);
if(Link->InputDown)
CanDashPixel(x, y+16, crystalSlot, collData);
}
break;
case DIR_RIGHT:
for(int i=ht; i<=15; i=Min(i+8, 15))
{
if(!CanDashPixel(x+16, y+i, crystalSlot, collData))
ret = false;
if(i==15)
break;
}
// Break crystals from the side when able to steer dash
if(DASH_CAN_SIDE_SMASH)
{
// We're using these for their secondary effects, not return value
if(Link->InputUp)
CanDashPixel(x+15, y+ht-1, crystalSlot, collData);
if(Link->InputDown)
CanDashPixel(x+15, y+16, crystalSlot, collData);
}
break;
}
return ret;
}
bool CanDashPixel(int x, int y, int crystalSlot, untyped collData)
{
int pos = ComboAt(x, y);
for(int i=0; i<2; ++i)
{
mapdata md = Game->LoadTempScreen(i);
switch(md->ComboT[pos])
{
case CT_SLASH:
case CT_SLASHITEM:
case CT_BUSH:
case CT_FLOWERS:
case CT_SLASHNEXT:
case CT_SLASHNEXTITEM:
case CT_BUSHNEXT:
case CT_SLASHC:
case CT_SLASHITEMC:
case CT_BUSHC:
case CT_FLOWERSC:
case CT_TALLGRASSC:
case CT_SLASHNEXTC:
case CT_SLASHNEXTITEMC:
case CT_BUSHNEXTC:
return true;
break;
}
combodata cd = Game->LoadComboData(md->ComboD[pos]);
if(cd->Script==crystalSlot&&collData[DC_CANBEAKCRYSTAL])
{
BreakCrystal(md, pos, cd);
collData[DC_COLLIDEDCRYSTAL] = true;
// Don't let Link through if "bonk" is checked
if(cd->Flags[0])
collData[DC_FORCEBOUNCE] = true;
return true;
}
}
return !Screen->isSolid(x, y);
}
void BreakCrystal(mapdata md, int pos, combodata cd)
{
int advanceCount = Max(1, cd->Attrishorts[0]);
int blockwidth = Max(1, cd->Attribytes[4]);
int blockheight = Max(1, cd->Attribytes[5]);
bool largeBlock;
// If this is a large combo block, move pos to the top-left corner
if(blockwidth>0||blockheight>0)
{
pos -= cd->Attribytes[6];
pos -= 16*cd->Attribytes[7];
largeBlock = true;
}
if(cd->Attribytes[0])
{
lweapon smash = CreateLWeaponAt(LW_SPARKLE, ComboX(pos), ComboY(pos));
smash->UseSprite(cd->Attribytes[0]);
smash->CollDetection = false;
smash->Extend = EXT_NORMAL;
smash->TileWidth = Max(1, cd->Attribytes[1]);
smash->TileHeight = Max(1, cd->Attribytes[2]);
// Center the sprite based on its size
smash->X -= (8*smash->TileWidth)-8;
smash->Y -= (8*smash->TileHeight)-8;
// If part of a larger tile block, center the sprite on the block's center
smash->X += (8*blockwidth)-8;
smash->Y += (8*blockheight)-8;
}
if(cd->Attribytes[3])
{
Game->PlaySound(cd->Attribytes[3]);
}
if(largeBlock)
{
// For large blocks, multiple combos need to be advanced at once when hit
// The top left corner is used as the point of reference for this and
// all other combos point to it
for(int x=0; x<blockwidth; ++x)
{
for(int y=0; y<blockheight; ++y)
{
md->ComboD[pos+x+y*16] += advanceCount;
}
}
}
else
md->ComboD[pos] += advanceCount;
}
void NoActionPegasus()
{
// This is a special version of NoAction() that doesn't stop the feather from being used
if(DASH_ALLOW_FEATHER)
{
if(Link->ItemA>=0)
{
itemdata id = Game->LoadItemData(Link->ItemA);
if(id->Type!=IC_ROCS)
{
Link->InputA = false; Link->PressA = false;
}
}
if(Link->ItemB>=0)
{
itemdata id = Game->LoadItemData(Link->ItemB);
if(id->Type!=IC_ROCS)
{
Link->InputB = false; Link->PressB = false;
}
}
if(Link->ItemX>=0)
{
itemdata id = Game->LoadItemData(Link->ItemX);
if(id->Type!=IC_ROCS)
{
Link->InputEx1 = false; Link->PressEx1 = false;
}
}
if(Link->ItemY>=0)
{
itemdata id = Game->LoadItemData(Link->ItemY);
if(id->Type!=IC_ROCS)
{
Link->InputEx2 = false; Link->PressEx2 = false;
}
}
}
else
{
Link->InputA = false; Link->PressA = false;
Link->InputB = false; Link->PressB = false;
Link->InputEx1 = false; Link->PressEx1 = false;
Link->InputEx2 = false; Link->PressEx2 = false;
}
Link->InputL = false; Link->PressL = false;
Link->InputR = false; Link->PressR = false;
Link->InputUp = false; Link->PressUp = false;
Link->InputDown = false; Link->PressDown = false;
Link->InputLeft = false; Link->PressLeft = false;
Link->InputRight = false; Link->PressRight = false;
}
void TryImitateFeather()
{
// Like the above, but this one does a jump if the feather is used
if(DASH_ALLOW_FEATHER)
{
int usingFeather = -1;
if(Link->PressA&&Link->ItemA>=0)
{
itemdata id = Game->LoadItemData(Link->ItemA);
if(id->Type==IC_ROCS)
usingFeather = id->ID;
}
if(Link->PressB&&Link->ItemB>=0)
{
itemdata id = Game->LoadItemData(Link->ItemB);
if(id->Type==IC_ROCS)
usingFeather = id->ID;
}
if(Link->PressEx1&&Link->ItemX>=0)
{
itemdata id = Game->LoadItemData(Link->ItemX);
if(id->Type==IC_ROCS)
usingFeather = id->ID;
}
if(Link->PressEx2&&Link->ItemY>=0)
{
itemdata id = Game->LoadItemData(Link->ItemY);
if(id->Type==IC_ROCS)
usingFeather = id->ID;
}
if(usingFeather>-1)
{
if(Link->Standing)
{
itemdata id = Game->LoadItemData(usingFeather);
Game->PlaySound(id->UseSound);
Link->Jump = (id->Power+2)*0.8;
Link->Action = LA_NONE;
}
}
}
}
}
@Flag0("Bonk"),
@FlagHelp0("Link bonks off the combo after colliding"),
@Attribyte0("Shatter Sprite"),
@AttribyteHelp0("The sprite to draw when the combo breaks. 0 for none"),
@Attribyte1("Sprite W"),
@AttribyteHelp1("The tile width of the sprite"),
@Attribyte2("Sprite H"),
@AttribyteHelp2("The tile height of the sprite"),
@Attribyte3("Shatter SFX"),
@AttribyteHelp3("The sound when the combo breaks"),
@Attribyte4("Block W"),
@AttribyteHelp4("If this is part of a larger breakable object, this is the tile width of the object"),
@Attribyte5("Block H"),
@AttribyteHelp5("If this is part of a larger breakable object, this is the tile height of the object"),
@Attribyte6("Core X Off"),
@AttribyteHelp6("If this is part of a larger breakable object, this is the X position relative to the upper-left corner"),
@Attribyte7("Core Y Off"),
@AttribyteHelp7("If this is part of a larger breakable object, this is the Y position relative to the upper-left corner"),
@Attrishort0("Skip Amount"),
@AttrishortHelp0("The number of combos to advance by in the list when broken")
combodata script PegasusCrystal
{
void run()
{
while(true)
{
// This script is just used to flag combos that can be broken by the pegasus boots
Waitframe();
}
}
}
}