import "std.zh" // only need this once
import "ffcscript.zh" // only need this once
import "string.zh"
//Index
// F1.0-F1.1 Sideview Ladder and Global
// F1.2 Moving Platform
// F1.3 Circular Motion
//F1.4 Moosh NPC
//F1.5 Alternate NPC
//F1.6 RealNPC
//G1.0 Global Script
// F1.0-F1.1 Sideview Ladder and Global
// ----------------------------------------------------------------------
// Setup
//
// 1. Modify the constants to fit your needs. It looks for both, so they can potentially be used by other scripts.
// If so, the other script will probably want to make sure it isn't both. isSVLadder function is helpful for this.
// 2. Use one of the existing globals (see step#3) if no other global scripts.
// Or see the samp` le globals at the bottom of the script to see how to combine.
// 3. Decide if using the global version or FFC version. DON'T USE BOTH!!!!
// Global - if you have lots of screens with the sideview ladder combos
// FFC - only a few sideview ladder screens, or want more flexibility reusing the Script ComboTypes/Flags.
// Both versions require adding things to the global script. Each has its own sample global script at the bottom.
// 3a. If using the FFC version, just place the Sideview_Ladder FFC on that screen. Won't run on non-SV screens.
// See the sample global, as there are numerous things to add to global for the FFC version.
// 3b. If using the global version, add or uncomment the GLB_Sideview_Ladder(); part of the global script.
// See the sample global for what needs to be included.
// 4. Watch out for compile conflicts with the isSolid function that many other scripts use.
// Optional Setup
//
// Roc's Feather
// 1. The script looks for only the default Roc Feather item#.
// So if you've changed that, or have multiple levels of Roc items, you'll need to mod that part of the script.
//
// Item use on ladder
// 1. A/B buttons are still enabled, allowing item use on the ladder
// Expect weird behavior with certain items due to the script changing Link's direction.
// i.e. can stab sword to the side while facing up
// If you use the hookshot, you're likely going to need to modify the script to make it work right, or disable it.
// ----------------------------------------------------------------------
// Constants
//Common Constant, only need to define once per script file.
const int BIG_LINK = 0; //Set this constant to 1 if using the Large Link Hit Box feature.
//Constants used by Bottomless Pits & Lava.
const int CT_HOLELAVA = 128; //Combo type to use for pit holes and lava."No Ground Enemies by default"
const int CF_PIT = 100; //The combo flag to register combos as pits.
const int CF_LAVA = 101; //The combo flag to register combos as lava.
const int WPS_LINK_FALL = 40; //The weapon sprite to display when Link falls into a pit. "Sprite 88 by default"
const int WPS_LINK_LAVA = 89; //The weapon sprite to display when Link drowns in lava. "Sprite 89 by default"
const int SFX_LINK_FALL = 38; //The sound to play when Link falls into a pit. "SFX_FALL by default"
const int SFX_LINK_LAVA = 55; //The sound to play when Link drowns in Lava. "SFX_SPLASH by default.
const int CMB_AUTOWARP = 888; //The first of your four transparent autowarp combos.
const int HOLELAVA_DAMAGE = 8; //Damage in hit points to inflict on link. "One Heart Container is worth 16 hit points"
//NPC constants
const int RESET_NPC_LAYER_ON_ENTRY = 1; //This fix will replace all CMB_NPC_SOLID tiles with CMB_NPC_HIDDEN
//when entering the screen. This should prevent problems with shared
//layers. If you don't want this fix, set it to 0.
const int LAYER_NPC = 2; //The layer NPCs use for solid combos
const int CMB_NPC_HIDDEN = 41; //Non-solid combo used for hidden NPCs
const int CMB_NPC_SOLID = 42; //Solid combo placed under visible NPCs
const int LAYER_NPC_CANTALK = 4; //The layer used for the speech bubble
const int CMB_NPC_CANTALK = 37; //The combo used for the speech bubble
const int CS_NPC_CANTALK = 8; //The CSet used for the speech bubble
const int NPCBT_NONE = 0; //Regular NPCs
const int NPCBT_FACELINK = 1; //NPCs that turn to face Link
const int NPCBT_GUARDH = 2; //NPCs that move along a horizontal path
const int NPCBT_GUARDV = 3; //NPCs that move along a vertical path
//Alternate NPC constant
const int CMB_AUTOWARPD = 0; //Only include this once in the script file. Set to the number of an Autowarp-type combo you create, preferably transparent.
//Bracelet constants
const int CF_PICK = 98; // SCRIPT1, bracelet
const int SCRIPT_POWERBRACELET = 9; // set this to the ffc script slot assigned to PowerBracelet script when compiling
const int LTM_CATCHING = 123; // LTM for Link catching a block with the Power Bracelet
const int LTM_PULLING = 125; // LTM for Link pulling a block with the Power Bracelet
const int LTM_HOLDING = 124; // LTM for Link holding a block with the Power Bracelet
const int BLOCK_VH=4; //thrown block/bush horizontal initial velocity
const int BLOCK_VV=0; //thrown block/bush vertical initial velocity
const int BLOCK_DMG=8; //damage dealt to enemies by thrown block/bush
const int LW_BLOCK = 31; //id of a lweapon to be used as thrown block
const float BLOCK_FALL = 0.5; //gravity acceleration for block in sideview screens
const int PB_PULL_TIME=14; // num of frames to wait for pickup with PB
const int PB_UNDERCOMBO=0; // combo to set after picking up a block; set a negative value to have a shift of the original combo
const int SFX_PICKUP_BLOCK = 45; // sfx played when link picks up the block
const int SFX_THROW_BLOCK = 51; // sfx played when the block is thrown
const int SFX_CRASH_BLOCK = 11; // sfx of a block crashing
const int INV_COMBO_ID = 0; // id of an invisible combo
const int INV_TILE_ID = 4; // id of an invisible tile
const int CRASH_SPR = 88; // sprite for a block crashing at ground
const int BUSH_SPR = 54; // sprite for a bush crashing at ground
const int LAYER_OVER = 4; // an overhead layer
const int NPC_ITEMSET = 182; // id of a dummy enemy with type different from "none"
// Sideview Ladder constant
const int CT_SVLADDER = 142; // CT_SCRIPT1, ComboType for Sideview Ladder
const int CF_SVLADDER = 99; // CF_SCRIPT2, ComboFlag for Sideview Ladder
// This ComboFlags can be reused since this script requires both the ComboType and Flag.
// If reusing, that script should check that it isn't the SV ComboType.
// ^^What is SV Combotype in regards to this?^^
// ----------------------------------------------------------------------
// Global variable
//sideview ladder
bool onLadder = false;
//power bracelets
bool throw_disabled;
int holding_block;
bool holding_bush;
int link_catching;
//Global variables used by Bottomless Pits & Lava.
int Falling;
bool Warping;
//--------------------------------------------------------------------
//G1.0 Global Script
global script Slot_2_GLB_Version{
void run(){
//Initialize variables used to store Link's strating position on Screen Init.
int olddmap = Game->GetCurDMap();
int oldscreen = Game->GetCurDMapScreen();
int startx = Link->X;
int starty = Link->Y;
int startdir = Link->Dir;
//Clear global variables used by Bottomless pits.
Falling = 0;
Warping = false;
holding_block = 0;
while(true){
if(Link->Action != LA_SCROLLING)
{
Update_HoleLava(startx, starty, olddmap, oldscreen, startdir);
if(Link->Z==0 && !Falling && (oldscreen != Game->GetCurDMapScreen() || olddmap != Game->GetCurDMap()))
{
olddmap = Game->GetCurDMap();
oldscreen = Game->GetCurDMapScreen();
startx = Link->X;
starty = Link->Y;
startdir = Link->Dir;
}
}
PowerBracelet();
GLB_Sideview_Ladder(); //********* (before waitdraw)
Waitdraw();
if (onLadder) Link->Dir = DIR_UP; //********* (After Waitdraw)
Waitframe();
}//end whileloop
}//end run
}//end global slot2 Global Version
global script Slot_3{
void run(){
Link->Item[LTM_CATCHING] = false;
Link->Item[LTM_HOLDING] = false;
Link->Item[LTM_PULLING] = false;
holding_block = 0;
holding_bush = false;
link_catching = 0;
}
}
void PowerBracelet(){
if(Link->Item[LTM_HOLDING]){
if(CountFFCsRunning(SCRIPT_POWERBRACELET)==0 && holding_block>0){
holding_block = 0;
Link->Item[LTM_HOLDING] = false;
Link->Item[LTM_CATCHING] = false;
Link->Item[LTM_PULLING] = false;
}
}
}
//----------------------------------------------------------------------------------------------------------------------------------
// F1.2 Moving Platform
//This function simply checks if that the center of Link is within the bounds of the ffc.
bool OnPlatform(ffc this){
if(CenterLinkX() < this->X) return false;
else if(CenterLinkX() >= this->X + this->EffectWidth) return false;
else if(CenterLinkY() < this->Y) return false;
else if(CenterLinkY() >= this->Y + this->EffectHeight) return false;
else return true;
}
//This script turns a moving ffc into a platform so it carries Link.
ffc script Platform{
void run(){
//Initialize two variables to store the position of the ffc the previous frame.
float oldx = this->X;
float oldy = this->Y;
//Initialize two variables to store the change in Link's position each frame.
float linkx;
float linky;
//Loop Indefinitely.
while(true){
//Check if Link is on the platform and is not mid-air.
if(OnPlatform(this) && Link->Z == 0){
//Increment linkx by the change in the ffc's position along the x axis.
linkx += this->X - oldx;
//Check if the absolute value of linkx truncated to an int doesn't = 0.
if(linkx<<0 != 0){
//Increment link's position by linkx truncated removed.
Link->X += linkx<<0;
//Decrement linkx by itself rounded down.
linkx -= linkx<<0;
}
//Vertical Movement is the same as horizontal movement just along the y axis.
linky += this->Y - oldy;
if(linky<<0 != 0){
Link->Y += linky<<0;
linky -= linky<<0;
}
}
//Set oldx and oldy to the ffc's current position.
oldx = this->X;
oldy = this->Y;
//Waitframe to prevent lag.
Waitframe();
}
}
}
//-------------------------------------------------------------------
//F1.7 Pits and Lava
//Handles Pit Combo Functionality.
void Update_HoleLava(int x, int y, int dmap, int scr, int dir)
{
lweapon hookshot = LoadLWeaponOf(LW_HOOKSHOT);
if(hookshot->isValid()) return;
if(Falling)
{
Falling--;
if(Falling == 1)
{
int buffer[] = "holelava";
if(CountFFCsRunning(Game->GetFFCScript(buffer)))
{
ffc f = Screen->LoadFFC(FindFFCRunning(Game->GetFFCScript(buffer)));
Warping = true;
if(f->InitD[1]==0)
{
f->InitD[6] = x;
f->InitD[7] = y;
}
}
else
{
Link->X = x;
Link->Y = y;
Link->Dir = dir;
Link->DrawXOffset -= Cond(Link->DrawXOffset < 0, -1000, 1000);
Link->HitXOffset -= Cond(Link->HitXOffset < 0, -1000, 1000);
Link->HP -= HOLELAVA_DAMAGE;
Link->Action = LA_GOTHURTLAND;
Link->HitDir = -1;
Game->PlaySound(SFX_OUCH);
if(Game->GetCurDMap()!=dmap || Game->GetCurDMapScreen()!=scr)
Link->PitWarp(dmap, scr);
}
NoAction();
Link->Action = LA_NONE;
}
}
else if(Link->Z==0 && OnPitCombo() && !Warping)
{
Link->DrawXOffset += Cond(Link->DrawXOffset < 0, -1000, 1000);
Link->HitXOffset += Cond(Link->HitXOffset < 0, -1000, 1000);
int comboflag = OnPitCombo();
SnaptoGrid();
Game->PlaySound(Cond(comboflag == CF_PIT, SFX_LINK_FALL, SFX_LINK_LAVA));
lweapon dummy = CreateLWeaponAt(LW_SCRIPT10, Link->X, Link->Y);
dummy->UseSprite(Cond(comboflag == CF_PIT, WPS_LINK_FALL, WPS_LINK_LAVA));
dummy->DeadState = dummy->NumFrames*dummy->ASpeed;
dummy->DrawXOffset = 0;
dummy->DrawYOffset = 0;
Falling = dummy->DeadState;
NoAction();
Link->Action = LA_NONE;
}
}
ffc script Holelava
{
void run(int warp, bool position, int damage)
{
while(true)
{
while(!Warping) Waitframe();
if(warp > 0)
{
this->Data = CMB_AUTOWARP+warp-1;
this->Flags[FFCF_CARRYOVER] = true;
Waitframe();
this->Data = FFCS_INVISIBLE_COMBO;
this->Flags[FFCF_CARRYOVER] = false;
Link->Z = Link->Y;
Warping = false;
Link->DrawXOffset -= Cond(Link->DrawXOffset < 0, -1000, 1000);
Link->HitXOffset -= Cond(Link->HitXOffset < 0, -1000, 1000);
Quit();
}
if(position)
{
Link->X = this->X;
Link->Y = this->Y;
}
else
{
Link->X = this->InitD[6];
Link->Y = this->InitD[7];
}
if(damage)
{
Link->HP -= damage;
Link->Action = LA_GOTHURTLAND;
Link->HitDir = -1;
Game->PlaySound(SFX_OUCH);
}
Link->DrawXOffset -= Cond(Link->DrawXOffset < 0, -1000, 1000);
Link->HitXOffset -= Cond(Link->HitXOffset < 0, -1000, 1000);
Warping = false;
Waitframe();
}
}
}
//Used to determine if Link is on a Pit or Lava combo.
int OnPitCombo()
{
int comboLoc = ComboAt(Link->X+8, Link->Y + Cond(BIG_LINK==0, 12, 8));
if(Screen->ComboT[comboLoc] != CT_HOLELAVA)
return 0;
else if(Screen->ComboI[comboLoc] == CF_PIT || Screen->ComboI[comboLoc] == CF_LAVA)
return Screen->ComboI[comboLoc];
else if(Screen->ComboF[comboLoc] == CF_PIT || Screen->ComboF[comboLoc] == CF_LAVA)
return Screen->ComboF[comboLoc];
else
return 0;
}
//Snaps Link to the combo so he appears completely over pit and lava combos.
void SnaptoGrid()
{
int x = Link->X;
int y = Link->Y + Cond(BIG_LINK==0, 8, 0);
int comboLoc = ComboAt(x, y);
//X Axis
if(Screen->ComboT[comboLoc] == CT_HOLELAVA && Cond(x % 16 == 0, true, Screen->ComboT[comboLoc+1] != CT_HOLELAVA))
Link->X = ComboX(comboLoc);
else if(Screen->ComboT[comboLoc+1] == CT_HOLELAVA && Cond(x % 16 == 0, true, Screen->ComboT[comboLoc] != CT_HOLELAVA))
Link->X = ComboX(comboLoc+1);
if(Cond(y % 16 == 0, false, Screen->ComboT[comboLoc+16] == CT_HOLELAVA) && Cond(x % 16 == 0, true, Screen->ComboT[comboLoc+17] != CT_HOLELAVA))
Link->X = ComboX(comboLoc+16);
else if(Cond(y % 16 == 0, false, Screen->ComboT[comboLoc+17] == CT_HOLELAVA) && Cond(x % 16 == 0, true, Screen->ComboT[comboLoc+16] != CT_HOLELAVA))
Link->X = ComboX(comboLoc+17);
//Y Axis
if(Screen->ComboT[comboLoc] == CT_HOLELAVA && Cond(y % 16 == 0, true, Screen->ComboT[comboLoc+16] != CT_HOLELAVA))
Link->Y = ComboY(comboLoc);
else if(Screen->ComboT[comboLoc+16] == CT_HOLELAVA && Cond(y % 16 == 0, true, Screen->ComboT[comboLoc] != CT_HOLELAVA))
Link->Y = ComboY(comboLoc+16);
if(Cond(x % 16 == 0, false, Screen->ComboT[comboLoc+1] == CT_HOLELAVA) && Cond(y % 16 == 0, true, Screen->ComboT[comboLoc+17] != CT_HOLELAVA))
Link->Y = ComboY(comboLoc+1);
else if(Cond(x % 16 == 0, false, Screen->ComboT[comboLoc+17] == CT_HOLELAVA) && Cond(y % 16 == 0, true, Screen->ComboT[comboLoc+1] != CT_HOLELAVA))
Link->Y = ComboY(comboLoc+17);
}