///////////////////////////////////////////////////// //////////////// Mirror of the Moon ///////////////// /////// Scrolling & Engine Script, ver 0.01////////// ///////////////// by blue_knight //////////////////// ///////////////////////////////////////////////////// ///////////////////////////////////////////////////// // Sound FX ///////////////////////////////////////////////////// const int SFX_LIFT = 61; const int SFX_LIFT_BUSH = 62; const int SFX_SHATTER = 63; const int SFX_BUSH = 64; const int SFX_SWORD_SHOUT1 = 65; const int SFX_SWORD_SHOUT2 = 66; const int SFX_SWORD_SHOUT3 = 67; const int SFX_SELECT = 68; const int SFX_FADE_IN = 69; const int SFX_OPEN_WINDOW = 70; const int SFX_CLOSE_WINDOW = 71; const int SFX_MENU_CURSOR = 72; const int SFX_CHEST_OPEN = 73; const int SFX_LINK_SHIELD = 74; const int SFX_ERROR = 75; const int SFX_MENU_SELECT = 76; const int SFX_HEART = 77; const int SFX_HEART_BOUNCE = 78; const int SFX_RUPEE = 79; const int SFX_RUPEE_BOUNCE = 80; const int SFX_DIVE = 81; const int SFX_FLOOR_SWITCH = 82; ///////////////////////////////////////////////////// // Link Temporary Data. ///////////////////////////////////////////////////// const int LMISC_FRAME = 0; const int LMISC_FLAGS = 1; const int LMISC_WARP_FLAGS = 2; //"union" - A const int LMISC_WARP_X = 3; const int LMISC_WARP_Y = 4; //"union" - B const int LMISC_HIT_FRAME = 3; const int LMISC_PREV_X = 4; // const int LMISC_STATE = 5; const int LMISC_ZVEL = 6; const int LMISC_PREV_Y = 7; const int LMISC_HELD_ITEM = 8; const int LMISC_HELD_ITEM_TYPE = 9; const int LMISC_DAMAGE_ANIM = 10; const int LMSIC_KNOCKBACKX = 11; const int LMSIC_KNOCKBACKY = 12; const int LMSIC_KNOCKBACK_FRAMES = 13; const int LMISC_TIMER = 14; const int LMISC_MISC = 15; ///////////////////////////////////////////////////// // Link State. ///////////////////////////////////////////////////// const int LSTATE_NORMAL = 0; const int LSTATE_LEDGE_JUMP = 1; const int LSTATE_HOLDING_ITEM = 2; const int LSTATE_PUSHING = 3; const int LSTATE_ITEM_FOUND = 4; const int LSTATE_ATTACKING = 5; const int LSTATE_ATTACK_HOLD = 6; const int LSTATE_SPIN_ATTACK = 7; const int LSTATE_BLOCKING = 8; const int LSTATE_THROW_WPN = 9; const int LSTATE_DROWN = 10; const int LSTATE_FALL = 11; ///////////////////////////////////////////////////// // Weapon Data. ///////////////////////////////////////////////////// const int WEAPON_ACTIVE = 0; const int WEAPON_X = 1; const int WEAPON_Y = 2; const int WEAPON_DIR_X = 3; const int WEAPON_DIR_Y = 4; const int WEAPON_SPEED = 5; const int WEAPON_TRAVEL = 6; const int WEAPON_FRAME = 7; const int WEAPON_LEVEL = 8; const int BRang_Speed = 3; ///////////////////////////////////////////////////// // Active Weapon Types. ///////////////////////////////////////////////////// const int WEAPON_BRANG = 1; ///////////////////////////////////////////////////// // Effect Tiles. ///////////////////////////////////////////////////// const int EFFECT_SHALLOW_WATER = 1480; const int EFFECT_TALLGRASS = 1482; const int EFFECT_DROWN = 97; const int EFFECT_FALL = 301; ///////////////////////////////////////////////////// // Additional Combo Flags. ///////////////////////////////////////////////////// const int COMBO_FLAG_LEDGE = 98; const int COMBO_FLAG_BRIDGE_ENTER = 99; const int COMBO_FLAG_BRIDGE = 100; const int COMBO_FLAG_BRIDGE_EDGE = 101; ///////////////////////////////////////////////////// // Additional Combo Types & other combo data. ///////////////////////////////////////////////////// const int INVIS_COMBO = 13256; const int CT_SIGN = 142;//CT_SCRIPT1 const int CT_URCHIN = 143;//CT_SCRIPT2 const int CT_BOMB_FLOWER = 144;//CT_SCRIPT3 const int CT_PIT_FALL = 145;//CT_SCRIPT4 const int CT_FLOOR_SWITCH = 146; //CT_SCRIPT5 const int CT_PLATFORM = 15;//CT_CVDOWN ///////////////////////////////////////////////////// // Default movement speeds and tile offsets. ///////////////////////////////////////////////////// const int MoveSpeed_Straight = 1.25; const int MoveSpeed_Diagonal = 0.90; ///////////////////////////////////////////////////// // Misc. constants. ///////////////////////////////////////////////////// const int TooHeavyString = 9; const int reGrowFrame_1 = 1312; const int reGrowFrame_2 = 1313; const int REGROW_DATA = 0; const int REGROW_COMBO = 16; const int REGROW_TIMER = 32; const int reGrowMaxCnt = 16; int reGrowCnt = 0; int reGrowData[48]; int SecretCount_Req[16]; int SecretCount_Cur[16]; ///////////////////////////////////////////////////// // Layer save/rewind stuff. ///////////////////////////////////////////////////// int layerChangeCnt = 0; int layerChange_Map[256]; int layerChange_Scr[256]; int layerChange_Pos[256]; int layerChange_Combo[256]; void DisableControls() { Link->InputStart = false; Link->InputMap = false; Link->InputUp = false; Link->InputDown = false; Link->InputLeft = false; Link->InputRight = false; Link->InputA = false; Link->InputB = false; Link->InputL = false; Link->InputR = false; Link->InputEx1 = false; Link->InputEx2 = false; Link->InputEx3 = false; Link->InputEx4 = false; } ///////////////////////////////////////////////////// // Initialize scrolling screen data. ///////////////////////////////////////////////////// void InitializeScreen() { Link_vX = 120; Link_vY = 80; Link_vZ = 0; camPos_X = 0; camPos_Y = 0; Link_Force[0] = 0; Link_Force[1] = 0; layerChangeCnt = 0; //initialize secret flags for (int i=0; i<16; i++) { SecretCount_Req[i] = 0; SecretCount_Cur[i] = 0; } int findWarp = -1; if ( Link->Misc[ LMISC_WARP_FLAGS ] == 1 ) //Type 1: the position is explicitly set. { Link->Misc[ LMISC_WARP_FLAGS ] = 0; Link_vX = Link->Misc[ LMISC_WARP_X ]; Link_vY = Link->Misc[ LMISC_WARP_Y ]; } else if ( Link->Misc[ LMISC_WARP_FLAGS ] == 2 ) //Type 2: the position is found, given the incoming warp. { Link->Misc[ LMISC_WARP_FLAGS ] = 0; findWarp = Link->Misc[ LMISC_WARP_X ]; } Link->Misc[ LMISC_HIT_FRAME ] = 0; Link->Misc[ LMISC_STATE ] = LSTATE_NORMAL; Link->Misc[ LMISC_HELD_ITEM ] = 0; Link->Misc[ LMISC_DAMAGE_ANIM ] = 0; Link->Misc[ LMSIC_KNOCKBACKX ] = 0; Link->Misc[ LMSIC_KNOCKBACKY ] = 0; Link->Misc[ LMSIC_KNOCKBACK_FRAMES ] = 0; //Create an FFC to store weapon Data. ffc wpnHolder = Screen->LoadFFC(1); wpnHolder->Data = INVIS_COMBO; wpnHolder->Misc[WEAPON_ACTIVE] = 0; reGrowCnt = 0; Items_Reset(); //Load Enemy Spawns & setup bridges. Enemy_Count = 0; Proj_Count = 0; Platform_Count = 0; int map = ScreenData[1]; int scrBase = ScreenData[2]; int scrCountX = ScreenData[3]; int scrCountY = ScreenData[4]; int layer1_map = ScreenData[5]; int layer1_scr = ScreenData[6]; int layer3_map = ScreenData[9]; int layer3_scr = ScreenData[10]; int layer4_map = ScreenData[11]; int layer4_scr = ScreenData[12]; int scrY = scrBase; for (int sy=0; syGetComboFlag(map, scr, p); if ( f >= CF_ENEMY0 && f <= CF_ENEMY9 ) { int eType = f-CF_ENEMY0; int iy = ((p>>4)<<4) + sy*176; int ix = ((p&15)<<4) + sx*256; Enemy_Spawn(ix, iy, ENEMY_OCTOROK + eType); } //handle platforms. int t = Game->GetComboType(layer4_map, layer4_scr+(sy<<4)+sx, p); if ( t == CT_PLATFORM ) { int ix = (p&15); int iy = (p>>4); Platform_ReadPath(sx, sy, ix, iy); continue; } //handle secret flags. int f4 = Game->GetComboFlag(layer4_map, layer4_scr+(sy<<4)+sx, p); if ( f4 >= CF_SECRETS01 && f4 <= CF_SECRETS16 ) { int f4_i = Game->GetComboInherentFlag(layer4_map, layer4_scr+(sy<<4)+sx, p); //only include switch types for requirements. if ( t == CT_FLOOR_SWITCH || f4_i == CF_STRIKE ) SecretCount_Req[f4-CF_SECRETS01]++; } //check for warps? if ( findWarp > -1 ) { int t = Game->GetComboType(map, scr, p); if ( t == CT_CAVE || t == CT_CAVE2 || t == CT_CAVEB || t == CT_CAVE2B || t == CT_CAVEC || t == CT_CAVE2C || t == CT_CAVED || t == CT_CAVE2D) { int f = Game->GetComboFlag(map, scr, p); int warp = f-CF_SECRETS01; if ( warp >= 0 && warp == findWarp ) { Link_vX = ((p&15)<<4) + sx*256; Link_vY = ((p>>4)<<4) + sy*176; if ( t == CT_CAVE || t == CT_CAVE2 ) { Link_vY += 16; if ( Game->GetComboSolid(map, scr, p) != 0 ) Link_vX += 8; Link->Dir = DIR_DOWN; } else if ( t == CT_CAVEB || t == CT_CAVE2B ) { Link_vY -= 16; if ( Game->GetComboSolid(map, scr, p) != 0 ) Link_vX += 8; Link->Dir = DIR_UP; } else if ( t == CT_CAVEC || t == CT_CAVE2C ) { Link_vX -= 16; Link->Dir = DIR_LEFT; } else { Link_vX += 16; Link->Dir = DIR_RIGHT; } findWarp = -1; } } } //check for bridge pieces on layer 0 (since we're here anyway...) if ( layer3_map > -1 ) { f = Game->GetComboInherentFlag(map, scr, p); if ( f == COMBO_FLAG_BRIDGE || f == COMBO_FLAG_BRIDGE_EDGE ) { Game->SetComboData(layer3_map, layer3_scr+(sy<<4)+sx, p, Game->GetComboData(map, scr, p)); Game->SetComboCSet(layer3_map, layer3_scr+(sy<<4)+sx, p, 2); } //check layer 1 as well... if ( layer1_map > -1 ) { f = Game->GetComboInherentFlag(layer1_map, layer1_scr+(sy<<4)+sx, p); if ( f == COMBO_FLAG_BRIDGE || f == COMBO_FLAG_BRIDGE_EDGE ) { int c1 = Game->GetComboData(layer1_map, layer1_scr+(sy<<4)+sx, p); Game->SetComboData(layer3_map, layer3_scr+(sy<<4)+sx, p, c1); Game->SetComboCSet(layer3_map, layer3_scr+(sy<<4)+sx, p, 2); } } } } } scrY += 16; } //Check for spawns that use inherent flags (on layer 4). map = ScreenData[11]; scrBase = ScreenData[12]; scrY = scrBase; for (int sy=0; syGetComboInherentFlag(map, scr, p); if ( f >= CF_ENEMY0 && f <= CF_ENEMY9 ) { int eType = f-CF_ENEMY0; int iy = ((p>>4)<<4) + sy*176; int ix = ((p&15)<<4) + sx*256; Enemy_Spawn(ix, iy, ENEMY_OCTOROK + eType); //now clear out the combo itself. AddLayerChange(map, scr, p, Game->GetComboData(map, scr, p)); Game->SetComboData(map, scr, p, 0); } } } scrY += 16; } Link->Misc[ LMISC_PREV_X ] = Link_vX; Link->Misc[ LMISC_PREV_Y ] = Link_vY; } void AddLayerChange(int map, int scr, int pos, int combo) { if ( layerChangeCnt < 256 ) { layerChange_Map[layerChangeCnt] = map; layerChange_Scr[layerChangeCnt] = scr; layerChange_Pos[layerChangeCnt] = pos; layerChange_Combo[layerChangeCnt] = combo; layerChangeCnt++; } } void RewindLayerChanges() { //rewind in reverse order so the original change is last. //this is in case multiple changes happen to the same spot. for (int i=layerChangeCnt-1; i>=0; i--) { Game->SetComboData(layerChange_Map[i], layerChange_Scr[i], layerChange_Pos[i], layerChange_Combo[i]); Game->SetComboCSet(layerChange_Map[i], layerChange_Scr[i], layerChange_Pos[i], 2); } Platform_Rewind(); } ///////////////////////////////////////////////////// // A single loop of the Scrolling Engine. ///////////////////////////////////////////////////// void ScrollingLoop() { //Get the FFC once and pass the data around. ffc wpnHolder = Screen->LoadFFC(1); //Update if ( EngineFlags&EFLAG_OPENING_WIPE ) { //Update the camera and sprite position UpdateCamera(); } else { //update platforms first since they can move Link around. Platform_Update(); int LinkState = Link->Misc[ LMISC_STATE ]; if ( LinkState == LSTATE_NORMAL || LinkState == LSTATE_HOLDING_ITEM || LinkState == LSTATE_PUSHING || LinkState == LSTATE_BLOCKING ) UpdateLink( wpnHolder ); else if (LinkState == LSTATE_LEDGE_JUMP) UpdateLink_LedgeJump(); else if (LinkState == LSTATE_THROW_WPN ) UpdateLink_ThrowWeapon(); else if (LinkState == LSTATE_ATTACKING) UpdateLink_Attack(); else if (LinkState == LSTATE_ATTACK_HOLD) UpdateLink_AttackHold(); else if (LinkState == LSTATE_SPIN_ATTACK) UpdateLink_SpinAttack(); else if (LinkState == LSTATE_DROWN || LinkState == LSTATE_FALL) UpdateLink_Drown(); if ( wpnHolder->Misc[WEAPON_ACTIVE] > 0 ) { UpdateScreenWeapon( wpnHolder ); } Link_vZ += Link->Misc[ LMISC_ZVEL ]; if ( Link_vZ > 0 && Link->Misc[LMISC_ZVEL] > -TerminalVelocity ) { Link->Misc[ LMISC_ZVEL ] -= Gravity_Acceleration; } else if ( Link_vZ <= 0 ) { Link->Misc[ LMISC_ZVEL ] = 0; Link_vZ = 0; CheckForWarps(); CheckSpecialTiles(); } Items_Update(); Enemy_Update(); Proj_Update(); if ( Item_BombTimer > 0 ) { Item_BombTimer--; //Is it time to blow up? if ( Item_BombTimer == 0 ) { if ( Link->Misc[ LMISC_HELD_ITEM_TYPE ] == CT_BOMB_FLOWER ) { int level = 0; if ( LinkFlags&LFLAG_ON_BRIDGE ) level = 1; Link->Misc[ LMISC_HELD_ITEM ] = 0; Link->Misc[ LMISC_HELD_ITEM_TYPE ] = 0; Link->Misc[ LMISC_STATE ] = LSTATE_NORMAL; AddPoof(Link_vX+8, Link_vY-4, Explosion, level); } } } //regrow plants. int end = reGrowCnt-1; for (int i=end; i>=0; i--) { reGrowData[i+REGROW_TIMER]--; if ( reGrowData[i+REGROW_TIMER] == 16 ) { int scr = reGrowData[i+REGROW_DATA]&127; int pos = reGrowData[i+REGROW_DATA]>>7; int map = ScreenData[11]; Game->SetComboData(map, scr, pos, reGrowFrame_1); } else if ( reGrowData[i+REGROW_TIMER] == 8 ) { int scr = reGrowData[i+REGROW_DATA]&127; int pos = reGrowData[i+REGROW_DATA]>>7; int map = ScreenData[11]; Game->SetComboData(map, scr, pos, reGrowFrame_2); } else if ( reGrowData[i+REGROW_TIMER] == 0 ) { int scr = reGrowData[i+REGROW_DATA]&127; int pos = reGrowData[i+REGROW_DATA]>>7; int map = ScreenData[11]; Game->SetComboData(map, scr, pos, reGrowData[i+REGROW_COMBO]); } if ( reGrowData[i+REGROW_TIMER] <= 0 ) { RemoveReGrowData(i); } } Link_Force[0] = 0; Link_Force[1] = 0; } int offset = 0; ScrollingEngine_Draw(offset); if ( EngineFlags&EFLAG_OPENING_WIPE ) { if ( Link->Misc[ LMISC_TIMER ] == 126 ) { Game->PlaySound( SFX_FADE_IN ); } if ( Link->Misc[ LMISC_TIMER ] > 0 ) { int x0 = Link->Misc[ LMISC_TIMER ]; int x1 = 256-x0; Screen->Rectangle(6, 0, -64, x0, 176, 21, 1.0, 0, 0, 0, true, 128); Screen->Rectangle(6, x1, -64, 256, 176, 21, 1.0, 0, 0, 0, true, 128); Link->Misc[ LMISC_TIMER ]-=2; } else { EngineFlags &= ~EFLAG_OPENING_WIPE; Link->Misc[ LMISC_TIMER ] = 0; } } else if ( Link->PressStart ) //This must be done last to avoid flicker. { StartActiveSubscreen(); } } void ScrollingEngine_Draw(int offset) { ffc wpnHolder = Screen->LoadFFC(1); //Draw DrawScreen(offset); Items_Draw(offset); Enemy_Draw(offset); Proj_Draw(offset); Platform_Draw(offset); DrawLink(offset, wpnHolder); Items_DrawExternal(offset); if ( !(EngineFlags&EFLAG_ACTIVE_SUBSCREEN) ) { DrawPassiveSubscreen(offset); DrawHUD(offset); } } void Link_AddForce(int fx, int fy) { Link_Force[0] = fx; Link_Force[1] = fy; } bool Link_Damage(int dmg, int dx, int dy, bool bProj, bool bUnblockable) { bool blocked = false; if ( Link->Misc[ LMISC_DAMAGE_ANIM ] == 0 ) { if ( Link->Misc[ LMISC_HELD_ITEM ] > 0 ) { Throw(); } //does Link block the attack? if ( Link->Misc[ LMISC_STATE ] == LSTATE_BLOCKING ) { if ( bUnblockable == false ) { //is the damage coming from in front? float dirX=0; float dirY=0; if ( Link->Dir == DIR_LEFT ) dirX = -1; else if ( Link->Dir == DIR_RIGHT ) dirX = 1; else if ( Link->Dir == DIR_UP ) dirY = -1; else if ( Link->Dir == DIR_DOWN ) dirY = 1; float m = 1.0 / Sqrt(dx*dx + dy*dy); float dmgDirX = dx*m; float dmgDirY = dy*m; if ( dmgDirX*dirX + dmgDirY*dirY < -0.72 ) { blocked = true; } } } else { //If Link is attacking, this cancels the attack. Link->Misc[ LMISC_STATE ] = LSTATE_NORMAL; Link->Misc[ LMISC_HIT_FRAME ] = 0; } if ( blocked ) { Game->PlaySound( SFX_CLINK ); if ( bProj == false ) { if ( Abs(dx) > 0.01 || Abs(dy) > 0.01 ) { float m = Sqrt( dx*dx + dy*dy ); dx = dx/m; dy = dy/m; Link->Misc[ LMSIC_KNOCKBACKX ] = dx*2; Link->Misc[ LMSIC_KNOCKBACKY ] = dy*2; } else { Link->Misc[ LMSIC_KNOCKBACKX ] = 2; } Link->Misc[ LMSIC_KNOCKBACK_FRAMES ] = 12; } } else { Link->HP -= dmg; if ( Link->HP <= 0 ) { //Link->HP = 1; Game->End(); } else { Game->PlaySound( SFX_OUCH ); Link->Misc[ LMISC_DAMAGE_ANIM ] = 30; if ( Abs(dx) > 0.01 || Abs(dy) > 0.01 ) { float m = Sqrt( dx*dx + dy*dy ); dx = dx/m; dy = dy/m; Link->Misc[ LMSIC_KNOCKBACKX ] = dx*2; Link->Misc[ LMSIC_KNOCKBACKY ] = dy*2; } else { Link->Misc[ LMSIC_KNOCKBACKX ] = 2; } Link->Misc[ LMSIC_KNOCKBACK_FRAMES ] = 6; } } } return blocked; } void ActivateItem(int itmIdx, ffc wpnHolder) { if ( itmIdx == ITEM_SHIELD && Link->Misc[LMISC_STATE] != LSTATE_BLOCKING ) { Game->PlaySound( SFX_LINK_SHIELD ); Link->Misc[LMISC_STATE] = LSTATE_BLOCKING; } else if ( itmIdx == ITEM_BRANG && wpnHolder->Misc[WEAPON_ACTIVE] == 0 ) { int dx = 0; int dy = 0; //First try to use the actual input if ( Link->InputLeft ) dx = -1; else if ( Link->InputRight ) dx = 1; if ( Link->InputUp ) dy = -1; else if ( Link->InputDown ) dy = 1; //If the player is not pressing any direction, then use the facing direction. if ( dx == 0 && dy == 0 ) { if ( Link->Dir == DIR_LEFT ) dx = -1; else if ( Link->Dir == DIR_RIGHT ) dx = 1; else if ( Link->Dir == DIR_UP ) dy = -1; else if ( Link->Dir == DIR_DOWN ) dy = 1; } else { //otherwise normalize the direction vector. float m = 1.0 / Sqrt( dx*dx + dy*dy ); dx *= m; dy *= m; } wpnHolder->Misc[WEAPON_ACTIVE] = WEAPON_BRANG; wpnHolder->Misc[WEAPON_X] = Link_vX; wpnHolder->Misc[WEAPON_Y] = Link_vY; wpnHolder->Misc[WEAPON_DIR_X] = dx; wpnHolder->Misc[WEAPON_DIR_Y] = dy; wpnHolder->Misc[WEAPON_SPEED] = BRang_Speed * 0.25; wpnHolder->Misc[WEAPON_TRAVEL] = 0; wpnHolder->Misc[WEAPON_FRAME] = 0; int level = 0; if ( LinkFlags&LFLAG_ON_BRIDGE ) level = 1; wpnHolder->Misc[WEAPON_LEVEL] = level; Link->Misc[ LMISC_STATE ] = LSTATE_THROW_WPN; Link->Misc[ LMISC_TIMER ] = 16; } else if ( itmIdx == ITEM_BRACELET ) { Link_Action(true); } } void ActivateSecret(int secretFlag) { int sIdx = secretFlag - CF_SECRETS01; if ( SecretCount_Cur[sIdx] < SecretCount_Req[sIdx] ) SecretCount_Cur[sIdx]++; if ( SecretCount_Cur[sIdx] == SecretCount_Req[sIdx] ) { Platform_TriggetSecret(secretFlag); Trace(-1); Trace(sIdx); } } void DeactivateSecret(int secretFlag) { int sIdx = secretFlag - CF_SECRETS01; if ( SecretCount_Cur[sIdx] > 0 ) SecretCount_Cur[sIdx]--; } void UpdateScreenWeapon(ffc wpnHolder) { if ( wpnHolder->Misc[WEAPON_ACTIVE] == WEAPON_BRANG ) { int x; int y; bool bMoved = false; if ( wpnHolder->Misc[WEAPON_TRAVEL] >= 0 ) //going out { float spd = wpnHolder->Misc[WEAPON_SPEED]; int newX = wpnHolder->Misc[WEAPON_X] + wpnHolder->Misc[WEAPON_DIR_X]*spd; int newY = wpnHolder->Misc[WEAPON_Y] + wpnHolder->Misc[WEAPON_DIR_Y]*spd; wpnHolder->Misc[WEAPON_SPEED] += 0.25; if ( wpnHolder->Misc[WEAPON_SPEED] > BRang_Speed ) wpnHolder->Misc[WEAPON_SPEED] = BRang_Speed; wpnHolder->Misc[WEAPON_TRAVEL] += wpnHolder->Misc[WEAPON_SPEED]; x = newX; y = newY; //Check for collisions. bool bCheckA = ComboSolid(x+ 2, y+ 2, wpnHolder->Misc[WEAPON_LEVEL]); bool bCheckB = ComboSolid(x+14, y+ 2, wpnHolder->Misc[WEAPON_LEVEL]); bool bCheckC = ComboSolid(x+ 2, y+14, wpnHolder->Misc[WEAPON_LEVEL]); bool bCheckD = ComboSolid(x+14, y+14, wpnHolder->Misc[WEAPON_LEVEL]); int enemyID = -1; int itemID = -1; if ( wpnHolder->Misc[WEAPON_LEVEL] == 0 ) { enemyID = Weapon_EnemyCollision(x, y); //try to stun the enemy if ( enemyID > -1 ) Enemy_Stun(enemyID); } if ( enemyID < 0 ) { itemID = Weapon_ItemCollision(x, y); Items_DisableMovement(itemID); } if ( bCheckA || bCheckB || bCheckC || bCheckD || enemyID > -1 || itemID > -1 ) { if ( enemyID == -1 ) //If this doesn't stun the enemy, go ahead and play the "Clink" Game->PlaySound( SFX_CLINK ); wpnHolder->Misc[WEAPON_TRAVEL] = -0.5; //spawn a special "poof" if ( itemID < 0 ) { x += wpnHolder->Misc[WEAPON_DIR_X]*8; y += wpnHolder->Misc[WEAPON_DIR_Y]*8; AddPoof(x, y, Clink_Poof, wpnHolder->Misc[WEAPON_LEVEL]); } if ( enemyID < 0 && itemID < 0 ) { //ok, does this activate a switch? int chk_x; int chk_y; if ( bCheckA ) { chk_x = x+2; chk_y = y+2; } else if ( bCheckB ) { chk_x = x+14; chk_y = y+2; } else if ( bCheckC ) { chk_x = x+2; chk_y = y+14; } else { chk_x = x+14; chk_y = y+14; } int f = ComboFlag(4, chk_x, chk_y, 1); if ( f == CF_STRIKE ) { //Get the secret flag and activate it. int secretFlag = ComboFlag(4, chk_x, chk_y, 2); ActivateSecret( secretFlag ); //Change the combo and play a sound effect. int c = ComboData(4, chk_x, chk_y); SetComboData(4, chk_x, chk_y, c+1); Game->PlaySound( SFX_FLOOR_SWITCH ); } } } else { wpnHolder->Misc[WEAPON_X] = newX; wpnHolder->Misc[WEAPON_Y] = newY; if ( wpnHolder->Misc[WEAPON_TRAVEL] > 80 ) wpnHolder->Misc[WEAPON_TRAVEL] = -0.5; bMoved = true; } } else //return { wpnHolder->Misc[WEAPON_SPEED] += 0.25; if ( wpnHolder->Misc[WEAPON_SPEED] > BRang_Speed ) wpnHolder->Misc[WEAPON_SPEED] = BRang_Speed; int dx = Link_vX+8 - (wpnHolder->Misc[WEAPON_X]+8); int dy = Link_vY+8 - (wpnHolder->Misc[WEAPON_Y]+8); if ( Abs(dx) < 8 && Abs(dy) < 8 ) { wpnHolder->Misc[WEAPON_ACTIVE] = 0; } else { //otherwise normalize the direction vector, scale by WEAPON_SPEED float m = wpnHolder->Misc[WEAPON_SPEED] / Sqrt( dx*dx + dy*dy ); dx *= m; dy *= m; wpnHolder->Misc[WEAPON_X] += dx; wpnHolder->Misc[WEAPON_Y] += dy; } //stun enemies on the way back too... int enemyID = -1; if ( wpnHolder->Misc[WEAPON_LEVEL] == 0 ) { enemyID = Weapon_EnemyCollision(wpnHolder->Misc[WEAPON_X], wpnHolder->Misc[WEAPON_Y]); //try to stun the enemy if ( enemyID > -1 ) Enemy_Stun(enemyID); } bMoved = true; } if ( (wpnHolder->Misc[WEAPON_FRAME]%12) == 0 ) { Game->PlaySound( SFX_BRANG ); } wpnHolder->Misc[WEAPON_FRAME]++; if ( bMoved ) { x = wpnHolder->Misc[WEAPON_X]; y = wpnHolder->Misc[WEAPON_Y]; int prevLevel = wpnHolder->Misc[WEAPON_LEVEL]; wpnHolder->Misc[WEAPON_LEVEL] = CheckObjectLevel(x, y, prevLevel); } if ( Item_External > -1 ) { Items_SetPos(Item_External, wpnHolder->Misc[WEAPON_X], wpnHolder->Misc[WEAPON_Y]); } } else { Item_External = -1; } } void DrawScreenWeapon(ffc wpnHolder) { if ( wpnHolder->Misc[WEAPON_ACTIVE] == WEAPON_BRANG ) { int layer = 2 + wpnHolder->Misc[WEAPON_LEVEL]; int frame = (wpnHolder->Misc[WEAPON_FRAME]>>1)&7; int tile = 1563 + frame; Screen->FastTile(layer, wpnHolder->Misc[WEAPON_X]-camPos_X, wpnHolder->Misc[WEAPON_Y]-camPos_Y, tile, 2, 128); } } void CheckForWarps() { if ( Link->Misc[LMISC_STATE] != LSTATE_DROWN && Link->Misc[LMISC_STATE] != LSTATE_FALL ) { int t = ComboType(0, Link_vX+8, Link_vY+8); if ( t == CT_WATER && !(LinkFlags&LFLAG_ON_PLATFORM) ) { Link->Misc[LMISC_STATE] = LSTATE_DROWN; Link->Misc[LMISC_FRAME] = 0; Game->PlaySound( SFX_DIVE ); } else if ( t == CT_PIT_FALL && !(LinkFlags&LFLAG_ON_PLATFORM) ) { Link->Misc[LMISC_STATE] = LSTATE_FALL; Link->Misc[LMISC_FRAME] = 0; Game->PlaySound( SFX_FALL ); } else if ( (Abs(Link_vX)%32 < 2 || Abs(Link_vY)%32 < 2) && !(LinkFlags&LFLAG_ON_PLATFORM) ) { Link->Misc[ LMISC_PREV_X ] = Link_vX; Link->Misc[ LMISC_PREV_Y ] = Link_vY; } //Is this a warp? if ( t == CT_CAVE || t == CT_CAVE2 || t == CT_CAVEB || t == CT_CAVE2B || t == CT_CAVEC || t == CT_CAVE2C || t == CT_CAVED || t == CT_CAVE2D ) { //if so, which warp? int f = ComboFlag(0, Link_vX+8, Link_vY+8, 0); int warp = f - CF_SECRETS01; if ( warp >= 0 && warp < 16 ) { int WarpDMap = WarpData[ warp*2 + 0 ]; int WarpScr = WarpData[ warp*2 + 1 ]; Link->Misc[ LMISC_WARP_FLAGS ] = 2; Link->Misc[ LMISC_WARP_X ] = warp; //Fade out... int FADE_TIME = 20; Screen->Lit = 0; for (int delay=0; delayWarp(WarpDMap, WarpScr); } } } } void UpdateLink(ffc wpnHolder) { //Handle movement inputs. Link->Misc[LMISC_FLAGS] = 0; int nMoveX = 0; int nMoveY = 0; int dirH = -1; int dirV = -1; bool bKnockback = false; if ( Link->Misc[LMSIC_KNOCKBACK_FRAMES] > 0 ) { bKnockback = true; nMoveX = Link->Misc[LMSIC_KNOCKBACKX]; nMoveY = Link->Misc[LMSIC_KNOCKBACKY]; Link->Misc[LMSIC_KNOCKBACK_FRAMES]--; if ( Link->Misc[LMSIC_KNOCKBACK_FRAMES] == 0 ) { Link->Misc[LMSIC_KNOCKBACKX] = 0; Link->Misc[LMSIC_KNOCKBACKY] = 0; } } else { if ( Link->InputLeft ) { dirH = DIR_LEFT; nMoveX = -1; Link->Misc[LMISC_FLAGS] = 1; } else if ( Link->InputRight ) { dirH = DIR_RIGHT; nMoveX = 1; Link->Misc[LMISC_FLAGS] = 1; } if ( Link->InputUp ) { dirV = DIR_UP; nMoveY = -1; Link->Misc[LMISC_FLAGS] = 1; } else if ( Link->InputDown ) { dirV = DIR_DOWN; nMoveY = 1; Link->Misc[LMISC_FLAGS] = 1; } //Handle direction changes. //This is setup to handle diagonal movement //based on the first direction held. if ( dirH > -1 && dirV > -1 ) { if ( dirH != Link->Dir && dirV != Link->Dir ) { Link->Dir = dirH; } } else if ( dirH > -1 ) { Link->Dir = dirH; } else if ( dirV > -1 ) { Link->Dir = dirV; } } //Handle movement and collision. if ( nMoveX != 0 || nMoveY != 0 || Link_Force[0] != 0 || Link_Force[1] != 0 ) { int speed; if ( bKnockback ) speed = 1; else if ( nMoveX == 0 || nMoveY == 0 ) speed = MoveSpeed_Straight; else speed = MoveSpeed_Diagonal; if ( Link->Misc[LMISC_STATE] == LSTATE_BLOCKING ) { speed = Enemy_Push(speed, nMoveX, nMoveY); } ///////////// //collision// ///////////// int move[2]; move[0] = nMoveX*speed + Link_Force[0]; move[1] = nMoveY*speed + Link_Force[1]; Link_PrevFlags = LinkFlags; LinkFlags &= ~LFLAG_ON_BRIDGE; HandleCollision(move, true); Link_vX += move[0]; Link_vY += move[1]; if ( move[0] == 0 && move[1] == 0 && Link->Misc[ LMISC_STATE ] == LSTATE_NORMAL ) { //we're pushing against a wall or something. Link->Misc[ LMISC_STATE ] = LSTATE_PUSHING; } else if ( (move[0] != 0 || move[1] != 0) && Link->Misc[ LMISC_STATE ] == LSTATE_PUSHING ) { if ( (Link->InputL && Link_Items[0] == ITEM_SHIELD) || (Link->InputR && Link_Items[1] == ITEM_SHIELD) ) Link->Misc[LMISC_STATE] = LSTATE_BLOCKING; else Link->Misc[LMISC_STATE] = LSTATE_NORMAL; } } else { Link->Misc[ LMISC_TIMER ] = 0; if ( Link->Misc[LMISC_STATE] == LSTATE_PUSHING ) { if ( (Link->InputL && Link_Items[0] == ITEM_SHIELD) || (Link->InputR && Link_Items[1] == ITEM_SHIELD) ) Link->Misc[LMISC_STATE] = LSTATE_BLOCKING; else Link->Misc[LMISC_STATE] = LSTATE_NORMAL; } } //If A is pressed, try doing some action (context sensitive). if ( Link->PressA && (dirH==-1 || dirV==-1) && bKnockback == false ) { Link_Action(false); } else if ( Link->PressB && Link->Misc[ LMISC_HELD_ITEM ] == 0 && bKnockback == false ) { Link_Attack(); } else { if ( Link->PressL && Link_Items[0] >= 0 ) ActivateItem(Link_Items[0], wpnHolder); if ( Link->PressR && Link_Items[1] >= 0 ) ActivateItem(Link_Items[1], wpnHolder); } //////////////////////////////////////////////////////// //Stop blocking if the Shield button is no longer held. //////////////////////////////////////////////////////// //First figure out which button to check. bool btnToCheck = false; if ( Link_Items[0] == ITEM_SHIELD ) btnToCheck = Link->InputL; else if ( Link_Items[1] == ITEM_SHIELD ) btnToCheck = Link->InputR; //Then turn off blocking if the input is no longer held. if ( Link->Misc[LMISC_STATE] == LSTATE_BLOCKING && btnToCheck == false ) Link->Misc[LMISC_STATE] = LSTATE_NORMAL; //Update the camera and sprite position UpdateCamera(); //update Link animation. if ( bKnockback == false ) { if ( Link->Misc[LMISC_FLAGS]&1 ) { Link->Misc[ LMISC_FRAME ] = (Link->Misc[ LMISC_FRAME ]+1)%12; } else { Link->Misc[ LMISC_FRAME ] = 0; } } if ( Link->Misc[LMISC_DAMAGE_ANIM] > 0 ) Link->Misc[LMISC_DAMAGE_ANIM]--; } void UpdateLink_LedgeJump() { if ( Link_vZ <= 0 ) { if ( Link->InputL && Link_Items[0] == ITEM_SHIELD ) { Link->Misc[LMISC_STATE] = LSTATE_BLOCKING; } else { Link->Misc[ LMISC_STATE ] = LSTATE_NORMAL; } } //Update the camera and sprite position UpdateCamera(); //update Link animation. Link->Misc[ LMISC_FRAME ] = (Link->Misc[ LMISC_FRAME ]+1)%12; } void UpdateLink_Drown() { //Update the camera and sprite position UpdateCamera(); //update Link animation. Link->Misc[ LMISC_FRAME ]++; if ( Link->Misc[ LMISC_FRAME ] > 30 ) { Link->Misc[LMISC_STATE] = LSTATE_NORMAL; Link_vX = Link->Misc[ LMISC_PREV_X ]; Link_vY = Link->Misc[ LMISC_PREV_Y ]; Link->HP -= 4; if ( Link->HP <= 0 ) { //Link->HP = 1; Game->End(); } else { Game->PlaySound( SFX_OUCH ); Link->Misc[ LMISC_DAMAGE_ANIM ] = 30; } } //Update the camera and sprite position UpdateCamera(); } void UpdateLink_ThrowWeapon() { //Update the camera and sprite position UpdateCamera(); //update Link animation. Link->Misc[ LMISC_FRAME ] = 0; Link->Misc[ LMISC_TIMER ]--; if ( Link->Misc[ LMISC_TIMER ] <= 0 ) { if ( (Link->InputL && Link_Items[0] == ITEM_SHIELD) || (Link->InputR && Link_Items[1] == ITEM_SHIELD) ) { Link->Misc[LMISC_STATE] = LSTATE_BLOCKING; } else { Link->Misc[ LMISC_STATE ] = LSTATE_NORMAL; } } } void UpdateLink_Attack() { //Update the camera and sprite position UpdateCamera(); //update Link animation. Link->Misc[ LMISC_FRAME ]++; if ( Link->Misc[ LMISC_FRAME ] > 15 ) { //go back to normal. if ( Link->InputB ) { Link->Misc[ LMISC_STATE ] = LSTATE_ATTACK_HOLD; } else { if ( Link->InputL && Link_Items[0] == ITEM_SHIELD ) { Link->Misc[LMISC_STATE] = LSTATE_BLOCKING; } else { Link->Misc[ LMISC_STATE ] = LSTATE_NORMAL; } } Link->Misc[ LMISC_FRAME ] = 0; Link->Misc[ LMISC_TIMER ] = 0; } } void UpdateLink_AttackHold() { Link->Misc[LMISC_DAMAGE_ANIM] = 0; if ( Link->Misc[ LMISC_TIMER ] == 60 ) { Game->PlaySound( SFX_CHARGE1 ); } if ( Link->Misc[ LMISC_HIT_FRAME ] > 0 ) { Link->Misc[ LMISC_HIT_FRAME ]--; if ( Link->Misc[ LMISC_HIT_FRAME ] == 0 ) { //ok we're done attacking... Link->Misc[ LMISC_STATE ] = LSTATE_NORMAL; } } else if ( !Link->InputB ) { if ( Link->Misc[ LMISC_TIMER ] > 60 && Link->MP >= SPIN_ATTACK_COST ) { Link->Misc[ LMISC_STATE ] = LSTATE_SPIN_ATTACK; Link->Misc[ LMISC_TIMER ] = Link->Dir; Link->Misc[ LMISC_MISC ] = Link->Dir; Link->MP -= SPIN_ATTACK_COST; Game->PlaySound( SFX_SPINATTACK ); } else { if ( Link->Misc[ LMISC_TIMER ] > 60 && Link->MP < SPIN_ATTACK_COST ) Game->PlaySound( SFX_ERROR ); if ( Link->InputL && Link_Items[0] == ITEM_SHIELD ) { Link->Misc[LMISC_STATE] = LSTATE_BLOCKING; } else { Link->Misc[ LMISC_STATE ] = LSTATE_NORMAL; } Link->Misc[ LMISC_TIMER ] = 0; Link->Misc[ LMISC_MISC ] = 0; } Link->Misc[ LMISC_FRAME ] = 0; } else { int nMoveX = 0; int nMoveY = 0; //allow movement. Link->Misc[LMISC_FLAGS] = 0; if ( Link->InputLeft ) { nMoveX = -1; Link->Misc[LMISC_FLAGS] = 1; } else if ( Link->InputRight ) { nMoveX = 1; Link->Misc[LMISC_FLAGS] = 1; } if ( Link->InputUp ) { nMoveY = -1; Link->Misc[LMISC_FLAGS] = 1; } else if ( Link->InputDown ) { nMoveY = 1; Link->Misc[LMISC_FLAGS] = 1; } //Handle movement and collision. if ( nMoveX != 0 || nMoveY != 0 || Link_Force[0] != 0 || Link_Force[1] != 0 ) { int speed = MoveSpeed_Diagonal; ///////////// //collision// ///////////// int move[2]; move[0] = nMoveX*speed + Link_Force[0]; move[1] = nMoveY*speed + Link_Force[1]; Link_PrevFlags = LinkFlags; LinkFlags &= ~LFLAG_ON_BRIDGE; HandleCollision(move, false); Link_vX += move[0]; Link_vY += move[1]; } if ( Link->Misc[LMISC_FLAGS]&1 ) { Link->Misc[ LMISC_FRAME ] = (Link->Misc[ LMISC_FRAME ]+1)%12; } else { Link->Misc[ LMISC_FRAME ] = 0; } Link->Misc[ LMISC_TIMER ]++; } //Update the camera and sprite position UpdateCamera(); } void UpdateLink_SpinAttack() { //OK, let's go through each quadrant until the last is reached. Link->Misc[ LMISC_FRAME ]++; if ( Link->Misc[ LMISC_FRAME ] > 34 ) { //all the way around, we're done. if ( Link->InputL && Link_Items[0] == ITEM_SHIELD ) { Link->Misc[LMISC_STATE] = LSTATE_BLOCKING; } else { Link->Misc[ LMISC_STATE ] = LSTATE_NORMAL; } Link->Misc[ LMISC_TIMER ] = 0; Link->Misc[ LMISC_MISC ] = 0; Link->Misc[ LMISC_FRAME ] = 0; } } void ApplyExplosionDamage(int x, int y, int r) { //Damage enemies. Enemy_ApplyDamage(32, x-r, x+r, y-r, y+r, false); //Damage Link? if ( x-r < Link_vX+16 && x+r > Link_vX && y-r < Link_vY+16 && y+r > Link_vY ) { //knock back the enemy if it hits Link's shield - no damage, just knockback. Link_Damage(32, (Link_vX+8) - x, (Link_vY+8) - y, false, true); } //Blow up stuff in the environment. int idx = 11; if ( ScreenData[idx+0] < 0 ) return; int map = ScreenData[idx+0]; int scrBase = ScreenData[idx+1]; int minTx = Max(x-r, 0)>>4; int minTy = Max(y-r, 0)>>4; int maxTx = Min(x+r, ScreenData[18]+255)>>4; int maxTy = Min(y+r, ScreenData[19]+175)>>4; for (int y=minTy; y<=maxTy; y++) { int scrY = Floor(y/11)*16 + scrBase; int cy = y%11; for (int x=minTx; x<=maxTx; x++) { int scrX = x>>4; int cx = x&15; int pos = (cy<<4) + cx; int scr = scrY+scrX; int t = Game->GetComboType(map, scr, pos); if (t == CT_BUSH || t == CT_BUSHC) { AddPoof(x<<4, y<<4, 1490, 0); AddLayerChange(map, scr, pos, Game->GetComboData(map, scr, pos)); Game->SetComboData(map, scr, pos, 0); DropRandomItem(15, x<<4, y<<4); } else if ( t == CT_SLASH ) { AddPoof(x<<4, y<<4, 1475, 0); AddLayerChange(map, scr, pos, Game->GetComboData(map, scr, pos)); Game->SetComboData(map, scr, pos, 0); DropRandomItem(15, x<<4, y<<4); } else if ( t == CT_TALLGRASS || t == CT_TALLGRASSC ) { AddPoof(x<<4, y<<4, 1495, 0); AddLayerChange(map, scr, pos, Game->GetComboData(map, scr, pos)); Game->SetComboData(map, scr, pos, 0); DropRandomItem(15, x<<4, y<<4); } else if ( t == CT_BOMB_FLOWER ) { AddReGrowData(scr, pos, Game->GetComboData(map, scr, pos)); AddPoof((x<<4)+8, (y<<4)+8, Explosion, 0); AddLayerChange(map, scr, pos, Game->GetComboData(map, scr, pos)); Game->SetComboData(map, scr, pos, 0); } else { int f = Game->GetComboInherentFlag(map, scr, pos); if ( f == CF_BOMB ) { AddPoof(x<<4, y<<4, 1515, 0); AddLayerChange(map, scr, pos, Game->GetComboData(map, scr, pos)); Game->SetComboData(map, scr, pos, 0); } } } } } void SetLinkAttackBounds( int x0, int y0 ) { bool bHitSomething = false; if ( Link->Misc[ LMISC_STATE ] == LSTATE_ATTACKING || Link->Misc[ LMISC_STATE ] == LSTATE_ATTACK_HOLD || Link->Misc[ LMISC_STATE ] == LSTATE_SPIN_ATTACK ) { bool bCanCutGrass = true; if ( Link->Misc[ LMISC_STATE ] == LSTATE_ATTACK_HOLD ) bCanCutGrass = false; x0 += 1; y0 += 1; int x1 = x0+13; int y1 = y0+13; //Hurt any enemies in range. if ( Enemy_ApplyDamage(8, x0, x1, y0, y1, true) ) bHitSomething = true; //Damage stuff in the environment. int cpos[2]; int combo = ComboData(4, x0, y0); if ( combo ) { int ctype = ComboType(4, x0, y0); if (ctype == CT_BUSH || ctype == CT_BUSHC) { GetComboPos(x0, y0, cpos); AddPoof(cpos[0], cpos[1], 1490, 0); SetComboData(4, x0, y0, 0); DropRandomItem(15, cpos[0], cpos[1]); Game->PlaySound(SFX_BUSH); bHitSomething = true; } else if ( ctype == CT_SLASH ) { GetComboPos(x0, y0, cpos); AddPoof(cpos[0], cpos[1], 1475, 0); SetComboData(4, x0, y0, 0); DropRandomItem(15, cpos[0], cpos[1]); Game->PlaySound( SFX_SHATTER ); bHitSomething = true; } else if ( (ctype == CT_TALLGRASS || ctype == CT_TALLGRASSC) && bCanCutGrass ) { GetComboPos(x0, y0, cpos); AddPoof(cpos[0], cpos[1], 1495, 0); SetComboData(4, x0, y0, 0); DropRandomItem(15, cpos[0], cpos[1]); Game->PlaySound(SFX_GRASSCUT); } } combo = ComboData(4, x1, y0); if ( combo ) { int ctype = ComboType(4, x1, y0); if (ctype == CT_BUSH || ctype == CT_BUSHC) { GetComboPos(x1, y0, cpos); AddPoof(cpos[0], cpos[1], 1490, 0); SetComboData(4, x1, y0, 0); Game->PlaySound(SFX_BUSH); DropRandomItem(15, cpos[0], cpos[1]); bHitSomething = true; } else if ( ctype == CT_SLASH ) { GetComboPos(x1, y0, cpos); AddPoof(cpos[0], cpos[1], 1475, 0); SetComboData(4, x1, y0, 0); Game->PlaySound( SFX_SHATTER ); DropRandomItem(15, cpos[0], cpos[1]); bHitSomething = true; } else if ( (ctype == CT_TALLGRASS || ctype == CT_TALLGRASSC) && bCanCutGrass) { GetComboPos(x1, y0, cpos); AddPoof(cpos[0], cpos[1], 1495, 0); SetComboData(4, x1, y0, 0); Game->PlaySound(SFX_GRASSCUT); DropRandomItem(15, cpos[0], cpos[1]); } } combo = ComboData(4, x1, y1); if ( combo ) { int ctype = ComboType(4, x1, y1); if (ctype == CT_BUSH || ctype == CT_BUSHC) { GetComboPos(x1, y1, cpos); AddPoof(cpos[0], cpos[1], 1490, 0); SetComboData(4, x1, y1, 0); Game->PlaySound(SFX_BUSH); DropRandomItem(15, cpos[0], cpos[1]); bHitSomething = true; } else if ( ctype == CT_SLASH ) { GetComboPos(x1, y1, cpos); AddPoof(cpos[0], cpos[1], 1475, 0); SetComboData(4, x1, y1, 0); Game->PlaySound( SFX_SHATTER ); DropRandomItem(15, cpos[0], cpos[1]); bHitSomething = true; } else if ( (ctype == CT_TALLGRASS || ctype == CT_TALLGRASSC) && bCanCutGrass ) { GetComboPos(x1, y1, cpos); AddPoof(cpos[0], cpos[1], 1495, 0); SetComboData(4, x1, y1, 0); Game->PlaySound(SFX_GRASSCUT); DropRandomItem(15, cpos[0], cpos[1]); } } combo = ComboData(4, x0, y1); if ( combo ) { int ctype = ComboType(4, x0, y1); if (ctype == CT_BUSH || ctype == CT_BUSHC) { GetComboPos(x0, y1, cpos); AddPoof(cpos[0], cpos[1], 1490, 0); SetComboData(4, x0, y1, 0); Game->PlaySound(SFX_BUSH); DropRandomItem(15, cpos[0], cpos[1]); bHitSomething = true; } else if ( ctype == CT_SLASH ) { GetComboPos(x0, y1, cpos); AddPoof(cpos[0], cpos[1], 1475, 0); SetComboData(4, x0, y1, 0); Game->PlaySound( SFX_SHATTER ); DropRandomItem(15, cpos[0], cpos[1]); bHitSomething = true; } else if ( (ctype == CT_TALLGRASS || ctype == CT_TALLGRASSC) && bCanCutGrass ) { GetComboPos(x0, y1, cpos); AddPoof(cpos[0], cpos[1], 1495, 0); SetComboData(4, x0, y1, 0); Game->PlaySound(SFX_GRASSCUT); DropRandomItem(15, cpos[0], cpos[1]); } } } if ( bHitSomething && Link->Misc[ LMISC_STATE ] == LSTATE_ATTACK_HOLD && Link->Misc[ LMISC_HIT_FRAME ] == 0 ) { Link->Misc[ LMISC_HIT_FRAME ] = 16; } } void UpdateCamera() { //compute camera position. camPos_X = Link_vX - 120; camPos_Y = Link_vY-Link_vZ - 80; //clamp camera coordinates. if ( camPos_X < 0 ) camPos_X = 0; if ( camPos_X > ScreenData[18] ) camPos_X = ScreenData[18]; if ( camPos_Y < 0 ) camPos_Y = 0; if ( camPos_Y > ScreenData[19] ) camPos_Y = ScreenData[19]; //compute the sprite location. Link_sX = Link_vX - camPos_X; Link_sY = Link_vY-Link_vZ - camPos_Y; } void Link_Attack() { Link->Misc[ LMISC_STATE ] = LSTATE_ATTACKING; Link->Misc[ LMISC_FRAME ] = 0; //we randomly choose a sword shout to play, if any. int shout = Rand(100); if ( shout >= 60 ) { if ( shout < 73 ) Game->PlaySound( SFX_SWORD_SHOUT1 ); else if ( shout < 87 ) Game->PlaySound( SFX_SWORD_SHOUT2 ); else Game->PlaySound( SFX_SWORD_SHOUT3 ); } Game->PlaySound( SFX_SWORD ); } void Link_Action(bool bPickupOnly) { int offsetX = 0; int offsetY = 0; if ( Link->Dir == DIR_LEFT ) { offsetX = -8; offsetY = 8; } else if ( Link->Dir == DIR_RIGHT ) { offsetX = 24; offsetY = 8; } else if ( Link->Dir == DIR_UP ) { offsetX = 8; offsetY = -8; } else if ( Link->Dir == DIR_DOWN ) { offsetX = 8; offsetY = 24; } if ( Link->Misc[ LMISC_HELD_ITEM ] > 0 ) { Throw(); } else { int ctype = ComboType(4, Link_vX+offsetX, Link_vY+offsetY); if ( ctype == CT_BUSH || ctype == CT_BUSHC ) { Pickup(4, Link_vX+offsetX, Link_vY+offsetY); Game->PlaySound( SFX_LIFT_BUSH ); } else if ( ctype == CT_SLASH ) { if ( Link_Items[0] == ITEM_BRACELET || Link_Items[1] == ITEM_BRACELET ) { Pickup(4, Link_vX+offsetX, Link_vY+offsetY); } else { OpenMessageBox(TooHeavyString, -1, true, false); } } else if ( ctype == CT_BOMB_FLOWER ) { if ( Link_Items[0] == ITEM_BRACELET || Link_Items[1] == ITEM_BRACELET ) { Pickup(4, Link_vX+offsetX, Link_vY+offsetY); Item_BombTimer = 300; } else { OpenMessageBox(TooHeavyString, -1, true, false); } } else if ( ctype == CT_SIGN && bPickupOnly == false ) { //which string do we display? int string = ComboFlag(4, Link_vX+offsetX, Link_vY+offsetY, 0); OpenMessageBox(string, -1, true, false); } else if ( (ctype == CT_CHEST || ctype == CT_CHEST2) && bPickupOnly == false ) { //which string do we display? int string = ComboFlag(4, Link_vX+offsetX, Link_vY+offsetY, 0); //first we change the combo to an open treasure chest and play the sound... int c = ComboData(4, Link_vX+offsetX, Link_vY+offsetY); SetComboData(4, Link_vX+offsetX, Link_vY+offsetY, c+1); Game->PlaySound(SFX_CHEST_OPEN); //then go through the message box. OpenMessageBox(string, -1, true, true); } else { //Are there any "items" near by to interact with? int itm = GetItemInRange(Link_vX+offsetX, Link_vY+offsetY); if ( itm < 0 ) itm = GetItemInRange(Link_vX, Link_vY); if ( itm > -1 ) { int type = itemType[itm]; if ( type == CT_SLASH || type == CT_BOMB_FLOWER ) { if ( Link_Items[0] == ITEM_BRACELET || Link_Items[1] == ITEM_BRACELET ) { Link->Misc[ LMISC_HELD_ITEM ] = itemCombo[itm]; Link->Misc[ LMISC_HELD_ITEM_TYPE ] = type; Link->Misc[ LMISC_STATE ] = LSTATE_HOLDING_ITEM; if ( type == CT_BOMB_FLOWER ) Item_BombTimer = itemTimer[itm]; Items_Remove(itm); Game->PlaySound( SFX_LIFT ); } else { OpenMessageBox(TooHeavyString, -1, true, false); } } } } } } void AddReGrowData(int scr, int pos, int combo) { //Add a "regrowth" item if ( reGrowCnt < reGrowMaxCnt ) { reGrowData[reGrowCnt+REGROW_DATA ] = scr | (pos<<7); reGrowData[reGrowCnt+REGROW_COMBO] = combo; reGrowData[reGrowCnt+REGROW_TIMER] = 600; reGrowCnt++; } } void RemoveReGrowData(int idx) { for (int i=idx; iPlaySound( SFX_LIFT ); //which screen? int sx = x>>8; int sy = Floor( y/176 ); //which tile on that screen? int tx = (x - sx*256)>>4; int ty = (y - sy*176)>>4; //read the map and screen data for layer 4. int idx = 11; int map = ScreenData[idx+0]; int scrBase = ScreenData[idx+1]; //finally get the flag. If the inherent flag is non-zero then use that //otherwise use the combo flag. int scr = sy*16 + sx + scrBase; int pos = ty*16+tx; int c = Game->GetComboData(map, scr, pos); int t = Game->GetComboType(map, scr, pos); AddLayerChange(map, scr, pos, c); //special combo for Bomb Flowers... if ( t == CT_BOMB_FLOWER ) { //Add a "regrowth" item AddReGrowData(scr, pos, c); c = 1311; } Game->SetComboData(map, scr, pos, 0); Link->Misc[ LMISC_HELD_ITEM ] = c; Link->Misc[ LMISC_HELD_ITEM_TYPE ] = t; Link->Misc[ LMISC_STATE ] = LSTATE_HOLDING_ITEM; } void Throw() { int c = Link->Misc[ LMISC_HELD_ITEM ]; if ( c <= 0 ) return; int timer = 0; int t = Link->Misc[ LMISC_HELD_ITEM_TYPE ]; Link->Misc[ LMISC_HELD_ITEM ] = 0; Link->Misc[ LMISC_STATE ] = LSTATE_NORMAL; bool bThrow = true; float elast = -1; if ( t == CT_BOMB_FLOWER ) { timer = Item_BombTimer; Item_BombTimer = 0; //just put it down if we're not moving. if ( Link->InputLeft == false && Link->InputRight == false && Link->InputUp == false && Link->InputDown == false ) bThrow = false; elast = 0.5; } else if ( t == CT_SLASH && Link->InputLeft == false && Link->InputRight == false && Link->InputUp == false && Link->InputDown == false ) { bThrow = false; elast = 0; } //Add the item and set it in motion. int itemX = Link_vX; int itemY = Link_vY; int level = 0; if ( Link_PrevFlags&LFLAG_ON_BRIDGE ) level = 1; float zVel_Throw = 2.0; float zVel_Place = 1.0; if ( Link->Dir == DIR_LEFT ) { if ( bThrow ) Items_Add(itemX, itemY, 13, t, c, elast, -4, 0, zVel_Throw, timer, level); else Items_Add(itemX, itemY, 0, t, c, elast, -2, 0, zVel_Place, timer, level); } else if ( Link->Dir == DIR_RIGHT ) { if ( bThrow ) Items_Add(itemX, itemY, 13, t, c, elast, 4, 0, zVel_Throw, timer, level); else Items_Add(itemX, itemY, 0, t, c, elast, 2, 0, zVel_Place, timer, level); } else if ( Link->Dir == DIR_UP ) { if ( bThrow ) Items_Add(itemX, itemY, 13, t, c, elast, 0, -4, zVel_Throw, timer, level); else Items_Add(itemX, itemY, 0, t, c, elast, 0, -2, zVel_Place, timer, level); } else// if ( Link->Dir == DIR_DOWN ) { if ( bThrow ) Items_Add(itemX, itemY, 13, t, c, elast, 0, 4, zVel_Throw, timer, level); else Items_Add(itemX, itemY, 0, t, c, elast, 0, 2, zVel_Place, timer, level); } } void DrawLink(int scrOffset, ffc wpnHolder) { int cset = 2; int layer = 2; int sword_cset = 2; //flicker if damaged. int ef = Link->Misc[LMISC_DAMAGE_ANIM]>>2; if ( (ef&1) ) cset = 3; if ( LinkFlags&LFLAG_ON_BRIDGE ) layer = 3; int LinkDir_TileOffset[] = { 9, 0, 6, 3 }; int LinkDir_TileOffset2t[] = { 6, 0, 4, 2 }; int LinkState_TileOffset[] = { 0, 133, 220, 180, 0, 0, 0, 0, 279 }; int attackBaseFrameOffset[] = { 0, 1, 1 }; //Up, Down, Left, Right int attackBaseFrame[] = { 158, 152, 156, 154 }; int attackWeaponFrames[] = { 1528, 1523, 1527, 1528, 1524, 1527, 1527, 1525, 1528, 1527, 1526, 1528 }; int attackWeaponFlip[] = { 0, 0, 0, 1, 0, 2, 0, 0, 1, 0, 0, 0 }; int attackOffsetX[] = { 16, 8, 0, -14, -14, 4, 0, -14, -16, 8, 14, 16 }; int attackOffsetY[] = { -4, -12, -15, 0, 14, 14, -15, -10, 1, -15, -10, 1 }; //Spin Attack! int sa_attackFrame[] = { 157, 157, 159, 159, 155, 155, 153, 153 }; int sa_attackWeaponFrames[] = { 1528, 1523, 1527, 1526, 1528, 1524, 1527, 1525 }; int sa_attackWeaponFlip[] = { 1, 1, 0, 0, 0, 1, 2, 2 }; int sa_attackOffsetX[] = { -15, -14, 0, 14, 16, 14, 4, -12 }; int sa_attackOffsetY[] = { 1, -12, -15, -10, 1, 14, 14, 12 }; int curTile; int frame; int wpnFrame; if ( Link->Misc[LMISC_STATE] == LSTATE_ITEM_FOUND ) { curTile = 222; frame = 0; } else if ( Link->Misc[LMISC_STATE] == LSTATE_THROW_WPN ) { curTile = 157; if ( Link->Dir == DIR_UP ) curTile = 159; else if ( Link->Dir == DIR_RIGHT ) curTile = 155; else if ( Link->Dir == DIR_DOWN ) curTile = 153; frame = 0; } else if ( Link->Misc[LMISC_STATE] == LSTATE_ATTACKING ) { curTile = attackBaseFrame[ Link->Dir ]; wpnFrame = Min( Link->Misc[ LMISC_FRAME ] >> 2, 2 ); frame = attackBaseFrameOffset[ wpnFrame ]; } else if ( Link->Misc[LMISC_STATE] == LSTATE_SPIN_ATTACK ) { curTile = 0; int curFrameBase = Link->Misc[ LMISC_FRAME ] >> 2; if ( Link->Dir == DIR_UP ) curFrameBase += 2; else if ( Link->Dir == DIR_RIGHT ) curFrameBase += 4; else if ( Link->Dir == DIR_DOWN ) curFrameBase += 6; wpnFrame = curFrameBase % 8; frame = sa_attackFrame[ wpnFrame ]; } else if ( Link->Misc[LMISC_STATE] == LSTATE_ATTACK_HOLD ) { curTile = 2 + LinkDir_TileOffset[Link->Dir] + LinkState_TileOffset[LSTATE_NORMAL]; wpnFrame = 2; frame = ( Link->Misc[ LMISC_FRAME ] >> 2 ) % 3; } else if ( Link->Misc[LMISC_STATE] == LSTATE_PUSHING ) { curTile = 2 + LinkDir_TileOffset2t[Link->Dir] + LinkState_TileOffset[ Link->Misc[LMISC_STATE] ]; frame = ( Link->Misc[ LMISC_FRAME ] >> 2 ) % 2; } else if ( Link->Misc[LMISC_STATE] == LSTATE_DROWN ) { frame = Min(Link->Misc[ LMISC_FRAME ]/10, 2); curTile = EFFECT_DROWN; } else if ( Link->Misc[LMISC_STATE] == LSTATE_FALL ) { frame = Min(Link->Misc[ LMISC_FRAME ]/6, 4); curTile = EFFECT_FALL; } else { curTile = 2 + LinkDir_TileOffset[Link->Dir] + LinkState_TileOffset[ Link->Misc[LMISC_STATE] ]; if ( Link->Misc[LMISC_STATE] == LSTATE_NORMAL && (Link_Items[0] == ITEM_SHIELD || Link_Items[1] == ITEM_SHIELD) ) { curTile += 259; } frame = ( Link->Misc[ LMISC_FRAME ] >> 2 ) % 3; } //Draw any screen weapons. DrawScreenWeapon(wpnHolder); //draw Link shadow, if in the air... if ( Link_vZ > 0 ) { int vy_shadow = Floor(Link_vY-Link_vZ) - camPos_Y; if ( Link_vZ > 8 ) Screen->FastTile(layer, Link_sX, vy_shadow+scrOffset, 1120, 2, 128); else if ( Link_vZ > 0 ) Screen->FastTile(layer, Link_sX, vy_shadow+scrOffset, 1121, 2, 128); } //draw the Link sprite. Screen->DrawTile(layer, Link_sX, Link_sY+scrOffset, curTile+frame, 1, 1, cset, -1, -1, 0, 0, 0, 0, true, 128); //draw the item that Link is holding (if any). if ( Link->Misc[LMISC_STATE] == LSTATE_ATTACKING || Link->Misc[LMISC_STATE] == LSTATE_ATTACK_HOLD || Link->Misc[LMISC_STATE] == LSTATE_SPIN_ATTACK ) { if ( Link->Misc[LMISC_STATE] == LSTATE_ATTACK_HOLD && Link->Misc[ LMISC_TIMER ] > 60 ) { int sf = Link->Misc[LMISC_TIMER]>>2; if ( (sf&1) ) sword_cset = 3; } int dx = 0; int dy = 0; if ( Link->Misc[LMISC_STATE] == LSTATE_ATTACK_HOLD ) { if ( Link->Dir == DIR_LEFT ) { dx = 5; if ( Link->Misc[ LMISC_HIT_FRAME ] < 12 ) dx += 2; } else if ( Link->Dir == DIR_RIGHT ) { dx = -5; if ( Link->Misc[ LMISC_HIT_FRAME ] < 12 ) dx -= 2; } else if ( Link->Dir == DIR_UP ) { dy = 4; if ( Link->Misc[ LMISC_HIT_FRAME ] < 12 ) dx += 2; } else { dy = -2; if ( Link->Misc[ LMISC_HIT_FRAME ] < 12 ) dx -= 2; } } int flip; int wframe; int wdx; int wdy; if ( Link->Misc[LMISC_STATE] != LSTATE_SPIN_ATTACK ) { wpnFrame += (Link->Dir*3); flip = attackWeaponFlip[wpnFrame]; wframe = attackWeaponFrames[wpnFrame]; wdx = attackOffsetX[wpnFrame]+dx; wdy = attackOffsetY[wpnFrame]+dy; } else { flip = sa_attackWeaponFlip[wpnFrame]; wframe = sa_attackWeaponFrames[wpnFrame]; wdx = sa_attackOffsetX[wpnFrame]+dx; wdy = sa_attackOffsetY[wpnFrame]+dy; } Screen->DrawTile(layer, Link_sX+wdx, Link_sY+scrOffset+wdy, wframe, 1, 1, sword_cset, -1, -1, 0, 0, 0, flip, true, 128); if ( Link->Misc[LMISC_STATE] != LSTATE_ATTACK_HOLD || Link->Misc[ LMISC_FRAME ] > 0 ) SetLinkAttackBounds( Link_vX+wdx, Link_vY+wdy ); } else if ( Link->Misc[ LMISC_STATE ] == LSTATE_ITEM_FOUND ) { int yOffs = -13+scrOffset; int frameOffs = Min( (Link->Misc[ LMISC_FRAME ] >> 2), 4 ); yOffs -= frameOffs; Screen->DrawTile(layer, Link_sX, Link_sY+yOffs, Link->Misc[ LMISC_HELD_ITEM ], 1, 1, 2, -1, -1, 0, 0, 0, 0, true, 128); } else if ( Link->Misc[ LMISC_HELD_ITEM ] > 0 ) { int yOffs = -13+scrOffset; if ( Link->Dir == DIR_LEFT || Link->Dir == DIR_RIGHT ) { int LinkAnimHeldOffs[] = { 0, 1, 1 }; yOffs += LinkAnimHeldOffs[frame]; } int itm_cset = 2; if ( Link->Misc[ LMISC_HELD_ITEM_TYPE ] == CT_BOMB_FLOWER && Item_BombTimer < 60 ) { ef = Item_BombTimer>>2; if ( (ef&1) ) itm_cset = 3; } Screen->DrawCombo(layer, Link_sX, Link_sY+yOffs, Link->Misc[ LMISC_HELD_ITEM ], 1, 1, itm_cset, -1, -1, 0, 0, 0, 0, 0, true, 128); } //draw effects on top of Link. if ( Link_vZ == 0 && (Link->Misc[LMISC_STATE] == LSTATE_NORMAL || Link->Misc[LMISC_STATE] == LSTATE_HOLDING_ITEM || Link->Misc[LMISC_STATE] == LSTATE_PUSHING || Link->Misc[LMISC_STATE] == LSTATE_ATTACKING || Link->Misc[LMISC_STATE] == LSTATE_BLOCKING) ) { int effectTile = 0; int type = ComboType(4, Link_vX+8, Link_vY+12); if ( type == CT_TALLGRASS || type == CT_TALLGRASSC ) { if ( Link->Dir == DIR_UP || Link->Dir == DIR_DOWN ) effectTile = EFFECT_TALLGRASS; else effectTile = EFFECT_TALLGRASS+1; } else { type = ComboType(0, Link_vX+8, Link_vY+12); if ( type == CT_SHALLOWWATER ) { if ( Link->Dir == DIR_UP || Link->Dir == DIR_DOWN ) effectTile = EFFECT_SHALLOW_WATER; else effectTile = EFFECT_SHALLOW_WATER+1; } } if ( effectTile > 0 ) { Screen->DrawTile(layer, Link_sX, Link_sY+scrOffset+2, effectTile, 1, 1, 2, -1, -1, 0, 0, 0, 0, true, 128); } } } void DrawScreen(int scrOffset) { //Draw the layers. int scrX = Max( camPos_X>>8, 0 ); int scrY = Max( Floor(camPos_Y/176), 0 ); int cx = Floor(camPos_X); int cy = Floor(camPos_Y); for (int sy=scrY; sy<=scrY+1; sy++) { int ypos = sy*176; int y = ypos - cy + scrOffset; int scr_base = ScreenData[2]+(sy<<4); for (int sx=scrX; sx<=scrX+1; sx++) { int xpos = sx*256; int x = xpos - cx; Screen->DrawLayer(0, ScreenData[1], scr_base+sx, 0, x, y, 0, 128); //Layers... int ii=5; for (int l=1; l<=6; l++) { //make sure the layer is set before drawing. if ( ScreenData[ii+0] > -1 ) { int layer = l; //our layer 4 is special - interactive objects. //so it really needs to be on layer 2. if ( l == 4 ) layer = 2; //draw the current layer Screen->DrawLayer(layer, ScreenData[ii+0], ScreenData[ii+1]+(sy<<4)+sx, 0, x, y, 0, 128); } ii += 2; } } } } //type = which type of flag? // 0 = inherent OR (if 0) combo // 1 = inherent only // 2 = combo only. int ComboFlag(int layer, int x, int y, int type) { //which screen? int sx = x>>8; int sy = Floor( y/176 ); //which tile on that screen? int tx = (x - sx*256)>>4; int ty = (y - sy*176)>>4; //which map and base screen, based on layer. int map = ScreenData[1]; int scrBase = ScreenData[2]; if ( layer > 0 ) { int idx = 5+(layer-1)*2; if ( ScreenData[idx+0] > -1 ) { map = ScreenData[idx+0]; scrBase = ScreenData[idx+1]; } } //finally get the flag. If the inherent flag is non-zero then use that //otherwise use the combo flag. int scr = sy*16 + sx + scrBase; int flag = 0; if ( type < 2 ) { flag = Game->GetComboInherentFlag(map, scr, ty*16+tx); } if ( flag == 0 && type != 1 ) { flag = Game->GetComboFlag(map, scr, ty*16+tx); } return flag; } int ComboType_Data(int layer, int x, int y, int data) { //which screen? int sx = x>>8; int sy = Floor( y/176 ); //which tile on that screen? int tx = (x - sx*256)>>4; int ty = (y - sy*176)>>4; //which map and base screen, based on layer. int map = ScreenData[1]; int scrBase = ScreenData[2]; if ( layer > 0 ) { int idx = 5+(layer-1)*2; if ( ScreenData[idx+0] > -1 ) { map = ScreenData[idx+0]; scrBase = ScreenData[idx+1]; } } //finally get the flag. If the inherent flag is non-zero then use that //otherwise use the combo flag. int scr = sy*16 + sx + scrBase; //fill in data about this combo so it doesn't have to be re-computed. data[0] = map; data[1] = scr; data[2] = ty*16+tx; return Game->GetComboType(map, scr, data[2]); } int ComboType(int layer, int x, int y) { //which screen? int sx = x>>8; int sy = Floor( y/176 ); //which tile on that screen? int tx = (x - sx*256)>>4; int ty = (y - sy*176)>>4; //which map and base screen, based on layer. int map = ScreenData[1]; int scrBase = ScreenData[2]; if ( layer > 0 ) { int idx = 5+(layer-1)*2; if ( ScreenData[idx+0] > -1 ) { map = ScreenData[idx+0]; scrBase = ScreenData[idx+1]; } } //finally get the flag. If the inherent flag is non-zero then use that //otherwise use the combo flag. int scr = sy*16 + sx + scrBase; return Game->GetComboType(map, scr, ty*16+tx); } int ComboData(int layer, int x, int y) { //which screen? int sx = x>>8; int sy = Floor( y/176 ); //which tile on that screen? int tx = (x - sx*256)>>4; int ty = (y - sy*176)>>4; //which map and base screen, based on layer. int map = ScreenData[1]; int scrBase = ScreenData[2]; if ( layer > 0 ) { int idx = 5+(layer-1)*2; if ( ScreenData[idx+0] > -1 ) { map = ScreenData[idx+0]; scrBase = ScreenData[idx+1]; } } //finally get the flag. If the inherent flag is non-zero then use that //otherwise use the combo flag. int scr = sy*16 + sx + scrBase; return Game->GetComboData(map, scr, ty*16+tx); } void SetComboData(int layer, int x, int y, int combo) { //which screen? int sx = x>>8; int sy = Floor( y/176 ); //which tile on that screen? int tx = (x - sx*256)>>4; int ty = (y - sy*176)>>4; //which map and base screen, based on layer. int map = ScreenData[1]; int scrBase = ScreenData[2]; if ( layer > 0 ) { int idx = 5+(layer-1)*2; if ( ScreenData[idx+0] > -1 ) { map = ScreenData[idx+0]; scrBase = ScreenData[idx+1]; } } //finally get the flag. If the inherent flag is non-zero then use that //otherwise use the combo flag. int scr = sy*16 + sx + scrBase; int pos = ty*16+tx; AddLayerChange(map, scr, pos, Game->GetComboData(map, scr, pos)); Game->SetComboData(map, scr, pos, combo); } void GetComboPos(int x, int y, int pos) { pos[0] = (x>>4)<<4; pos[1] = (y>>4)<<4; } int ComboSolid(int x, int y, int level) { //which screen? int sx = x>>8; int sy = Floor( y/176 ); //which tile on that screen? int tx = (x - sx*256)>>4; int ty = (y - sy*176)>>4; //determine the quadrant that this occupies... int qx = (x&15)>>3; int qy = (y&15)>>3; int m = Max(qx<<2,1); int bits = (1<GetComboSolid(ScreenData[1], scr, ty*16+tx); if ( level > 0 ) { int f = Game->GetComboInherentFlag(ScreenData[1], scr, ty*16+tx); if ( f == COMBO_FLAG_BRIDGE_ENTER ) solid = 0; else if ( f == COMBO_FLAG_BRIDGE_EDGE ) solid = 255; } //layer 1 if ( ScreenData[5+0] > -1 ) { scr = sy*16 + sx + ScreenData[5+1]; solid |= Game->GetComboSolid(ScreenData[5+0], scr, ty*16+tx); if ( level > 0 ) { int f = Game->GetComboInherentFlag(ScreenData[5+0], scr, ty*16+tx); if ( f == COMBO_FLAG_BRIDGE_EDGE ) solid = 255; } } //layer 2 if ( ScreenData[7+0] > -1 ) { scr = sy*16 + sx + ScreenData[7+1]; solid |= Game->GetComboSolid(ScreenData[7+0], scr, ty*16+tx); } //layer 4 if ( ScreenData[11+0] > -1 ) { scr = sy*16 + sx + ScreenData[11+1]; solid |= Game->GetComboSolid(ScreenData[11+0], scr, ty*16+tx); } return (solid & bits); } //This is just like ComboSolid() except that if any quandrant //of the combo is solid, it treats the whole thing as solid. int ComboSolid_AI(int x, int y, bool bCheckWater) { //which screen? int sx = x>>8; int sy = Floor( y/176 ); //which tile on that screen? int tx = (x - sx*256)>>4; int ty = (y - sy*176)>>4; //now do the base first. int scr = sy*16 + sx + ScreenData[2]; int solid = Game->GetComboSolid(ScreenData[1], scr, ty*16+tx); if ( solid == 0 && bCheckWater ) { int t = Game->GetComboType(ScreenData[1], scr, ty*16+tx); if ( t == CT_WATER ) solid = 255; } //layer 1 if ( ScreenData[5+0] > -1 ) { scr = sy*16 + sx + ScreenData[5+1]; solid |= Game->GetComboSolid(ScreenData[5+0], scr, ty*16+tx); } //layer 2 if ( ScreenData[7+0] > -1 ) { scr = sy*16 + sx + ScreenData[7+1]; solid |= Game->GetComboSolid(ScreenData[7+0], scr, ty*16+tx); } //layer 4 if ( ScreenData[11+0] > -1 ) { scr = sy*16 + sx + ScreenData[11+1]; solid |= Game->GetComboSolid(ScreenData[11+0], scr, ty*16+tx); } return solid; } void HandleCollision(int move, bool bAllowLedgeJump) { //Link's bounds: //x+ 0, x+8, x+15 //y+ 0, y+8, y+15 int level = 0; if ( Link_PrevFlags&LFLAG_ON_BRIDGE ) level = 1; int dx = move[0]; int dy = move[1]; int oldX = Link_vX; int oldY = Link_vY; int newX = oldX + dx; int newY = oldY + dy; bool bMoveOnX = false; bool bMoveOnY = false; if ( dx != 0 ) bMoveOnX = true; if ( dy != 0 ) bMoveOnY = true; bool bPressAgainstLedge = false; bool bYChecked = false; bool bAllowSliding = true; if ( dy != 0 ) //check y { int yOffs = 0; if ( dy > 0 ) yOffs = 15; bool bSolidLeft = ComboSolid(oldX+ 0, newY+yOffs, level); bool bSolidMid = ComboSolid(oldX+ 8, newY+yOffs, level); bool bSolidRight = ComboSolid(oldX+15, newY+yOffs, level); if ( bSolidLeft || bSolidMid || bSolidRight ) { //if we're just moving down and not moving left or right //and Link is hitting the solid surface in the middle of his bounding box, see if we need to jump... if ( dx == 0 && dy > 0 && bSolidMid ) { int cflag = ComboFlag(0, oldX+8, newY+yOffs, 0); if ( cflag == COMBO_FLAG_LEDGE && bAllowLedgeJump && Link->Misc[ LMISC_STATE ] != LSTATE_LEDGE_JUMP ) { bPressAgainstLedge = true; Link->Misc[ LMISC_TIMER ]++; if ( Link->Misc[ LMISC_TIMER ] > 15 ) { //if Link is holding something, throw it before jumping. if ( Link->Misc[ LMISC_HELD_ITEM ] > 0 ) Throw(); //determine where Link will land. int yTst = ((Link_vY>>4)<<4)+32; for (; yTstMisc[ LMISC_STATE ] = LSTATE_LEDGE_JUMP; Link_vZ = yTst - Link_vY; Link_vY = yTst; Link->Misc[ LMISC_ZVEL ] = 2.0; //reset my move data. move[0] = 0; move[1] = 0; Link->Misc[ LMISC_TIMER ] = 0; Game->PlaySound( SFX_JUMP ); return; } } } //support interactive objects. if ( bMoveOnX == false && bSolidMid ) { int ctype = ComboType(4, oldX+8, newY+yOffs); if ( ctype == CT_BUSH || ctype == CT_BUSHC || ctype == CT_SLASH || ctype == CT_SIGN || ctype == CT_BOMB_FLOWER ) { bAllowSliding = false; } } //we hit something, so just cancel out Y movement. newY = oldY; dy = 0; } if ( dx == 0 && dy == 0 && bAllowSliding ) //only adjust if not moving in X and we hit something. { newX = AdjustToGridOnX(bSolidLeft, bSolidRight, newX); dx = newX - oldX; } bYChecked = true; } if ( !bPressAgainstLedge && bAllowLedgeJump ) { Link->Misc[ LMISC_TIMER ] = 0; } dy = 0; if ( dx != 0 ) //check x { int xOffs = 0; if ( dx > 0 ) xOffs = 15; bool bSolidTop = ComboSolid(newX+xOffs, newY+0, level); bool bSolidMid = ComboSolid(newX+xOffs, newY+8, level); bool bSolidBot = ComboSolid(newX+xOffs, newY+15, level); if ( bSolidTop || bSolidMid || bSolidBot ) { //support interactive objects. if ( bMoveOnY == false && bSolidMid ) { int ctype = ComboType(4, newX+xOffs, newY+8); if ( ctype == CT_BUSH || ctype == CT_BUSHC || ctype == CT_SLASH || ctype == CT_SIGN || ctype == CT_BOMB_FLOWER ) { bAllowSliding = false; } } //we hit something, so just cancel out X movement. newX = oldX; dx = 0; } if ( dx == 0 && dy == 0 && bYChecked == false && bAllowSliding ) { newY = AdjustToGridOnY(bSolidTop, bSolidBot, oldY); dy = newY - oldY; } } if ( dy != 0 ) //check y again, if we needed to move based on X. { int yOffs = 0; if ( dy > 0 ) yOffs = 15; bool bSolidLeft = ComboSolid(newX+ 0, newY+yOffs, level); bool bSolidMid = ComboSolid(newX+ 8, newY+yOffs, level); bool bSolidRight = ComboSolid(newX+15, newY+yOffs, level); if ( bSolidLeft || bSolidMid || bSolidRight ) { //we hit something, so just cancel out Y movement. newY = oldY; dy = 0; } } move[0] = newX - oldX; move[1] = newY - oldY; if ( CheckObjectLevel(Link_vX, Link_vY, level) > 0 ) LinkFlags |= LFLAG_ON_BRIDGE; else LinkFlags &= ~LFLAG_ON_BRIDGE; } int AdjustToGridOnX(bool bSolidLeft, bool bSolidRight, int x) { int delta = 0; if ( bSolidLeft == false ) //move to the left... { delta = -1; } else if ( bSolidRight == false ) //move to the right... { delta = 1; } return x + delta; } int AdjustToGridOnY(bool bSolidTop, bool bSolidBot, int y) { int delta = 0; if ( bSolidTop == false ) //move upward... { delta = -1; } else if ( bSolidBot == false ) //move downward... { delta = 1; } return y + delta; }