itemdata script ReflectSword
{
const int REFLECTSWORD_ACTIVEFRAMES = 10; // How many frames reflection is active for
const int SFX_REFLECTSWORD = 6; // A sound when the sword hits a reflect
const int SPR_REFLECTSWORD = 105; // A sprite animation drawn between the sword and the reflected weapon
const int HITSTOP_REFLECTSWORD = 4; // Hitstop frames after reflecting (uses a generic script)
const int DAMAGE_MULT_REFLECTSWORD = 2; // Damage multiplier when reflecting
const int SPEED_MULT_REFLECTSWORD = 1.5; // Speed multiplier when reflecting
const int SPEED_FLOOR_REFLECTSWORD = 250; // Minimum speed of reflected projectiles
void run()
{
Waitframe();
lweapon sword = LoadLWeaponOf(LW_SWORD);
int frames;
int hitstop = HITSTOP_REFLECTSWORD;
while(sword->isValid())
{
if(frames<REFLECTSWORD_ACTIVEFRAMES+1)
++frames;
if(frames<=REFLECTSWORD_ACTIVEFRAMES)
{
for(int i=Screen->NumEWeapons(); i>0; --i)
{
eweapon e = Screen->LoadEWeapon(i);
if(Collision(sword, e))
{
if(CanReflect(e))
{
Reflect(sword, e, Angle(Link->X, Link->Y, e->X, e->Y), hitstop);
hitstop = 0;
}
}
}
}
Waitframe();
}
}
lweapon Reflect(lweapon sword, eweapon e, int angle, int hitstop)
{
lweapon l = ReflectSwordEWeaponToLWeapon(e);
if(l->isValid())
{
if(SFX_REFLECTSWORD)
Game->PlaySound(SFX_REFLECTSWORD);
if(SPR_REFLECTSWORD)
{
int x = (CenterX(l)+CenterX(sword))/2-8;
int y = (CenterY(l)+CenterY(sword))/2-8;
lweapon sparkle = CreateLWeaponAt(LW_SPARKLE, x, y);
sparkle->UseSprite(SPR_REFLECTSWORD);
}
if(hitstop)
{
genericdata gd = Game->LoadGenericData(Game->GetGenericScript("HitStop"));
gd->InitD[0] = HITSTOP_REFLECTSWORD;
gd->RunFrozen();
}
e->Remove();
ReflectSprite(l);
if(l->Angular)
{
l->DegAngle = angle;
}
else
{
l->Dir = OppositeDir(l->Dir);
}
l->Damage += DAMAGE_MULT_REFLECTSWORD;
l->Step *= SPEED_MULT_REFLECTSWORD;
if(SPEED_FLOOR_REFLECTSWORD)
l->Step = Max(l->Step, SPEED_FLOOR_REFLECTSWORD);
}
}
bool CanReflect(eweapon e)
{
if(!e->CollDetection)
return false;
if(e->Unblockable==UNBLOCK_ALL)
return false;
if(!e->Angular)
{
return FacingOpposite(Link->Dir, e->Dir);
}
return true;
}
lweapon ReflectSwordEWeaponToLWeapon(eweapon a)
{
int type = EWeaponToLWeaponID(a->ID);
if ( type == -1 ) //invalid
{
type = LW_SCRIPT10;
}
if(type==LW_BRANG)
type = LW_SCRIPT10;
//if ( type == -1 ) return type;
lweapon b = Screen->CreateLWeapon(type);
b->X = a->X;
b->Y = a->Y;
b->Z = a->Z;
b->Jump = a->Jump;
b->Extend = a->Extend;
b->TileWidth = a->TileWidth;
b->TileHeight = a->TileHeight;
b->HitWidth = a->HitWidth;
b->HitHeight = a->HitHeight;
b->HitZHeight = a->HitZHeight;
b->HitXOffset = a->HitXOffset;
b->HitYOffset = a->HitYOffset;
b->DrawXOffset = a->DrawXOffset;
b->DrawYOffset = a->DrawYOffset;
b->DrawZOffset = a->DrawZOffset;
b->Tile = a->Tile;
b->CSet = a->CSet;
b->DrawStyle = a->DrawStyle;
b->Dir = a->Dir;
b->OriginalTile = a->OriginalTile;
b->OriginalCSet = a->OriginalCSet;
b->FlashCSet = a->FlashCSet;
b->NumFrames = a->NumFrames;
b->Frame = a->Frame;
b->ASpeed = a->ASpeed;
b->Damage = a->Damage;
b->Step = a->Step;
b->Angle = a->Angle;
b->Angular = a->Angular;
b->CollDetection = a->CollDetection;
b->DeadState = a->DeadState;
b->Flash = a->Flash;
b->Flip = a->Flip;
for (int i = 0; i < 32; ++i)
b->Misc[i] = a->Misc[i];
b->ScriptTile = a->ScriptTile;
b->ScriptFlip = a->ScriptFlip;
b->Animation = a->Animation;
//not assigning weapon script stuff here, because that would never work.
//not assigning ->Parent, because that would be invalid.
//not assigning Weapon or ->Defence because that would be invalid.
//Not assigning ->Level, because that would be invalid.
return b;
}
bool FacingOpposite(int linkDir, int weapDir)
{
switch(weapDir)
{
case DIR_UP:
return linkDir==DIR_DOWN;
case DIR_DOWN:
return linkDir==DIR_UP;
case DIR_LEFT:
return linkDir==DIR_RIGHT;
case DIR_RIGHT:
return linkDir==DIR_LEFT;
case DIR_LEFTUP:
return linkDir==DIR_RIGHT||linkDir==DIR_DOWN;
case DIR_RIGHTUP:
return linkDir==DIR_LEFT||linkDir==DIR_DOWN;
case DIR_LEFTDOWN:
return linkDir==DIR_RIGHT||linkDir==DIR_UP;
case DIR_RIGHTDOWN:
return linkDir==DIR_LEFT||linkDir==DIR_UP;
}
}
void ReflectSprite(lweapon l)
{
switch(l->Type)
{
case LW_BEAM:
case LW_ARROW:
case LW_MAGIC:
{
switch(l->Flip)
{
case FLIP_VERTICAL:
case FLIP_HORIZONTAL:
l->Flip = FLIP_NONE;
break;
case FLIP_NONE:
l->Flip = l->Dir==DIR_UP?FLIP_VERTICAL:FLIP_HORIZONTAL;
break;
}
break;
}
}
}
}
generic script HitStop
{
void run(int frames)
{
Waitframes(frames);
}
}
lweapon script Chakram
{
void run(int damage)
{
this->CollDetection = false;
while(true)
{
lweapon l = CreateLWeaponAt(LW_SCRIPT10, this->X, this->Y);
l->Dir = this->Dir;
l->Damage = damage;
l->Timeout = 2;
l->DrawYOffset = -1000;
Waitframe();
}
}
}
itemdata script DashAbility
{
const int SFX_DASH = 1; // Sound that plays on loop when dashing
const int FREQ_DASHSFX = 8; // How often the sound plays
const int SPR_DASHDUST = 106; // Sprite for dust kicked up while dashing
const int FREQ_DASHDUST = 8; // How often dust particles spawn
const int MPCOST_DASH = 4; // MP cost when dashing
const int FREQ_DASHMP = 8; // How frequently it ticks down
const int STEP_DASH = 1; // Extra pixels per frame added onto Link's step while dashing
void run()
{
int soundtimer;
int spritetimer;
int mptimer = FREQ_DASHMP;
bool ranOut = true;
if(Link->MP>0)
{
ranOut = false;
Link->MP = Max(Link->MP-MPCOST_DASH, 0);
}
while(((UsingItem(this->ID)&&CanDash())||Link->Action==LA_SCROLLING)&&!ranOut)
{
if(Link->Action!=LA_SCROLLING&&CanDash())
{
int vX = (Link->InputLeft?-1:0) + (Link->InputRight?1:0);
int vY = (Link->InputUp?-1:0) + (Link->InputDown?1:0);
int step = STEP_DASH;
if(vX!=0&&vY!=0)
step *= 0.7071;
Link->MoveXY(vX, vY);
unless(vX==0&&vY==0)
{
if(mptimer)
--mptimer;
else
{
Link->MP = Max(Link->MP-MPCOST_DASH, 0);
mptimer = FREQ_DASHMP;
if(Link->MP==0)
ranOut = true;
}
}
if(soundtimer)
--soundtimer;
else
{
if(SFX_DASH)
Game->PlaySound(SFX_DASH);
soundtimer = FREQ_DASHSFX;
}
if(Link->Z==0)
{
if(spritetimer)
--spritetimer;
else
{
if(SPR_DASHDUST)
{
lweapon dust = CreateLWeaponAt(LW_SPARKLE, Link->X+Rand(-8, 8), Link->Y+8+Rand(-4, 4));
dust->UseSprite(SPR_DASHDUST);
}
spritetimer = FREQ_DASHDUST;
}
}
}
Waitframe();
}
}
bool UsingItem(int itemid)
{
if(Link->ItemA==itemid&&Link->InputA) return true;
if(Link->ItemB==itemid&&Link->InputB) return true;
if(Link->ItemX==itemid&&Link->InputEx1) return true;
if(Link->ItemY==itemid&&Link->InputEx2) return true;
}
bool CanDash()
{
unless(Link->Action==LA_NONE||Link->Action==LA_WALKING||Link->Action==LA_GOTHURTLAND)
return false;
return true;
}
}
Two active item scripts, one generic script, and one lweapon script.
Reflect sword and chakram scripts go over a sword and boomerang item respectively.
Reflect sword and dash have some constants you'll want to set at the top of their scripts. Both use sprite animations. The reflect sword sprite is a 16x16 pixel impact animation that's drawn when the sword hits something. Dash sprite is intended as an 8x8 pixel puff in the center of the tile.