const int LW_MOOSHITEM_MELEE = 31; //Weapon type for melee weapons (Script 1 by default)
const int TIL_INVISIBLE = 65260; //Invisible tile
const int SFX_MOOSHITEM_MELEE = 30; //Sound of a MooshItem melee swing
const int SFX_MOOSHITEM_BLOCK = 6; //Sound of a MooshItem blocking a weapon
const int SFX_MOOSHITEM_CHARGE = 35; //Sound of a MooshItem charging up
const int SFX_MOOSHITEM_WALLBOUNCE = 6; //Sound of a MooshItem projectile bouncing off a wall
const int SFX_MOOSHITEM_HP_COST = 19; //Sound that plays when a MooshItem takes Link's HP
const int MOOSHITEM_MP_DRAIN_FREQ = 10; //How many frames it takes for MP draining items to take a charge
const float MOOSHITEM_MP_DRAIN_RATIO = 0.5; //How much subsequent ticks of MP costs drain relative to the initial one
//Type 0: Normal Shot (V)
//Type 1: Normal Shot (Line)
//Type 2: Sine Wave
//Type 3: Homing
//Type 4: Charge Shot
//Type 5: Circle Shield
//Type 6: Breath
//Type 7: Spread Shield
//Type 8: Bouncing Shot
//Moosh Item Flag Constants (Don't change these)
const int MIF_4WAY = 0000000001b; //1 - Sprites have four directions
const int MIF_8WAY = 0000000010b; //2 - Sprites have eight directions
const int MIF_360WAY = 0000000100b; //4 - Sprite rotates 360 degrees and faces left by default
const int MIF_2X2 = 0000001000b; //8 - Sprite is 2x2
const int MIF_PIERCE = 0000010000b; //16 - Weapon pierces enemies
const int MIF_ANIMSTAB = 0000100000b; //32 - Has a stab animation (Sprite+1)
const int MIF_ANIMSLASH = 0001000000b; //64 - Has a slash animation (Sprite+1)
const int MIF_8WAYAIM = 0010000000b; //128 - Can be aimed in 8 directions
const int MIF_EXPLODEDEATH = 0100000000b; //256 - Explodes when the weapon dies
const int MIF_DRAINMP = 1000000000b; //512 - Drains MP over time (for circle shield/breath)
//This is the main item script which calls the other two FFCs when the item is used
item script MooshItem{
void run(int dummy, int ID_mpCost, int type_step, int wType_maxShot, int arg1_arg2, int arg3_arg4, int sprite_sfx, int flags){
int handler[] = "MooshItem_Handler";
int anim[] = "MooshItem_Animations";
int type = MooshItem_LeftArg(type_step);
int maxShot = MooshItem_RightArg(wType_maxShot);
//Attacks that drain the MP bar over time can't be used twice on a screen
if(flags&MIF_DRAINMP)
maxShot = 0;
int args[8];
int ID = MooshItem_LeftArg(ID_mpCost);
int mpCost = MooshItem_RightArg(ID_mpCost);
//Prevent the script running by holding the item button
if(!MooshItem_PressButtonItem(ID))
Quit();
//Prevent the script running if not enough MP (charge shot is an exception)
if(!MooshItem_CanTakeResource(ID, mpCost)&&type!=4)
Quit();
if(MooshItem_NumType(type)<=maxShot){
if(flags&MIF_ANIMSTAB||flags&MIF_ANIMSLASH){
//Stab + Slash = Z3 slash
if(flags&MIF_ANIMSTAB&&flags&MIF_ANIMSLASH)
args[0] = 2;
else if(flags&MIF_ANIMSLASH)
args[0] = 1;
else
args[0] = 0;
args[1] = ID_mpCost;
args[2] = type_step;
args[3] = wType_maxShot;
args[4] = arg1_arg2;
args[5] = arg3_arg4;
args[6] = sprite_sfx;
args[7] = flags;
RunFFCScript(Game->GetFFCScript(anim), args);
}
else{
Link->Action = LA_ATTACKING;
args[0] = ID_mpCost;
args[1] = type_step;
args[2] = wType_maxShot;
args[3] = arg1_arg2;
args[4] = arg3_arg4;
args[5] = sprite_sfx;
args[6] = flags;
RunFFCScript(Game->GetFFCScript(handler), args);
}
}
}
bool MooshItem_CanTakeResource(int ID, int mpCost){
if(mpCost==0)
return true;
itemdata itemdat = Game->LoadItemData(ID);
if(itemdat->Counter>-1){ //If a counter is set
if(itemdat->Counter==CR_LIFE){ //If the counter is health, don't allow it to hit 0
if(Game->Counter[itemdat->Counter]<=mpCost)
return false;
}
else if(Game->Counter[itemdat->Counter]<mpCost)
return false;
}
else{ //Otherwise it's a regular magic cost
//Only magic costs are affected by 1/2 magic
mpCost = mpCost*Game->Generic[GEN_MAGICDRAINRATE];
if(Link->MP<mpCost)
return false;
}
return true;
}
}
//Returns the component of an argument left of the decimal point
int MooshItem_LeftArg(float i){
return Floor(i);
}
//Returns the component of an argument right of the decimal point
int MooshItem_RightArg(float i){
int left = MooshItem_LeftArg(i);
return (i-left)*10000;
}
//Count the number of active weapon scripts of a certain type
int MooshItem_NumType(int checktype){
int handler[] = "MooshItem_Handler";
int slot = Game->GetFFCScript(handler);
int count;
for(int i=1; i<=32; i++){
ffc f = Screen->LoadFFC(i);
if(f->Script==slot){
int type = MooshItem_LeftArg(f->InitD[1]);
if(type==checktype)
count++;
}
}
return count;
}
//Returns true if Link is pressing an item's button that frame
bool MooshItem_PressButtonItem(int id){
return (GetEquipmentA()==id&&Link->PressA)||(GetEquipmentB()==id&&Link->PressB);
}
//Returns true if Link is holding down an item's button
bool MooshItem_InputButtonItem(int id){
return (GetEquipmentA()==id&&Link->InputA)||(GetEquipmentB()==id&&Link->InputB);
}
//Get the difference between two angles in degrees
int MooshItem_AngDiff(int angle1, int angle2){
// Get the difference between the two angles
float dif = WrapDegrees(angle2) - WrapDegrees(angle1);
// Compensate for the difference being outside of normal bounds
if(dif >= 180)
dif -= 360;
else if(dif <= -1 * 180)
dif += 360;
return dif;
}
//This FFC script contains most of the item behaviors
ffc script MooshItem_Handler{
void run(int ID_mpCost, int type_step, int wType_maxShot, int arg1_arg2, int arg3_arg4, int sprite_sfx, int flags){
int i; int j; int k; int m;
int x; int y;
//First we split a bunch of the arguments into their two halves
int ID = MooshItem_LeftArg(ID_mpCost);
int mpCost = MooshItem_RightArg(ID_mpCost);
int type = MooshItem_LeftArg(type_step);
int step = MooshItem_RightArg(type_step);
int wType = MooshItem_LeftArg(wType_maxShot);
if(wType==0)
wType = LW_SCRIPT1;
int maxShot = MooshItem_RightArg(wType_maxShot);
int arg1 = MooshItem_LeftArg(arg1_arg2);
int arg2 = MooshItem_RightArg(arg1_arg2);
int arg3 = MooshItem_LeftArg(arg3_arg4);
int arg4 = MooshItem_RightArg(arg3_arg4);
int sprite = MooshItem_LeftArg(sprite_sfx);
int sfx = MooshItem_RightArg(sprite_sfx);
itemdata itemdat = Game->LoadItemData(ID);
if(type!=4){ //Charge shot calculates MP differently
//Take MP / Counter if applicable
if(!MooshItem_CanTakeResource(ID, mpCost))
Quit();
else
MooshItem_TakeResource(ID, mpCost);
}
//Get Link's direction
int LinkAng;
int angle;
if(Link->Dir==DIR_UP)
LinkAng = -90;
else if(Link->Dir==DIR_DOWN)
LinkAng = 90;
else if(Link->Dir==DIR_LEFT)
LinkAng = 180;
//Update the direction if the weapon has 8-way aiming
if(flags&MIF_8WAYAIM){
LinkAng = MooshItem_8WayAimAngle(LinkAng);
}
//Look at all this garbage!
lweapon lgroup[256];
lweapon l;
int lTimer[256];
int lTile[256];
int lAng[256];
int lType[256];
int lMisc1[256];
int lMisc2[256];
int lDecay[256];
int lFlags[256];
int lACounter[256];
int lNumFrames[256];
int lASpeed[256];
int lBombSP[256];
int lLastX[256];
int lLastY[256];
int vars[17] = {0, lTimer, lTile, lAng, lType, lMisc1, lMisc2, lDecay, lFlags, lACounter, lNumFrames, lASpeed, 0, lLastX, lLastY, itemdat->Power*2, 0};
//Bomb blasts get converted into a weapon with no collsion that regularly drops them
if(wType==LW_BOMBBLAST){
wType = LW_MOOSHITEM_MELEE;
vars[12] = 1;
}
else if(wType==LW_SBOMBBLAST){
wType = LW_MOOSHITEM_MELEE;
vars[12] = 2;
}
if(type==0){ //Normal (V)
//Arg 1 = Decay
//Arg 2 = NumShots
//Arg 3 = Spread
//Arg 4 = Step 2
if(arg2==0)
arg2 = 1;
if(arg3==0&&arg2>1)
arg3 = 60;
if(arg4==0)
arg4 = step;
x = Link->X+VectorX(12, LinkAng);
y = Link->Y+VectorY(12, LinkAng);
for(i=0; i<arg2; i++){
angle = LinkAng-arg3/2+(i/(arg2-1))*arg3;
if(arg2==1)
angle = LinkAng;
l = MooshItem_FireLWeapon(wType, x, y, angle, step-(step-arg4)*Abs((i/(arg2-1)*2)-1), itemdat->Power*2, sprite, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 0, angle, flags);
lDecay[j] = arg1;
}
while(vars[0]>0){
MooshItem_UpdateLWeapons(vars, lgroup);
Waitframe();
}
}
else if(type==1){ //Normal (Line)
//Arg 1 = Decay
//Arg 2 = NumShots
//Arg 3 = Spread
//Arg 4 = Step 2
if(arg2==0)
arg2 = 1;
if(arg4==0)
arg4 = step;
x = Link->X+VectorX(12, LinkAng);
y = Link->Y+VectorY(12, LinkAng);
for(i=0; i<arg2; i++){
angle = LinkAng+Rand(-arg3/2, arg3/2);
if(arg2==1)
angle = LinkAng;
l = MooshItem_FireLWeapon(wType, x, y, angle, step-(step-arg4)*(i/(arg2-1)), itemdat->Power*2, sprite, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 0, angle, flags);
lDecay[j] = arg1;
}
while(vars[0]>0){
MooshItem_UpdateLWeapons(vars, lgroup);
Waitframe();
}
}
else if(type==2){ //Sine Wave
//Arg 1 = Amplitude
//Arg 2 = Frequency
//Arg 3 = NumShots
//Arg 4 = Repeat
if(arg1==0)
arg1 = 16;
if(arg2==0)
arg2 = 32;
if(arg3==0)
arg3 = 1;
do{
for(i=0; i<arg3; i++){
LinkAng = 0;
if(Link->Dir==DIR_UP)
LinkAng = -90;
else if(Link->Dir==DIR_DOWN)
LinkAng = 90;
else if(Link->Dir==DIR_LEFT)
LinkAng = 180;
if(flags&MIF_8WAYAIM){
LinkAng = MooshItem_8WayAimAngle(LinkAng);
}
x = Link->X+VectorX(12, LinkAng);
y = Link->Y+VectorY(12, LinkAng);
l = MooshItem_FireLWeapon(wType, x, y, LinkAng, step, itemdat->Power*2, sprite, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 1, angle, flags);
lMisc1[j] = (Cond(i%2==0, 1, -1)*(arg1+Floor(i/2)*16))/2;
lMisc2[j] = arg2;
lTimer[j] += (45/arg2*(step/100))*Floor(i/2);
}
if(arg4>0){
k = 16/(step/100);
for(j=0; j<k; j++){
MooshItem_UpdateLWeapons(vars, lgroup);
NoAction();
Waitframe();
}
}
arg4--;
}while(arg4>0)
while(vars[0]>0){
MooshItem_UpdateLWeapons(vars, lgroup);
Waitframe();
}
}
else if(type==3){ //Homing
//Arg 1 = Decay
//Arg 2 = TurnSpeed
//Arg 3 = NumShots
//Arg 4 = Spread
if(arg2==0)
arg2 = 100;
if(arg4==0)
arg4 = 60;
x = Link->X+VectorX(12, LinkAng);
y = Link->Y+VectorY(12, LinkAng);
for(i=0; i<arg3; i++){
angle = LinkAng-arg4/2+(i/(arg3-1))*arg4;
if(arg3==1)
angle = LinkAng;
l = MooshItem_FireLWeapon(wType, x, y, angle, step, itemdat->Power*2, sprite, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 2, angle, flags);
lDecay[j] = arg1;
lMisc1[j] = arg2;
}
while(vars[0]>0){
MooshItem_UpdateLWeapons(vars, lgroup);
Waitframe();
}
}
else if(type==4){ //Charge shot
//Arg 1 = Charge time
//Arg 2 = Charge Type: 0 - 2x2 weapon, 1 - spread shot, 2 - homing shots
//Arg 3 = Sprite
//Arg 4 = Damage
if(arg1==0)
arg1 = 90;
int chargeCounter = 0;
while(MooshItem_InputButtonItem(ID)){
i = (i+1)%360;
if(i%3==0){
Link->InputUp = false;
Link->InputDown = false;
Link->InputLeft = false;
Link->InputRight = false;
}
if(!MooshItem_CanTakeResource(ID, mpCost))
chargeCounter = 0;
if(chargeCounter<arg1){
chargeCounter++;
if(chargeCounter==arg1)
Game->PlaySound(SFX_MOOSHITEM_CHARGE);
}
else{
DrawTile(3, Link->X+Link->DrawXOffset, Link->Y+Link->DrawYOffset-Link->Z, Link->Tile, 1, 1, 9-((Floor(i/2)%32)>>1), -1, -1, 0, 0, 0, Link->Flip, true, 128);
}
Waitframe();
}
LinkAng = 0;
if(Link->Dir==DIR_UP)
LinkAng = -90;
else if(Link->Dir==DIR_DOWN)
LinkAng = 90;
else if(Link->Dir==DIR_LEFT)
LinkAng = 180;
if(flags&MIF_8WAYAIM){
LinkAng = MooshItem_8WayAimAngle(LinkAng);
}
x = Link->X+VectorX(12, LinkAng);
y = Link->Y+VectorY(12, LinkAng);
if(chargeCounter<arg1){
l = MooshItem_FireLWeapon(wType, x, y, LinkAng, step, itemdat->Power*2, sprite, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 0, angle, flags);
}
else{
if(!MooshItem_CanTakeResource(ID, mpCost))
Quit();
else
MooshItem_TakeResource(ID, mpCost);
if(arg2==0){ //Big shot
l = MooshItem_FireLWeapon(wType, x, y, LinkAng, step, arg4*2, arg3, sfx, flags|MIF_2X2);
j = MooshItem_AddLWeapon(vars, lgroup, l, 0, angle, flags|MIF_2X2);
}
else if(arg2==1){ //Spread shot
if(arg3==0)
arg3 = 3;
if(arg4==0)
arg4 = 45;
for(i=0; i<12; i++){
angle = LinkAng-arg4/2+(i/(12-1))*arg4;
l = MooshItem_FireLWeapon(wType, x, y, angle, step, arg4*2, arg3, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 0, angle, flags);
}
}
else if(arg2==2){ //Homing
for(i=0; i<12; i++){
angle = LinkAng+(360/12)*i;
l = MooshItem_FireLWeapon(wType, x, y, angle, step, arg4*2, arg3, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 2, angle, flags);
lMisc1[j] = 1000;
}
}
}
while(vars[0]>0){
MooshItem_UpdateLWeapons(vars, lgroup);
Waitframe();
}
}
else if(type==5){ //Circular Shield
//Arg 1 = NumShots
//Arg 2 = Distance
//Arg 3 = AngOffset
//Arg 4 = BlockFlags
if(arg1==0)
arg1 = 6;
if(arg2==0)
arg2 = 24;
if(MooshItem_NumType(type)>1){
arg2 += Max(8, arg2-16)*MooshItem_NumType(type);
if(MooshItem_NumType(type)%2==0){
step = -step;
arg3 = -arg3;
}
}
x = Link->X;
y = Link->Y;
vars[16] = 1;
for(i=0; i<arg1; i++){
l = MooshItem_FireLWeapon(wType, x, y, 0, 0, itemdat->Power*2, sprite, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 3, 0, flags);
lMisc1[j] = arg4;
}
for(j=0; j<16; j++){
LinkAng = WrapDegrees(LinkAng+step/100);
for(i=0; i<arg1; i++){
x = Link->X;
y = Link->Y;
if(lgroup[i]->isValid()){
lgroup[i]->HitXOffset = VectorX(arg2*(j/16), LinkAng+360/arg1*i+arg3);
lgroup[i]->HitYOffset = VectorY(arg2*(j/16), LinkAng+360/arg1*i);
lgroup[i]->DrawXOffset = lgroup[i]->HitXOffset;
lgroup[i]->DrawYOffset = lgroup[i]->HitYOffset;
lgroup[i]->X = x;
lgroup[i]->Y = y;
lgroup[i]->Z = Link->Z;
if(arg3<=0)
lAng[i] = LinkAng+360/arg1*i+90;
else
lAng[i] = LinkAng+360/arg1*i-90;
}
}
MooshItem_UpdateLWeapons(vars, lgroup);
Waitframe();
}
k = 0;
while(vars[0]>0){
if(flags&MIF_DRAINMP){
k++;
if(k>=MOOSHITEM_MP_DRAIN_FREQ){
k = 0;
if(MooshItem_CanTakeResource(ID, Max(1, mpCost*MOOSHITEM_MP_DRAIN_RATIO)))
MooshItem_TakeResource(ID, Max(1, mpCost*MOOSHITEM_MP_DRAIN_RATIO));
else
break;
}
if(MooshItem_PressButtonItem(ID)){
NoAction();
break;
}
}
LinkAng = WrapDegrees(LinkAng+step/100);
int activeWeapons = 0;
for(i=0; i<arg1; i++){
x = Link->X;
y = Link->Y;
if(lgroup[i]->isValid()){
lgroup[i]->HitXOffset = VectorX(arg2, LinkAng+360/arg1*i+arg3);
lgroup[i]->HitYOffset = VectorY(arg2, LinkAng+360/arg1*i);
lgroup[i]->DrawXOffset = lgroup[i]->HitXOffset;
lgroup[i]->DrawYOffset = lgroup[i]->HitYOffset;
lgroup[i]->X = x;
lgroup[i]->Y = y;
lgroup[i]->Z = Link->Z;
if(arg3<=0)
lAng[i] = LinkAng+360/arg1*i+90;
else
lAng[i] = LinkAng+360/arg1*i-90;
//Count how many of the weapons are still alive.
//This is to fix a bug that was caused by another bugfix.
activeWeapons++;
}
}
if(activeWeapons==0)
break;
MooshItem_UpdateLWeapons(vars, lgroup);
Waitframe();
}
for(j=0; j<16; j++){
LinkAng = WrapDegrees(LinkAng+step/100);
for(i=0; i<arg1; i++){
x = Link->X;
y = Link->Y;
if(lgroup[i]->isValid()){
lgroup[i]->HitXOffset = VectorX(arg2-arg2*(j/16), LinkAng+360/arg1*i+arg3);
lgroup[i]->HitYOffset = VectorY(arg2-arg2*(j/16), LinkAng+360/arg1*i);
lgroup[i]->DrawXOffset = lgroup[i]->HitXOffset;
lgroup[i]->DrawYOffset = lgroup[i]->HitYOffset;
lgroup[i]->X = x;
lgroup[i]->Y = y;
lgroup[i]->Z = Link->Z;
if(arg3<=0)
lAng[i] = LinkAng+360/arg1*i+90;
else
lAng[i] = LinkAng+360/arg1*i-90;
}
}
MooshItem_UpdateLWeapons(vars, lgroup);
Waitframe();
}
for(i=0; i<arg1; i++){
if(lgroup[i]->isValid())
lgroup[i]->DeadState = 0;
}
}
else if(type==6){ //Breath
//Arg 1 = Decay
//Arg 2 = Spread
//Arg 3 = Special Firing
//Arg 4 = Special Arg
k = 0;
m = 0;
do{
if(flags&MIF_DRAINMP){
k++;
if(k>=MOOSHITEM_MP_DRAIN_FREQ){
k = 0;
if(MooshItem_CanTakeResource(ID, Max(1, mpCost*MOOSHITEM_MP_DRAIN_RATIO)))
MooshItem_TakeResource(ID, Max(1, mpCost*MOOSHITEM_MP_DRAIN_RATIO));
else
break;
}
}
m++;
if(m>=4){
m = 0;
LinkAng = 0;
if(Link->Dir==DIR_UP)
LinkAng = -90;
else if(Link->Dir==DIR_DOWN)
LinkAng = 90;
else if(Link->Dir==DIR_LEFT)
LinkAng = 180;
if(flags&MIF_8WAYAIM){
LinkAng = MooshItem_8WayAimAngle(LinkAng);
}
x = Link->X+VectorX(12, LinkAng);
y = Link->Y+VectorY(12, LinkAng);
if(arg3==0){ //Normal shot
if(arg4==0)
arg4 = step;
angle = LinkAng+Rand(-arg2/2, arg2/2);
l = MooshItem_FireLWeapon(wType, x, y, angle, Rand(arg4, step), itemdat->Power*2, sprite, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 0, angle, flags);
lDecay[j] = arg1;
}
else if(arg3==1){ //Sine Wave
if(arg4==0)
arg4 = 16;
angle = LinkAng+Rand(-arg2/2, arg2/2);
l = MooshItem_FireLWeapon(wType, x, y, angle, step, itemdat->Power*2, sprite, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 1, angle, flags);
lDecay[j] = arg1;
lMisc1[j] = arg4*Choose(-1, 1);
lMisc2[j] = arg4*2;
}
else if(arg3==2){ //Homing
if(arg4==0)
arg4 = 1000;
angle = LinkAng+Rand(-arg2/2, arg2/2);
l = MooshItem_FireLWeapon(wType, x, y, angle, step, itemdat->Power*2, sprite, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 2, angle, flags);
lDecay[j] = arg1;
lMisc1[j] = arg4;
}
else if(arg3==3){ //Wall Bounce
if(arg4==0)
arg4 = 3;
angle = LinkAng+Rand(-arg2/2, arg2/2);
l = MooshItem_FireLWeapon(wType, x, y, angle, step, itemdat->Power*2, sprite, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 4, angle, flags);
lDecay[j] = arg1;
lMisc1[j] = arg4;
}
}
MooshItem_UpdateLWeapons(vars, lgroup);
NoAction();
Waitframe();
}while(MooshItem_InputButtonItem(ID))
while(vars[0]>0){
MooshItem_UpdateLWeapons(vars, lgroup);
Waitframe();
}
}
else if(type==7){ //Spread Shield
//Arg 1 = Decay
//Arg 2 = NumShots
//Arg 3 = Spread
//Arg 4 = BlockFlags
if(arg2==0)
arg2 = 1;
if(arg3==0&&arg2>1)
arg3 = 60;
if(arg4==0)
arg4 = 1023;
x = Link->X+VectorX(12, LinkAng);
y = Link->Y+VectorY(12, LinkAng);
for(i=0; i<arg2; i++){
angle = LinkAng-arg3/2+(i/(arg2-1))*arg3;
if(arg2==1)
angle = LinkAng;
l = MooshItem_FireLWeapon(wType, x, y, angle, step, itemdat->Power*2, sprite, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 3, angle, flags);
lDecay[j] = arg1;
lMisc1[j] = arg4;
}
while(vars[0]>0){
MooshItem_UpdateLWeapons(vars, lgroup);
Waitframe();
}
}
else if(type==8){ //Bouncing Gem
//Arg 1 = Decay
//Arg 2 = NumShots
//Arg 3 = Spread
//Arg 4 = Bounces
x = Link->X+VectorX(12, LinkAng);
y = Link->Y+VectorY(12, LinkAng);
for(i=0; i<arg2; i++){
angle = LinkAng+Rand(-arg3/2, arg3/2);
k = 0;
if(arg2>1)
k = step*(Rand(5)*0.125);
l = MooshItem_FireLWeapon(wType, x, y, angle, step+k, itemdat->Power*2, sprite, sfx, flags);
j = MooshItem_AddLWeapon(vars, lgroup, l, 4, angle, flags);
lDecay[j] = arg1;
lMisc1[j] = arg4;
}
while(vars[0]>0){
MooshItem_UpdateLWeapons(vars, lgroup);
Waitframe();
}
}
}
//This function is used to shoot projectile lweapons used by the script
lweapon MooshItem_FireLWeapon(int type, int x, int y, int angle, int step, int damage, int sprite, int sfx, int flags){
lweapon l = CreateLWeaponAt(type, x, y);
l->Z = Link->Z;
l->Angular = true;
l->Angle = DegtoRad(angle);
l->Dir = AngleDir4(WrapDegrees(angle));
l->Step = step;
l->Damage = damage;
l->UseSprite(sprite);
//Adjust the hitbox if 2x2
if(flags&MIF_2X2){
l->Extend = 3;
l->HitWidth = 32;
l->HitHeight = 32;
l->TileWidth = 2;
l->TileHeight = 2;
l->X-=8;
l->Y-=8;
}
Game->PlaySound(sfx);
return l;
}
//This function is the main guts of the script
//It updates all the weapons every frame
void MooshItem_UpdateLWeapons(int vars, lweapon lgroup){
int i; int j; int k; int m; int o;
int x; int y;
int dir;
//vars[0] = Total weapons onscreen
int lTimer = vars[1];
int lTile = vars[2];
int lAng = vars[3];
int lType = vars[4];
int lMisc1 = vars[5];
int lMisc2 = vars[6];
int lDecay = vars[7];
int lFlags = vars[8];
int lACounter = vars[9];
int lNumFrames = vars[10];
int lASpeed = vars[11];
//vars[12] = BombSP
int lLastX = vars[13];
int lLastY = vars[14];
//vars[15] = Weapon Damage
//vars[16] = Prevent weapon clear
//Only cycle through weapons currently in use to save iterations
for(i=0; i<vars[0]; i++){
//If the weapon itself is there, run its behavior
if(lgroup[i]->isValid()){
lTimer[i] = (lTimer[i]+1)%360;
x = lgroup[i]->X;
y = lgroup[i]->Y;
if(lType[i]==1){ //Sine Wave
k = RadtoDeg(lgroup[i]->Angle);
o = lgroup[i]->Step/100;
j = lMisc1[i]*Sin((180/lMisc2[i]*o)*lTimer[i]);
lgroup[i]->HitXOffset = VectorX(j, k+90);
lgroup[i]->HitYOffset = VectorY(j, k+90);
//Weapons with -1000 draw offset are being drawn by the script instead of ZC.
//Thus they don't need draw offsets to be applied.
if(lgroup[i]->DrawYOffset>-1000){
lgroup[i]->DrawXOffset = lgroup[i]->HitXOffset;
lgroup[i]->DrawYOffset = lgroup[i]->HitYOffset;
}
//Adding hit offsets to X and Y affects where the script draws them in special cases
x += lgroup[i]->HitXOffset;
y += lgroup[i]->HitYOffset;
//Here we predict the weapon's position in the next frame so 360 rotating ones are angled right
m = lMisc1[i]*Sin((180/lMisc2[i]*o)*(lTimer[i]+1));
int nextX = lgroup[i]->X+VectorX(Max(1, o), k)+VectorX(m, k+90);
int nextY = lgroup[i]->Y+VectorY(Max(1, o), k)+VectorY(m, k+90);
lAng[i] = Angle(x, y, nextX, nextY);
}
else if(lType[i]==2){ //Homing
k = MooshItem_Homing_TargetAngle(vars, lAng[i], lgroup[i]);
j = MooshItem_AngDiff(lAng[i], k);
//If the distance to the target angle is less than the turn speed, turn it
if(Abs(j)>lMisc1[i]/100){
lAng[i] = WrapDegrees(lAng[i]+Sign(j)*(lMisc1[i]/100));
}
//Otherwise set the angle to the target angle
else
lAng[i] = k;
lgroup[i]->Angle = DegtoRad(lAng[i]);
}
else if(lType[i]==3){ //Shield
//Once again setting offsets to x and y for special cases
x += lgroup[i]->HitXOffset;
y += lgroup[i]->HitYOffset;
if(lMisc1[i]>0){ //If the shield has block flags, run the block weapons function
if(lFlags[i]&MIF_PIERCE)
MooshItem_BlockWeapons(vars, lgroup[i], i, false);
else
MooshItem_BlockWeapons(vars, lgroup[i], i, true);
}
}
else if(lType[i]==4){ //Wall bounce
//Turn angle into vX and vY components
int vX = VectorX(10, lAng[i]);
int vY = VectorY(10, lAng[i]);
o = Max(lgroup[i]->Step/100, 1);
bool bounce = false;
//Flip if they hit a wall
if((vX<0&&!MooshItem_CanWalk(lgroup[i], DIR_LEFT, o)) || (vX>0&&!MooshItem_CanWalk(lgroup[i], DIR_RIGHT, o))){
vX = -vX;
bounce = true;
}
if((vY<0&&!MooshItem_CanWalk(lgroup[i], DIR_UP, o)) || (vY>0&&!MooshItem_CanWalk(lgroup[i], DIR_DOWN, o))){
vY = -vY;
bounce = true;
}
//If a wall was hit, update the angle
if(bounce){
Game->PlaySound(SFX_MOOSHITEM_WALLBOUNCE);
lAng[i] = Angle(0, 0, vX, vY);
lgroup[i]->Angle = DegtoRad(lAng[i]);
lMisc1[i]--;
if(lMisc1[i]<=0){
lgroup[i]->DeadState = 0;
lFlags[i] &= ~MIF_PIERCE; //Pierce messes with killing weapons here so we take it away when a weapon should be killed
}
}
}
int tile = lgroup[i]->Tile;
//OH BOY OH BOY BEAM WEAPONS
//We have a special way of handling these. Because ZC is so intent on drawing the beam shards, I had to clear the weapon's sprite completely.
//Then we redraw what it should be with scripts.
if(lgroup[i]->ID==LW_BEAM){
//Detect any uncleared beams.
if(lgroup[i]->OriginalTile!=TIL_INVISIBLE){
lACounter[i] = 0;
lgroup[i]->OriginalTile = TIL_INVISIBLE;
lgroup[i]->Tile = lgroup[i]->OriginalTile;
lgroup[i]->NumFrames = 0;
lgroup[i]->ASpeed = 0;
}
else{
lACounter[i] = (lACounter[i]+1)%(lNumFrames[i]*lASpeed[i]);
tile = lTile[i]+Clamp(Floor(lACounter[i]/lASpeed[i]), 0, lNumFrames[i]-1)*lgroup[i]->TileWidth;
}
//When the beam shards happen, kill the weapon early so drawing doesn't bug out
if(lgroup[i]->DeadState>0)
lgroup[i]->DeadState = 0;
}
//These weapons have special collision exceptions because they can cause friendly fire damage
else if(lgroup[i]->ID==LW_FIRE||lgroup[i]->ID==LW_REFBEAM||lgroup[i]->ID==LW_REFMAGIC||lgroup[i]->ID==LW_REFROCK){
if(MooshItem_LinkCollisionNoZ(lgroup[i]))
lgroup[i]->CollDetection = false;
else
lgroup[i]->CollDetection = true;
}
int frames = Max(1, lNumFrames[i]);
//These all handle redrawing of beam animations and setting the directions of other weapon types based on angle so they animate correctly.
if(lFlags[i]&MIF_4WAY){ //4 way animations
dir = AngleDir4(WrapDegrees(lAng[i]));
if(lType[i]!=3)
lgroup[i]->Dir = dir;
j = (lgroup[i]->Tile-lTile[i])%frames;
if(lgroup[i]->ID!=LW_BEAM&&lgroup[i]->OriginalTile!=lTile[i]+frames*dir){
lgroup[i]->OriginalTile = lTile[i]+frames*dir;
lgroup[i]->Tile = lgroup[i]->OriginalTile+j;
}
if(lgroup[i]->ID==LW_BEAM&&lgroup[i]->DeadState==WDS_ALIVE){
tile += frames*dir;
if(Link->HP>0){
if(lFlags[i]&MIF_2X2)
Screen->DrawTile(2, x, y-2-lgroup[i]->Z, tile, 2, 2, lgroup[i]->CSet, -1, -1, 0, 0, 0, 0, true, 128);
else
Screen->FastTile(2, x, y-2-lgroup[i]->Z, tile, lgroup[i]->CSet, 128);
}
}
}
else if(lFlags[i]&MIF_8WAY){ //8 way animations
dir = AngleDir8(WrapDegrees(lAng[i]));
if(lType[i]!=3)
lgroup[i]->Dir = AngleDir4(WrapDegrees(lAng[i]));
j = (lgroup[i]->Tile-lTile[i])%frames;
if(lgroup[i]->ID!=LW_BEAM&&lgroup[i]->OriginalTile!=lTile[i]+frames*dir){
lgroup[i]->OriginalTile = lTile[i]+frames*dir;
lgroup[i]->Tile = lgroup[i]->OriginalTile+j;
}
if(lgroup[i]->ID==LW_BEAM&&lgroup[i]->DeadState==WDS_ALIVE){
tile += frames*dir;
if(Link->HP>0){
if(lFlags[i]&MIF_2X2)
Screen->DrawTile(2, x, y-2-lgroup[i]->Z, tile, 2, 2, lgroup[i]->CSet, -1, -1, 0, 0, 0, 0, true, 128);
else
Screen->FastTile(2, x, y-2-lgroup[i]->Z, tile, lgroup[i]->CSet, 128);
}
}
}
else if(lFlags[i]&MIF_360WAY){ //360 degree animations
lgroup[i]->DrawYOffset = -1000;
if(lType[i]!=3)
lgroup[i]->Dir = AngleDir4(WrapDegrees(lAng[i]));
if(Link->HP>0){
if(lFlags[i]&MIF_2X2)
DrawTile(2, x, y-2-lgroup[i]->Z, tile, 2, 2, lgroup[i]->CSet, -1, -1, x, y-2-lgroup[i]->Z, lAng[i], 0, true, 128);
else
DrawTile(2, x, y-2-lgroup[i]->Z, tile, 1, 1, lgroup[i]->CSet, -1, -1, x, y-2-lgroup[i]->Z, lAng[i], 0, true, 128);
}
}
else{ //Static animations
if(lgroup[i]->ID==LW_BEAM&&lgroup[i]->DeadState==WDS_ALIVE){
if(Link->HP>0){
if(lFlags[i]&MIF_2X2)
DrawTile(2, x, y-2-lgroup[i]->Z, tile, 2, 2, lgroup[i]->CSet, -1, -1, 0, 0, 0, 0, true, 128);
else
DrawTile(2, x, y-2-lgroup[i]->Z, tile, 1, 1, lgroup[i]->CSet, -1, -1, 0, 0, 0, 0, true, 128);
}
}
}
//Keep track of the weapon's position so we know where to put death effects when it dies
lLastX[i] = x;
lLastY[i] = y;
if(lFlags[i]&MIF_PIERCE){
lgroup[i]->DeadState = -1;
lgroup[i]->Dir = -1;
//Pierce is weird and lets weapons go way offscreen but only when the planets are aligned just right.
//When I tried to reproduce this in another part of the script, it said "No!"
if(lgroup[i]->X<-80||lgroup[i]->X>256+64||lgroup[i]->Y<-80||lgroup[i]->Y>176+64)
lgroup[i]->DeadState = 0;
}
//This handles the creation of bomb explosions on replacement explosion weapons
if(vars[12]>0){
lgroup[i]->CollDetection = false;
if(lTimer[i]%30==0){
if(vars[12]==1){
lweapon boom = CreateLWeaponAt(LW_BOMBBLAST, x+Rand(-8*lgroup[i]->TileWidth, 8*lgroup[i]->TileWidth), y+Rand(-8*lgroup[i]->TileWidth, 8*lgroup[i]->TileWidth));
boom->Damage = lgroup[i]->Damage;
}
else if(vars[12]==2){
lweapon boom = CreateLWeaponAt(LW_SBOMBBLAST, x+Rand(-8*lgroup[i]->TileWidth, 8*lgroup[i]->TileWidth), y+Rand(-8*lgroup[i]->TileWidth, 8*lgroup[i]->TileWidth));
boom->Damage = lgroup[i]->Damage;
}
}
}
//Death timer for weapons
if(lDecay[i]>0){
lDecay[i]--;
if(lDecay[i]==0)
lgroup[i]->DeadState = 0;
}
}
//Otherwise we remove it
else{
//Here's also where we put special weapon death effects
if(lFlags[i]&MIF_EXPLODEDEATH){
lweapon boom = CreateLWeaponAt(LW_BOMBBLAST, lLastX[i], lLastY[i]);
boom->Damage = vars[15];
boom->Dir = AngleDir4(WrapDegrees(lAng[i]));
lFlags[i] &= ~MIF_EXPLODEDEATH;
}
//Circular shield weapons shouldn't be cleared in order to prevent an animation error.
//The way I scripted the behavior is bad and I should feel bad.
if(vars[16]==0)
MooshItem_RemLWeapon(vars, lgroup, i);
}
}
}
int MooshItem_AddLWeapon(int vars, lweapon lgroup, lweapon l, int type, int angle, int flags){
int lTimer = vars[1];
int lTile = vars[2];
int lAng = vars[3];
int lType = vars[4];
int lMisc1 = vars[5];
int lMisc2 = vars[6];
int lDecay = vars[7];
int lFlags = vars[8];
int lACounter = vars[9];
int lNumFrames = vars[10];
int lASpeed = vars[11];
int lLastX = vars[13];
int lLastY = vars[14];
//All weapons are added at the end of the active part of the array
lgroup[vars[0]] = l;
lTimer[vars[0]] = 0;
lTile[vars[0]] = l->OriginalTile;
lAng[vars[0]] = angle;
lType[vars[0]] = type;
lMisc1[vars[0]] = 0;
lMisc2[vars[0]] = 0;
lDecay[vars[0]] = 0;
lFlags[vars[0]] = flags;
lACounter[vars[0]] = 0;
lNumFrames[vars[0]] = Max(l->NumFrames, 1);
lASpeed[vars[0]] = Max(l->ASpeed, 1);
lLastX[vars[0]] = l->X;
lLastY[vars[0]] = l->Y;
//Grow the "size" of the "array" to accomodate
vars[0]++;
//But the return value is what it used to be
return vars[0]-1;
}
void MooshItem_RemLWeapon(int vars, lweapon lgroup, int i){
int lTimer = vars[1];
int lTile = vars[2];
int lAng = vars[3];
int lType = vars[4];
int lMisc1 = vars[5];
int lMisc2 = vars[6];
int lDecay = vars[7];
int lFlags = vars[8];
int lACounter = vars[9];
int lNumFrames = vars[10];
int lASpeed = vars[11];
int lLastX = vars[13];
int lLastY = vars[14];
//Whenever a weapon is removed, the weapon at the end of the array gets copied over to it.
//It's then marked as free space for a new weapon to be created over.
vars[0]--;
lgroup[i] = lgroup[vars[0]];
lTimer[i] = lTimer[vars[0]];
lTile[i] = lTile[vars[0]];
lAng[i] = lAng[vars[0]];
lType[i] = lType[vars[0]];
lMisc1[i] = lMisc1[vars[0]];
lMisc2[i] = lMisc2[vars[0]];
lDecay[i] = lDecay[vars[0]];
lFlags[i] = lFlags[vars[0]];
lACounter[i] = lACounter[vars[0]];
lNumFrames[i] = lNumFrames[vars[0]];
lASpeed[i] = lASpeed[vars[0]];
lLastX[i] = lLastX[vars[0]];
lLastY[i] = lLastY[vars[0]];
}
int MooshItem_Homing_TargetAngle(int vars, int angle, lweapon l){
int minDist = 1000;
npc min;
for(int i=Screen->NumNPCs(); i>=1; i--){
npc n = Screen->LoadNPC(i);
//If the enemy isn't flagged as beatable, don't target it
if(n->MiscFlags&(1<<3))
continue;
if(!n->CollDetection)
continue;
//Get potential value for the enemy (based on how much it resists damage)
float potential = MooshItem_CanHurt(l, n);
if(potential==0)
continue;
//A combination of the distance to the enemy and the damage potential determines which the script will target
if(Distance(CenterX(l), CenterY(l), CenterX(n), CenterY(n))-10*potential<minDist){
minDist = Distance(CenterX(l), CenterY(l), CenterX(n), CenterY(n))-10*potential;
min = n;
}
}
//If a valid target was found, return the angle to it
if(min->isValid())
return Angle(CenterX(l), CenterY(l), CenterX(min), CenterY(min));
//Otherwise return the last angle
return angle;
}
int MooshItem_BlockWeapons(int vars, lweapon l, int i, bool remOnCollision){
bool hit;
//Cycle through all eweapons
for(int j=Screen->NumEWeapons(); j>=1; j--){
eweapon e = Screen->LoadEWeapon(j);
//Remove the ones that collide with the lweapon and can be destroyed
if(Collision(e, l)){
if(MooshItem_CanBlockEWeapon(vars, i, e)){
Game->PlaySound(SFX_MOOSHITEM_BLOCK);
e->DeadState = 0;
hit = true;
}
}
}
//Only kill the lweapon once its cycled through all weapons to prevent weapon priority BS with multiple collisions
if(remOnCollision&&hit)
l->DeadState = 0;
}
bool MooshItem_CanBlockEWeapon(int vars, int i, eweapon e){
int lMisc1 = vars[5];
//This is pretty much just imitating shield block flags
if(e->ID==EW_ROCK){
if(lMisc1[i]&1)
return true;
}
else if(e->ID==EW_ARROW){
if(lMisc1[i]&2)
return true;
}
else if(e->ID==EW_BRANG){
if(lMisc1[i]&4)
return true;
}
else if(e->ID==EW_FIREBALL){
if(lMisc1[i]&8)
return true;
}
else if(e->ID==EW_BEAM){
if(lMisc1[i]&16)
return true;
}
else if(e->ID==EW_MAGIC){
if(lMisc1[i]&32)
return true;
}
else if(e->ID==EW_FIRE||e->ID==EW_FIRE2){
if(lMisc1[i]&64)
return true;
}
else if(e->ID<=EW_SCRIPT10){
if(lMisc1[i]&128)
return true;
}
else if(e->ID==EW_FIREBALL2){
if(lMisc1[i]&256)
return true;
}
//But there's an extra flag for bombs and supers
else if(e->ID==EW_BOMB||e->ID==EW_SBOMB){
if(lMisc1[i]&512)
return true;
}
return false;
}
float MooshItem_CanHurt(lweapon l, npc n){
int type = l->ID;
int def;
//This is imitating enemy editor defenses
if(type>=LW_SCRIPT1)
def = n->Defense[NPCD_SCRIPT];
else if(type==LW_BRANG)
def = n->Defense[NPCD_BRANG];
else if(type==LW_BOMBBLAST)
def = n->Defense[NPCD_BOMB];
else if(type==LW_SBOMBBLAST)
def = n->Defense[NPCD_SBOMB];
else if(type==LW_ARROW)
def = n->Defense[NPCD_ARROW];
else if(type==LW_FIRE)
def = n->Defense[NPCD_FIRE];
else if(type==LW_MAGIC)
def = n->Defense[NPCD_MAGIC];
else if(type==LW_BEAM)
def = n->Defense[NPCD_BEAM];
else if(type==LW_REFBEAM)
def = n->Defense[NPCD_REFBEAM];
else if(type==LW_REFMAGIC)
def = n->Defense[NPCD_REFMAGIC];
else if(type==LW_REFROCK)
def = n->Defense[NPCD_REFROCK];
//But the return value is treated as the enemy's damage potential.
//This should make the script priotitize enemies that it will deal more damage to.
if(def==NPCDT_BLOCK||def==NPCDT_IGNORE)
return 0;
if(def==NPCDT_ONEHITKILL)
return 10;
if(def>=NPCDT_BLOCK1&&def<=NPCDT_BLOCK8||def==NPCDT_IGNORE1){
if(def==NPCDT_BLOCK8&&l->Damage>=16)
return 1;
else if(def==NPCDT_BLOCK6&&l->Damage>=12)
return 1;
else if(def==NPCDT_BLOCK4&&l->Damage>=8)
return 1;
else if(def==NPCDT_BLOCK2&&l->Damage>=4)
return 1;
else if(l->Damage>=2)
return 1;
return 0;
}
if(def==NPCDT_HALFDAMAGE)
return 0.5;
else if(def==NPCDT_QUARTERDAMAGE)
return 0.25;
return 1;
}
bool MooshItem_CanTakeResource(int ID, int mpCost){
if(mpCost==0)
return true;
itemdata itemdat = Game->LoadItemData(ID);
if(itemdat->Counter>-1){ //If a counter is set
if(itemdat->Counter==CR_LIFE){ //If the counter is health, don't allow it to hit 0
if(Game->Counter[itemdat->Counter]<=mpCost)
return false;
}
else if(Game->Counter[itemdat->Counter]<mpCost)
return false;
}
else{ //Otherwise it's a regular magic cost
//Only magic costs are affected by 1/2 magic
mpCost = mpCost*Game->Generic[GEN_MAGICDRAINRATE];
if(Link->MP<mpCost)
return false;
}
return true;
}
void MooshItem_TakeResource(int ID, int mpCost){
if(mpCost==0)
return;
itemdata itemdat = Game->LoadItemData(ID);
if(itemdat->Counter>-1){ //If a counter is set
if(itemdat->Counter==CR_LIFE){ //If the counter is health, don't allow it to hit 0
if(Game->Counter[itemdat->Counter]>mpCost){
Game->Counter[itemdat->Counter] -= mpCost;
Game->PlaySound(SFX_MOOSHITEM_HP_COST);
if(Link->Action==LA_NONE||Link->Action==LA_WALKING||Link->Action==LA_ATTACKING||Link->Action==LA_GOTHURTLAND)
Link->Action = LA_GOTHURTLAND;
else if(Link->Action==LA_SWIMMING||Link->Action==LA_GOTHURTWATER)
Link->Action = LA_GOTHURTWATER;
Link->HitDir = -1;
}
}
else if(Game->Counter[itemdat->Counter]>=mpCost)
Game->Counter[itemdat->Counter] -= mpCost;
}
else{ //Otherwise it's a regular magic cost
//Only magic costs are affected by 1/2 magic
mpCost = mpCost*Game->Generic[GEN_MAGICDRAINRATE];
if(Link->MP>=mpCost)
Link->MP -= mpCost;
}
}
int MooshItem_8WayAimAngle(int angle){
int xAxis;
int yAxis;
//Convert Link's directional inputs into axes so opposite inputs cancel out
if(Link->InputLeft&&!Link->InputRight)
xAxis = -1;
else if(!Link->InputLeft&&Link->InputRight)
xAxis = 1;
if(Link->InputUp&&!Link->InputDown)
yAxis = -1;
else if(!Link->InputUp&&Link->InputDown)
yAxis = 1;
//If he's holding a cardinal diretcion face that way
if(yAxis==-1&&xAxis==0)
Link->Dir=DIR_UP;
else if(yAxis==1&&xAxis==0)
Link->Dir=DIR_DOWN;
else if(xAxis==-1&&yAxis==0)
Link->Dir=DIR_LEFT;
else if(xAxis==1&&yAxis==0)
Link->Dir=DIR_RIGHT;
//But if he's holding the opposite of the direction he's facing, turn him around
if(Link->Dir==DIR_UP&&yAxis==1)
Link->Dir=DIR_DOWN;
else if(Link->Dir==DIR_UP&&yAxis==-1)
Link->Dir=DIR_UP;
else if(Link->Dir==DIR_LEFT&&xAxis==1)
Link->Dir=DIR_RIGHT;
else if(Link->Dir==DIR_RIGHT&&xAxis==-1)
Link->Dir=DIR_LEFT;
//Left up
if(xAxis==-1&&yAxis==-1)
return -135;
//Right up
else if(xAxis==1&&yAxis==-1)
return -45;
//Left down
else if(xAxis==-1&&yAxis==1)
return 135;
//Right down
else if(xAxis==1&&yAxis==1)
return 45;
//Up
else if(yAxis==-1)
return -90;
//Down
else if(yAxis==1)
return 90;
//Left
else if(xAxis==-1)
return 180;
//Right
else if(xAxis==1)
return 0;
return angle;
}
bool MooshItem_CanWalk(lweapon l, int dir, int step){
int x; int y;
//Returns whether a square weapon can move in a direction
for(int i=0; i<=l->HitWidth-1; i=Min(i+8, l->HitWidth-1)){
if(dir==DIR_UP){
x = l->X+l->HitXOffset+i;
y = l->Y+l->HitYOffset-step;
}
else if(dir==DIR_DOWN){
x = l->X+l->HitXOffset+i;
y = l->Y+l->HitYOffset+l->HitHeight-1+step;
}
else if(dir==DIR_LEFT){
x = l->X+l->HitXOffset-step;
y = l->Y+l->HitYOffset+i;
}
else if(dir==DIR_RIGHT){
x = l->X+l->HitXOffset+l->HitWidth-1+step;
y = l->Y+l->HitYOffset+i;
}
if(Screen->isSolid(x, y)){
//Account for combo types that are solid but aren't "solid"
int cd = Screen->ComboD[ComboAt(x, y)];
if(cd!=CT_WATER&&cd!=CT_SWIMWARP&&cd!=CT_SWIMWARPB&&cd!=CT_SWIMWARPC&&cd!=CT_SWIMWARPD&&
cd!=CT_DIVEWARP&&cd!=CT_DIVEWARPB&&cd!=CT_DIVEWARPC&&cd!=CT_DIVEWARPD&&
cd!=CT_LADDERONLY&&cd!=CT_LADDERHOOKSHOT&&cd!=CT_HOOKSHOTONLY)
return false;
}
//Return when the end of the collision line is reached
if(i==l->HitWidth-1)
return true;
}
return true;
}
//Duplicate of LinkCollision that doesn't account for Z-axis
//This prevents Link jumping onto fire/reflected weapons and hurting himself
bool MooshItem_LinkCollisionNoZ(lweapon b) {
int ax = Link->X + Link->HitXOffset;
int bx = b->X + b->HitXOffset;
int ay = Link->Y + Link->HitYOffset;
int by = b->Y + b->HitYOffset;
return RectCollision(ax, ay, ax+Link->HitWidth, ay+Link->HitHeight, bx, by, bx+b->HitWidth, by+b->HitHeight);
}
}
//This FFC script handles the sword animations
ffc script MooshItem_Animations{
void run(int animType, int ID_mpCost, int type_step, int wType_maxShot, int arg1_arg2, int arg3_arg4, int sprite_sfx, int flags){
int i; int j; int k;
lweapon melee;
int meleeSprite = MooshItem_LeftArg(sprite_sfx)+1;
int ID = MooshItem_LeftArg(ID_mpCost);
itemdata itemdat = Game->LoadItemData(ID);
int damage = itemdat->Power*2;
int startTile;
melee = CreateLWeaponAt(LW_MOOSHITEM_MELEE, -48, -48);
melee->UseSprite(meleeSprite);
startTile = melee->OriginalTile;
melee->DeadState = 0;
int args[8];
args[0] = ID_mpCost;
args[1] = type_step;
args[2] = wType_maxShot;
args[3] = arg1_arg2;
args[4] = arg3_arg4;
args[5] = sprite_sfx;
args[6] = flags;
int handler[] = "MooshItem_Handler";
//These are all reproductions of the slash animations in ZC as measured with a script
//Some minor details like the little step forward Link takes aren't included
if(animType==0){ //Stab
Game->PlaySound(SFX_MOOSHITEM_MELEE);
for(i=0; i<10; i++){
if(Link->Dir==DIR_UP){
if(i<8)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -1, -10, DIR_UP, damage);
else if(i<9)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -1, -6, DIR_UP, damage);
else
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -1, -2, DIR_UP, damage);
}
else if(Link->Dir==DIR_DOWN){
if(i<8)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 1, 13, DIR_DOWN, damage);
else if(i<9)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 1, 9, DIR_DOWN, damage);
else
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 1, 5, DIR_DOWN, damage);
}
else if(Link->Dir==DIR_LEFT){
if(i<8)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -11, 3, DIR_LEFT, damage);
else if(i<9)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -7, 3, DIR_LEFT, damage);
else
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -3, 3, DIR_LEFT, damage);
}
else if(Link->Dir==DIR_RIGHT){
if(i<8)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 11, 3, DIR_RIGHT, damage);
else if(i<9)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 7, 3, DIR_RIGHT, damage);
else
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 3, 3, DIR_RIGHT, damage);
}
Link->Action = LA_NONE;
Link->Action = LA_ATTACKING;
if(i==8)
RunFFCScript(Game->GetFFCScript(handler), args);
if(melee->isValid())
melee->Dir = Link->Dir;
Waitframe();
}
}
else if(animType==1){ //Slash
Game->PlaySound(SFX_MOOSHITEM_MELEE);
for(i=0; i<14; i++){
if(Link->Dir==DIR_UP){
if(i<6)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 15, -1, DIR_RIGHT, damage);
else if(i<10)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 12, -10, DIR_RIGHTUP, damage);
else if(i<13)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -1, -15, DIR_UP, damage);
else
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -1, -10, DIR_UP, damage);
}
else if(Link->Dir==DIR_DOWN){
if(i<6)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -13, 1, DIR_LEFT, damage);
else if(i<10)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -11, 16, DIR_LEFTDOWN, damage);
else if(i<13)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 1, 18, DIR_DOWN, damage);
else
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 1, 13, DIR_DOWN, damage);
}
else if(Link->Dir==DIR_LEFT){
if(i<6)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 3, -13, DIR_UP, damage);
else if(i<10)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -12, -10, DIR_LEFTUP, damage);
else if(i<13)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -18, 3, DIR_LEFT, damage);
else
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -11, 3, DIR_LEFT, damage);
}
else if(Link->Dir==DIR_RIGHT){
if(i<6)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 3, -13, DIR_UP, damage);
else if(i<10)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 18, -10, DIR_RIGHTUP, damage);
else if(i<13)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 18, 3, DIR_RIGHT, damage);
else
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 11, 3, DIR_RIGHT, damage);
}
if(i<10){
Link->Action = LA_NONE;
Link->Action = LA_ATTACKING;
}
if(i==10)
RunFFCScript(Game->GetFFCScript(handler), args);
if(melee->isValid())
melee->Dir = Link->Dir;
Waitframe();
}
}
else if(animType==2){ //Z3 Slash
Game->PlaySound(SFX_MOOSHITEM_MELEE);
for(i=0; i<14; i++){
if(Link->Dir==DIR_UP){
if(i<6)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 18, -10, DIR_RIGHTUP, damage);
else if(i<10)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -1, -15, DIR_UP, damage);
else if(i<13)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -12, -10, DIR_LEFTUP, damage);
else
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -5, -5, DIR_LEFTUP, damage);
}
else if(Link->Dir==DIR_DOWN){
if(i<6)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -11, 16, DIR_LEFTDOWN, damage);
else if(i<10)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 1, 18, DIR_DOWN, damage);
else if(i<13)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 15, 13, DIR_RIGHTDOWN, damage);
else
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 8, 8, DIR_RIGHTDOWN, damage);
}
else if(Link->Dir==DIR_LEFT){
if(i<6)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -12, -10, DIR_LEFTUP, damage);
else if(i<10)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -18, 3, DIR_LEFT, damage);
else if(i<13)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -11, 16, DIR_LEFTDOWN, damage);
else
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, -4, 11, DIR_LEFTDOWN, damage);
}
else if(Link->Dir==DIR_RIGHT){
if(i<6)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 18, -10, DIR_RIGHTUP, damage);
else if(i<10)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 18, 3, DIR_RIGHT, damage);
else if(i<13)
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 15, 13, DIR_RIGHTDOWN, damage);
else
melee = MooshItem_UpdateMeleeAnim(melee, meleeSprite, startTile, 8, 8, DIR_RIGHTDOWN, damage);
}
if(i<10){
Link->Action = LA_NONE;
Link->Action = LA_ATTACKING;
}
if(i==10)
RunFFCScript(Game->GetFFCScript(handler), args);
if(melee->isValid())
melee->Dir = Link->Dir;
Waitframe();
}
}
if(melee->isValid())
melee->DeadState = 0;
}
lweapon MooshItem_UpdateMeleeAnim(lweapon melee, int sprite, int startTile, int x, int y, int dir, int damage){
//This updates the melee lweapon and ensures that one always exists
if(melee->isValid()){
melee->X = Link->X+x;
melee->Y = Link->Y+y;
melee->Z = Link->Z;
}
else{
melee = CreateLWeaponAt(LW_MOOSHITEM_MELEE, Link->X+x, Link->Y+y);
melee->Z = Link->Z;
melee->UseSprite(sprite);
melee->Step = 0;
melee->Damage = damage;
}
//Change the tile based on the current direction
int frames = Max(melee->NumFrames, 1);
melee->OriginalTile = startTile+frames*dir;
melee->Tile = melee->OriginalTile+((melee->Tile-melee->OriginalTile)%frames);
return melee;
}
}