Copy to Clipboard Test

BS Ganon Code

const int SFX_BS_GANON_FANFARE = 64; //Sound that plays when entering the room
const int SFX_BS_GANON_HIT = 63; //Looping hit sound during Ganon's death
const int SFX_BS_GANON_DEATH = 62; //Screen flash sound during Ganon's death
const int SFX_BS_GANON_TRIFORCE = 65; //Sound of the triforce appearing

const int MIDI_BS_GANON = 13; //MIDI that plays when you enter the screen with Ganon. I'm assuming you're using MIDI right? If not, make it negative for enhanced music. Can read it off a ZQuest string.
const int TRACK_BS_GANON = 0; //On the even rarer chance you're using trackers...

const int C_BS_GANON_FLASHYELLOW = 0xEB; //Yellow used for the hit animation
const int C_BS_GANON_FLASHWHITE = 0x01; //White for the fade out

npc script BSGanon{
	const int _THISPTR = 0;
	const int _OTILE = 1;
	const int _FLASHTILES = 2;
	const int _SILVERHIT = 3;
	void SetTile(untyped dat, int frame){
		npc this = dat[_THISPTR];
		int oTile = dat[_OTILE];
		bitmap flashTiles = dat[_FLASHTILES];
		bool silverHit = dat[_SILVERHIT];
		
		if(!silverHit)
			this->ScriptTile = oTile+frame*this->TileWidth;
	}
	void SetFlashingTile(untyped dat, int frame, int flashLevel){
		npc this = dat[_THISPTR];
		int oTile = dat[_OTILE];
		bitmap flashTiles = dat[_FLASHTILES];
		bool silverHit = dat[_SILVERHIT];
		
		int x = frame*32;
		int y = flashLevel*32;
		
		flashTiles->WriteTile(0, x, y, oTile+9*this->TileWidth, true, false);
		flashTiles->WriteTile(0, x+16, y, oTile+9*this->TileWidth+1, true, false);
		flashTiles->WriteTile(0, x, y+16, oTile+9*this->TileWidth+20, true, false);
		flashTiles->WriteTile(0, x+16, y+16, oTile+9*this->TileWidth+21, true, false);
		
		this->ScriptTile = oTile+9*this->TileWidth;
	}
	void WaitArrowCollision(untyped dat, int frames){
		npc this = dat[_THISPTR];
		int oTile = dat[_OTILE];
		bitmap flashTiles = dat[_FLASHTILES];
		bool silverHit = dat[_SILVERHIT];
		for(int i=0; i<frames&&!silverHit; ++i){
			int hitby = this->HitBy[2]; //lweapon
			if(hitby){
				lweapon l = Screen->LoadLWeapon(hitby);
				Trace(l->ID);
				Trace(l->Level);
				if(l->ID==LW_ARROW&&l->Level>1){
					dat[_SILVERHIT] = true;
				}
			}
			Waitframe();
		}
	}
	//This teleport function is probably even janker than engine. Whoops
	void Teleport(npc this){
		int targetPos[] = {51, 59, 99, 107, 39, 119, 82, 92};
		int x; int y; int pos;
		//Loop over preset combo positions to find one suitably far from Link
		for(int i=0; i<24; ++i){
			pos = targetPos[Rand(8)];
			if(i>15)
				pos = targetPos[i-16];
			x = ComboX(pos);
			y = ComboY(pos);
			if(Distance(x+8, y+8, Link->X, Link->Y)>=96)
				break;
		}
		this->X = x;
		this->Y = y;
	}
	void run(int stunnedID){
		//If HP starts out at <0, this instance of the enemy was created to change the sprite palette
		if(this->HP<=0)
			Quit();
		
		//1 extra HP to account for health bar scripts
		++this->HP;
		
		//Store the enemy's defenses
		int defs[36];
		for(int i=0; i<36; ++i){
			defs[i] = this->Defense[i];
		}
		
		//Scale and position
		int oTile = this->Tile;
		this->Extend = 3;
		this->TileWidth = 2;
		this->TileHeight = 2;
		this->HitWidth = 32;
		this->HitHeight = 32;
		this->X = 112;
		this->Y = 80;
		
		//Create the bitmap used for drawing Ganon when flashing
		bitmap flashTiles = Game->CreateBitmap(320, 64);
		flashTiles->Clear(0);
		flashTiles->DrawTile(0, 0, 0, oTile, 20, 2, 14, -1, -1, 0, 0, 0, 0, false, 128);
		flashTiles->ReplaceColors(0, C_BS_GANON_FLASHYELLOW, 0xE1, 0xEF);
		flashTiles->DrawTile(0, 0, 32, oTile, 20, 2, 14, -1, -1, 0, 0, 0, 0, false, 128);
		flashTiles->Blit(0, flashTiles, 0, 0, 320, 32, 0, 32, 320, 32, 0, 0, 0, BITDX_TRANS, 0, true);
		flashTiles->Blit(0, flashTiles, 0, 0, 320, 32, 0, 32, 320, 32, 0, 0, 0, BITDX_TRANS, 0, true);
		flashTiles->Own();
		
		bitmap dissolve = Game->CreateBitmap(96, 32);
		dissolve->Clear(0);
		
		this->CollDetection = false;
		while(Link->X<32||Link->X>208||Link->Y<32||Link->Y>128){
			Waitframe();
		}
		
		untyped dat[] = {this, oTile, flashTiles, false};
		
		//Spawn in animation
		Game->PlayMIDI(0);
		Game->PlaySound(SFX_BS_GANON_FANFARE);
		WaitNoAction(64);
		SetTile(dat, 1);
		WaitNoAction(5);
		SetTile(dat, 2);
		WaitNoAction(5);
		SetTile(dat, 3);
		WaitNoAction(5);
		SetTile(dat, 4);
		WaitNoAction(5);
		SetTile(dat, 5);
		WaitNoAction(26);
		for(int i=0; i<7; ++i){
			SetTile(dat, 4);
			WaitNoAction(9);
			SetTile(dat, 5);
			WaitNoAction(9);
		}
		WaitNoAction(96);
		SetTile(dat, 3);
		WaitNoAction(5);
		SetTile(dat, 2);
		WaitNoAction(5);
		SetTile(dat, 1);
		WaitNoAction(5);
		SetTile(dat, 0);
		WaitNoAction(5);
		for(int i=0; i<6; ++i){
			this->DrawXOffset = -1000;
			WaitNoAction(2);
			this->DrawXOffset = 0;
			WaitNoAction(2);
		}
		Teleport(this);
		this->DrawXOffset = -1000;
		int shotTimer = 64;
		if(MIDI_BS_GANON>0)
			Game->PlayMIDI(MIDI_BS_GANON);
		else if(MIDI_BS_GANON<0){
			int str[512];
			Game->GetMessage(Abs(MIDI_BS_GANON), str);
			Game->PlayEnhancedMusic(str, TRACK_BS_GANON);
		}
		this->CollDetection = true;
		this->Immortal = true;
		this->NoSlide = true;
		int startHP = this->HP;
		int lastHP = this->HP;
		while(true){
			//Shoot a fireball every 64 frames
			if(shotTimer)
				--shotTimer;
			else{
				eweapon e = CreateEWeaponAt(EW_FIREBALL, this->X, this->Y);
				e->Angular = true;
				e->Dir = AngleDir4(Angle(e->X, e->Y, Link->X, Link->Y));
				e->Angle = DegtoRad(Angle(e->X, e->Y, Link->X, Link->Y));
				e->Step = 150;
				e->Damage = this->WeaponDamage;
				e->UseSprite(17);
				e->Unblockable = UNBLOCK_ALL;
				shotTimer = 64;
			}
			
			this->HP = Max(this->HP, 1);
			if(this->HP<lastHP){
				if(this->HP>1){ //Hit animation
					for(int i=0; i<36; ++i){
						if(i==NPCD_SWORD)
							this->Defense[i] = NPCDT_IGNORE;
						else
							this->Defense[i] = NPCDT_BLOCK;
					}
					this->DrawXOffset = 0;
					//Flash
					for(int i=0; i<5; ++i){
						SetTile(dat, 6);
						Waitframes(3);
						SetFlashingTile(dat, 6, 1);
						Waitframe();
						SetFlashingTile(dat, 6, 0);
						Waitframe();
						SetFlashingTile(dat, 6, 1);
						Waitframe();
					}
					SetTile(dat, 6);
					Waitframes(12);
					SetTile(dat, 5);
					Waitframes(7);
					SetTile(dat, 3);
					Waitframes(5);
					SetTile(dat, 2);
					Waitframes(5);
					SetTile(dat, 1);
					Waitframes(5);
					SetTile(dat, 0);
					Waitframes(5);
					//Flicker
					for(int i=0; i<6; ++i){
						this->DrawXOffset = -1000;
						Waitframes(2);
						this->DrawXOffset = 0;
						Waitframes(2);
					}
					this->DrawXOffset = -1000;
					for(int i=0; i<36; ++i){
						this->Defense[i] = defs[i];
					}
					Teleport(this);
				}
				else{ //Turn blue animation
					this->DrawXOffset = 0;
					//Create a new enemy to update the sprite palette, then instantly kill it. Hooray for jank
					npc n = CreateNPCAt(stunnedID, 120, -32);
					n->ItemSet = 0;
					n->HP = -1000;
					SetTile(dat, 5);
					WaitArrowCollision(dat, 32);
					for(int i=0; i<36; ++i){
						if(i==NPCD_ARROW)
							this->Defense[i] = NPCDT_NONE;
						else
							this->Defense[i] = NPCDT_IGNORE;
					}
					SetTile(dat, 4);
					WaitArrowCollision(dat, 9);
					SetTile(dat, 8);
					WaitArrowCollision(dat, 9);
					SetTile(dat, 6);
					for(int i=0; i<300; ++i){
						WaitArrowCollision(dat, 1);
					}
					WaitArrowCollision(dat, 9);
					SetTile(dat, 8);
					WaitArrowCollision(dat, 10);
					SetTile(dat, 4);
					WaitArrowCollision(dat, 8);
					SetTile(dat, 5);
					WaitArrowCollision(dat, 68);
					//Ganon was hit by a silver arrow, play death animation
					if(dat[_SILVERHIT]){
						//Randomize the order to dissolve pixels in
						int pixelOrder[1024];
						for(int i=0; i<1024; ++i){
							pixelOrder[i] = i;
						}
						for(int i=0; i<4096; ++i){
							int whichA = Rand(1024);
							int whichB = Rand(1024);
							int backup = pixelOrder[whichB];
							pixelOrder[whichB] = pixelOrder[whichA];
							pixelOrder[whichA] = backup;
						}
						this->CollDetection = false;
						flashTiles->Clear(0);
						flashTiles->DrawTile(0, 0, 0, this->ScriptTile, 2, 2, this->CSet, -1, -1, 0, 0, 0, 0, true, 128);
						Game->PlaySound(SFX_BS_GANON_DEATH);
						int erasedPixels;
						this->DrawXOffset = -1000;
						for(int i=0; i<240; ++i){
							if(i%32==0)
								Game->PlaySound(SFX_BS_GANON_HIT);
							if(i%2==0){
								this->HitXOffset = Rand(-1, 1);
								this->HitYOffset = Rand(-1, 1);
								if(i>32){
									for(int j=0; j<12; ++j){
										if(erasedPixels<1024){
											flashTiles->PutPixel(0, pixelOrder[erasedPixels]%32, Floor(pixelOrder[erasedPixels]/32), 0x00, 0, 0, 0, 128);
											++erasedPixels;
										}
									}
								}
							}
							flashTiles->Blit(2, RT_SCREEN, 0, 0, 32, 32, this->X+this->HitXOffset, this->Y+this->DrawYOffset+this->HitYOffset, 32, 32, 0, 0, 0, 0, 0, true);
							if(i>240*0.25)
								Screen->Rectangle(6, 0, 0, 255, 175, C_BS_GANON_FLASHWHITE, 1, 0, 0, 0, true, 64);
							if(i>240*0.5)
								Screen->Rectangle(6, 0, 0, 255, 175, C_BS_GANON_FLASHWHITE, 1, 0, 0, 0, true, 64);
							if(i>240*0.75)
								Screen->Rectangle(6, 0, 0, 255, 175, C_BS_GANON_FLASHWHITE, 1, 0, 0, 0, true, 128);
							WaitNoAction();
						}
						item triforce = CreateItemAt(I_TRIFORCEBIG, this->X+8, this->Y+8);
						triforce->Pickup = IP_DUMMY;
						item dust = CreateItemAt(I_DUST_PILE, this->X+8, this->Y+12);
						dust->HitXOffset = -1000;
						dust->Pickup = IP_DUMMY;
						Game->PlaySound(SFX_BS_GANON_TRIFORCE);
						for(int i=0; i<154; ++i){
							if(i<240*0.75)
								Screen->Rectangle(6, 0, 0, 255, 175, C_BS_GANON_FLASHWHITE, 1, 0, 0, 0, true, 64);
							if(i<240*0.5)
								Screen->Rectangle(6, 0, 0, 255, 175, C_BS_GANON_FLASHWHITE, 1, 0, 0, 0, true, 64);
							if(i<240*0.25)
								Screen->Rectangle(6, 0, 0, 255, 175, C_BS_GANON_FLASHWHITE, 1, 0, 0, 0, true, 128);
							WaitNoAction();
						}
						triforce->Pickup = 0;
						while(triforce->isValid()){
							Waitframe();
						}
						//Allow the enemy to die and shutters to open
						this->DrawXOffset = -1000;
						this->Immortal = false;
						this->ItemSet = 0;
						this->HP = -1000;
					}
					//Changing the sprite palette back. Ganon practices self harm
					n = CreateNPCAt(this->ID, 120, -32);
					n->ItemSet = 0;
					n->HP = -1000;
					for(int i=0; i<36; ++i){
						if(i==NPCD_SWORD)
							this->Defense[i] = NPCDT_IGNORE;
						else
							this->Defense[i] = NPCDT_BLOCK;
					}
					SetTile(dat, 3);
					Waitframes(5);
					SetTile(dat, 2);
					Waitframes(6);
					SetTile(dat, 1);
					Waitframes(4);
					SetTile(dat, 0);
					Waitframes(6);
					for(int i=0; i<10; ++i){
						this->DrawXOffset = -1000;
						Waitframes(2);
						this->DrawXOffset = 0;
						Waitframes(2);
					}
					this->DrawXOffset = -1000;
					for(int i=0; i<36; ++i){
						this->Defense[i] = defs[i];
					}
					this->HP = startHP;
					Teleport(this);
				}
			}
			lastHP = this->HP;
			
			this->ConstantWalk({this->Rate, this->Homing, 0});
			Waitframe();
		}
	}
}