Files
SMScripts/scripting/include/fartsy/ass_bosshandler.inc
Professor Fartsalot 94696de3ef v.8.4.0 - full audiosystem rewrite again
8th time rewriting it...

+ Make audio manager be an array - one entry for each possible client.
+ Added global audio manager for setting everyone's music at once and ticking all audio managers that are set to tick. This does not apply to clients who are experiencing "an event".
+ Made audio managers check if their clients are valid - if not, stop the manager immediately.
+ Use g_indexBGM for setting global bgm indexes.
+ Automatically set and update music for each client that joins the server
+ Automatically set and update music for each client when the game mode changes
+ Made /song command immediately reflect the current playing song
+ Changed how instant stopping works, unnecessary but yknow, tests???
+ Added a timer to delay music startup on plugin reload
- Removed old timer system used for playing music to new clients in favor of event based system.
2025-08-23 06:25:03 -04:00

601 lines
26 KiB
SourcePawn

// Fartsy's Boss Handler v1.0.0
enum struct BOSSHANDLER {
int bossID;
bool bTargetInRange[MAXPLAYERS+1];
bool bDoTankBuster;
bool isAlive;
bool shouldTick;
bool shouldTickBGM;
bool sOverflow;
bool tickBuster;
bool tickBusterNuclear;
float fAng[3];
float fPos[3];
int bustTarget;
int bustNukeTarget;
int iBossEnt;
int iBossEntHealth;
int iBossEntMaxHealth;
int iBossEntTarget;
int iBossPhase;
int iBossPhaseTmp;
void EmitSpawnSound(int boss){
switch(boss){
//Onslaughter
case 0:{
//Onslaughter has no spawn sound for now.... maybe when brute justice happens c:
}
//Sephiroth (wip)
case 1:{
}
//Metallizer (wip) To Do: Only change to Inferno if we're in boss attack range. Use a trigger_multiple for this. To Do, delay this by like 1 second so the DJMM sound can play properly.
case 2:{
sudo(1001);
GlobalAudio.Stop();
GlobalAudio.setBGM(20, true);
CustomSoundEmitter(SFXArray[106].realPath, 65, false, 0, 1.0, 100); //DJMM SFX
CreateTimer(1.5, DelayedMetalFace);
}
}
}
//For tracking model and skipping player ready check if boss is alive
void Tick(){
//PrintToServer("Boss %i, hp %i/%i", this.bossID, this.iBossEntHealth, this.iBossEntMaxHealth);
if (this.shouldTickBGM) (this.TickBGM());
if(this.isAlive && !core.isWave){
PrintToChatAll("PLACEHOLDER -- FORCE PLAYERS TO BE READY IF NOT IN WAVE.");
}
if(this.tickBuster) this.TickBuster();
switch(this.bossID){
case 0:{
float pos[3], ang[3], vel[3], newPos[3];
vel[0] = 0.0;
vel[1] = 0.0;
vel[2] = 0.0;
int BossEnt = FindEntityByTargetname("FB.BruteJusticeTrain", "func_tracktrain");
int BossTP = FindEntityByTargetname("FB.OnslaughterBase", "base_boss");
GetEntPropVector(BossEnt, Prop_Send, "m_vecOrigin", pos);
GetEntPropVector(BossEnt, Prop_Data, "m_angRotation", ang);
newPos[0] = pos[0];
newPos[1] = pos[1];
newPos[2] = pos[2] + 0.0;
TeleportEntity(BossTP, newPos, ang, vel);
}
case 1:{
}
case 2:{
this.iBossEnt = FindEntityByTargetname("FB.MetalBase", "base_boss");
if (!IsValidEntity(this.iBossEnt)) this.iBossPhase = 4;
else {
this.iBossEntHealth = GetEntProp(this.iBossEnt, Prop_Data, "m_iHealth");
this.iBossEntMaxHealth = GetEntProp(this.iBossEnt, Prop_Data, "m_iMaxHealth");
this.iBossEntTarget = FindEntityByTargetname("FB.MetalFace.Train", "func_tracktrain");
GetEntPropVector(this.iBossEntTarget, Prop_Send, "m_vecOrigin", this.fPos);
GetEntPropVector(this.iBossEntTarget, Prop_Data, "m_angRotation", this.fAng);
float vel[3];
float newPos[3];
vel[0] = 0.0;
vel[1] = 0.0;
vel[2] = 0.0;
newPos[0] = this.fPos[0];
newPos[1] = this.fPos[1];
newPos[2] = this.fPos[2] - 50.0;
TeleportEntity(this.iBossEnt, newPos, this.fAng, vel);
}
//Also need to set entity angles to its target... we can use this.bustTarget for the index of the client to get the angles...
//Do phase changes
switch(RoundToFloor((this.iBossEntHealth * 1.0/this.iBossEntMaxHealth * 1.0) * 10.0)){
case 10:{
this.iBossPhase = 1;
}
case 7:{
this.iBossPhase = 2;
}
case 4:{
this.iBossPhase = 3;
}
case 1:{
this.iBossPhase = 4;
}
}
if(this.iBossPhase != this.iBossPhaseTmp){
PrintToChatAll("PHASE CHANGED, %i to %i", this.iBossPhaseTmp, this.iBossPhase);
this.iBossPhaseTmp = this.iBossPhase;
if(this.iBossPhase > 1){
FastFire2("mfTelefect", "Start", "", 0.0, false);
FastFire2("FB.MetalFace.ReturnSND", "PlaySound", "", 0.0, false);
FastFire2("FB.MetalFace.Train", "TeleportToPathTrack", "MFTHOME", 0.25, false);
FastFire2("FB.MetalFace.Invuln", "Enable", "", 3.0, false);
}
//Phase changing, these only ever happen once per phase.
switch(this.iBossPhase){
case 0:{
PrintToChatAll("Phase 0 happened, boss must must be dead /shrug");
}
case 1:{
//PrintToChatAll("Recognized 0 -> 1, the boss must have just spawned!");
FastFire2("FB.MetalBase", "SetHealth", "320000", 0.0, false);
FastFire2("FB.MetalBase", "SetMaxHealth", "320000", 0.0, false);
FastFire2("FB.MetalBase", "AddOutput", "health 320000", 0.0, false);
FastFire2("FB.MetalFace", "Enable", "", 0.0, false);
}
case 2:{
//PrintToChatAll("Recognized 1 -> 2, things are heating up!");
GlobalAudio.setBGM(22, false);
FastFire2("FB.MetalFace.Train", "TeleportToPathTrack", "MFT1x0", 3.00, false);
FastFire2("FB.MetalFace.SkeleSpawner", "Enable", "", 0.0, false);
FastFire2("FB.MetalFace.SkeleSpawner", "Disable", "", 120.0, false);
FastFire2("mfTelefect", "Start", "", 120.0, false);
FastFire2("FB.MetalFace.ReturnSND", "PlaySound", "", 120.0, false);
FastFire2("FB.MetalFace.Train", "TeleportToPathTrack", "MFTHOME", 120.25, false);
FastFire2("FB.MetalFace.Invuln", "Disable", "", 123.0, false);
FastFire2("FB.MetalFace.Train", "TeleportToPathTrack", "tank_path_a_100", 126.85, false);
FastFire2("mfTelefect", "Start", "", 127.0, false);
FastFire2("FB.MetalFace.ReturnSND", "PlaySound", "", 127.0, false);
}
case 3:{
//PrintToChatAll("Recognized 2 -> 3, oh boi");
GlobalAudio.setBGM(24, false);
FastFire2("FB.MetalFace.Train", "TeleportToPathTrack", "MFT2x0", 3.00, false);
FastFire2("FB.MetalFace.MerasmusSpawner", "ForceSpawn", "", 5.0, false); //Spawn halloween bosses at MetalFace's location
FastFire2("FB.MetalFace.MonoculusSpawner", "ForceSpawn", "", 7.0, false); //Spawn halloween bosses at MetalFace's location
FastFire2("FB.MetalFace.HHHSpawner", "ForceSpawn", "", 9.0, false); //Spawn halloween bosses at MetalFace's location
FastFire2("FB.MetalFace.Invuln", "Disable", "", 123.0, false);
FastFire2("FB.MetalFace.Train", "TeleportToPathTrack", "tank_path_a_250", 126.85, false);
FastFire2("mfTelefect", "Start", "", 127.0, false);
FastFire2("FB.MetalFace.ReturnSND", "PlaySound", "", 127.0, false);
}
case 4:{
//PrintToChatAll("Recognized 3 -> 4, OHSHITOHFUCKOHNO");
GlobalAudio.setBGM(26, true);
FastFire2("FB.MetalFace.Train", "TeleportToPathTrack", "MFT3x0", 3.00, false);
FastFire2("FB.MetalFace.GigaBuster", "ForceSpawn", "", 10.0, false);
}
}
}
}
case 69:{
int eCount = 0;
for (int i = 1; i <= MaxClients; i++) {
if (!IsClientInGame(i)) continue;
if (GetClientTeam(i) == 2 && TF2_GetPlayerClass(i) == TFClass_Engineer) eCount++;
}
int sCount = 0;
int ent = -1
while ((ent = FindEntityByClassname(ent, "obj_sentrygun")) != -1) {
if (!IsValidEntity(ent)) continue;
if (this.sOverflow) {
SetEntProp(ent, Prop_Send, "m_iTeamNum", TFTeam_Blue);
SetEntPropEnt(ent, Prop_Data, "m_hOwnerEntity", 0);
}
if (GetEntProp(ent, Prop_Send, "m_iTeamNum") == 2) {
sCount++;
PrintToChatAll("Found %i sentry guns. I'm watching your ass...", sCount);
if (sCount > eCount * 7){
PrintToChatAll("[FARTSY.DLL] TOO MANY SENTRIES DETECTED. PREPARE TO DIE. WOLOLOOOOOOO!");
this.sOverflow = true;
ent = -1;
sCount = 0;
CreateTimer(3.0, TimedOperator, 42690);
}
}
}
}
}
}
//For tracking bgm and updating bgm based on player emnity
void TickBGM(){
if (this.shouldTickBGM){
int iBGM0;
int iBGM1;
switch(this.iBossPhase){
case 1:{
iBGM0 = 20;
iBGM1 = 21;
}
case 2:{
iBGM0 = 22;
iBGM1 = 23;
}
case 3:{
iBGM0 = 24;
iBGM1 = 25;
}
case 4:{
iBGM0 = 26;
iBGM1 = 27;
}
}
for (int i = 0; i < MaxClients; i++){
if (EmnityManager[i].IsAboveEmnityThreshold(SuggestEmnityThreshold())) {
CSEClient(i, BGMArray[iBGM1].realPath, BGMArray[iBGM1].SNDLVL, true, 1, 1.0, 100);
CSEClient(i, BGMArray[iBGM0].realPath, BGMArray[iBGM0].SNDLVL, true, 1, 0.05, 100);
}
else {
CSEClient(i, BGMArray[iBGM1].realPath, BGMArray[iBGM1].SNDLVL, true, 1, 0.05, 100);
CSEClient(i, BGMArray[iBGM0].realPath, BGMArray[iBGM0].SNDLVL, true, 1, 1.0, 100);
}
}
GlobalAudio.setBGM(iBGM0, false);
}
}
//Tank buster thing
void TickBuster(){
float cPos[3]; //Client's Position
float pPos[3]; //Ground Particle Target Position
float sPos[3]; //Sign/Arrow's Target Position
float rPos[3];
float fVelocity[3]; //Velocity for teleporting entities, used for angles as well so we don't need two vectors.
int iAirParticle = FindEntityByTargetname("FB.TankBuster.AirParticle", "func_brush");
int iBuster = FindEntityByTargetname("FB.TankBuster", "trigger_hurt");
int iExploParticle = FindEntityByTargetname("FB.TankBuster.ExplodeParticle", "info_particle_system");
int iParticle = FindEntityByTargetname("FB.TankBuster.GroundParticle", "info_particle_system");
int iSign = FindEntityByTargetname("FB.TankBuster.Sign", "func_rotating");
int iSpin = FindEntityByTargetname("FB.TankBuster.Spinner", "func_rotating");
GetClientAbsOrigin(this.bustTarget, cPos);
rPos[0] = cPos[0];
rPos[1] = cPos[1];
rPos[2] = cPos[2] + 50.0;
pPos[0] = cPos[0];
pPos[1] = cPos[1];
pPos[2] = cPos[2] + 25.0;
sPos[0] = cPos[0];
sPos[1] = cPos[1];
sPos[2] = cPos[2] + 150.0;
TeleportEntity(iAirParticle, rPos, fVelocity, fVelocity);
TeleportEntity(iParticle, pPos, fVelocity, fVelocity);
TeleportEntity(iExploParticle, pPos, fVelocity, fVelocity);
TeleportEntity(iBuster, cPos, fVelocity, fVelocity);
TeleportEntity(iSign, sPos, fVelocity, fVelocity);
TeleportEntity(iSpin, pPos, fVelocity, fVelocity);
}
//Tick client post
void TickForClient(int client){
float cPos[3];
GetClientAbsOrigin(client, cPos);
if(GetVectorDistance(cPos, this.fPos) < 500 && client == GetEmnityMax() && this.bDoTankBuster){
char sDamage[32];
this.bustTarget = client;
this.bDoTankBuster = false;
int iHealth = GetClientHealth(this.bustTarget);
Format(sDamage, sizeof(sDamage), "damage %i", RoundToFloor(iHealth * 0.65));
FastFire2("FB.TankBuster", "AddOutput", sDamage, 0.0, false);
FastFire2("FB.TankBusterSND", "PlaySound", "", 1.25, false);
FastFire2("FB.TankBuster.GroundParticle", "Start", "", 0.0, false);
FastFire2("FB.TankBuster.ExplodeSND", "PlaySound", "", 5.50, false);
FastFire2("FB.TankBuster.ExplodeParticle", "Start", "", 5.75, false);
FastFire2("FB.TankBuster.ExplodeParticle", "Stop", "", 9.0, false);
FastFire2("FB.TankBuster", "Enable", "", 5.75, false);
FastFire2("FB.TankBuster", "Disable", "", 6.0, false);
CreateTimer(1.0, StartTickBuster);
PrintToServer("Dropping a tank buster on %N, they have %i HP, the tank buster will do %s.", this.bustTarget, iHealth, sDamage);
CreateTimer(7.0, ResetTickBuster);
}
}
// Tank buster - Potato cannon edition!
void TickBusterNuclear(){
float cPos[3]; //Client's Position
float pPos[3]; //Ground Particle Target Position
float sPos[3]; //Sign/Arrow's Target Position
float rPos[3];
float nPos[3];
float fVelocity[3]; //Velocity for teleporting entities, used for angles as well so we don't need two vectors.
int iAirParticle = FindEntityByTargetname("FB.TankBuster.AirParticle", "func_brush");
int iBuster = FindEntityByTargetname("FB.TankBuster", "trigger_hurt");
int iExploParticle = FindEntityByTargetname("FB.TankBuster.ExplodeParticle", "info_particle_system");
int iParticle = FindEntityByTargetname("FB.TankBuster.GroundParticle", "info_particle_system");
int iSign = FindEntityByTargetname("FB.TankBuster.Sign", "func_rotating");
int iSpin = FindEntityByTargetname("FB.TankBuster.Spinner", "func_rotating");
int iNukeTemplate = FindEntityByTargetname("PL.NukeCart", "env_entity_maker");
GetClientAbsOrigin(this.bustNukeTarget, cPos);
rPos[0] = cPos[0];
rPos[1] = cPos[1];
rPos[2] = cPos[2] + 50.0;
pPos[0] = cPos[0];
pPos[1] = cPos[1];
pPos[2] = cPos[2] + 25.0;
sPos[0] = cPos[0];
sPos[1] = cPos[1];
sPos[2] = cPos[2] + 150.0;
nPos[0] = cPos[0];
nPos[1] = cPos[1];
nPos[2] = cPos[2] + 750.0;
TeleportEntity(iAirParticle, rPos, fVelocity, fVelocity);
TeleportEntity(iParticle, pPos, fVelocity, fVelocity);
TeleportEntity(iExploParticle, pPos, fVelocity, fVelocity);
TeleportEntity(iBuster, cPos, fVelocity, fVelocity);
TeleportEntity(iSign, sPos, fVelocity, fVelocity);
TeleportEntity(iSpin, pPos, fVelocity, fVelocity);
TeleportEntity(iNukeTemplate, nPos, fVelocity, fVelocity);
}
//Boss was slain
void TriggerDeath(){
this.shouldTick = false;
this.tickBuster = false;
this.iBossPhase = 0;
switch(this.bossID){
//Onslaughter lost
case 0: {
}
//Sephiroth lost
case 1:{
}
//Metal Face lost
case 2:{
FastFire2("FB.MetalFace.Train", "TeleportToPathTrack", "MFTHOME", 0.0, false);
}
}
}
//Boss reached bomb hatch
void TriggerVictory(){
this.shouldTick = false;
this.tickBuster = false;
float bossOrigin[3];
float targetOrigin[3];
int targetEnt = FindEntityByTargetname("capturezone_blue", "func_capturezone");
GetEntPropVector(targetEnt, Prop_Send, "m_vecOrigin", targetOrigin);
switch(this.bossID){
//Onslaughter Won
case 0:{
int bossEnt = FindEntityByTargetname("FB.MetalBase", "base_boss");
if(bossEnt == -1) return;
GetEntPropVector(bossEnt, Prop_Send, "m_vecOrigin", bossOrigin);
if (GetVectorDistance(bossOrigin, targetOrigin) < 2200){
//Do something here because Onslaughter is at the pit, victory for boss.
}
}
//Sephiroth Won
case 1:{
}
//Metallizer Won
case 2:{
int bossEnt = FindEntityByTargetname("FB.MetalBase", "base_boss");
if(bossEnt == -1) return;
GetEntPropVector(bossEnt, Prop_Send, "m_vecOrigin", bossOrigin);
if (GetVectorDistance(bossOrigin, targetOrigin) < 2200){
//Do something here because Metal Face is at the pit, victory for boss.
}
}
}
}
//Spawn the boi
void TriggerSpawn(int boss){
this.bossID = boss;
this.shouldTick = true;
switch(boss){
case 0:{
}
case 1:{
}
case 2:{
this.EmitSpawnSound(2);
FastFire2("FB.MetalFace.Train", "TeleportToPathTrack", "MFT0x0", 1.0, false);
FastFire2("FB.MetalFace.SpawnParticle", "Start", "", 1.0, false);
FastFire2("FB.MetalFace.Train", "SetSpeedReal", "1024", 1.0, false);
//switch(GetClientCount(true))
FastFire2("FB.MetalBase", "AddOutput", "health 320000", 0.0, false);
}
}
}
}
BOSSHANDLER BossHandler;
BOSSHANDLER Get_Boss_Handler() {
return BossHandler;
}
//Player specific async tick process
public Action OnPlayerRunCmd(int client, int &buttons, int &impulse, float vel[3], float angles[3], int &weapon){
if (BossHandler.shouldTick) BossHandler.TickForClient(client);
return Plugin_Continue;
}
stock int SuggestEmnityThreshold(){
switch(GetClientCount(true)){
case 1:{
return 75;
}
case 2:{
return 60;
}
case 3:{
return 50;
}
case 4:{
return 35;
}
case 5:{
return 25;
}
case 6:{
return 20;
}
case 7:{
return 15;
}
case 8,9,10:{
return 10;
}
}
return -1;
}
//Used for emitting boss sounds
public Action QueueBossSound(Handle timer, int sound){
AssLogger(LOGLVL_DEBUG, "QueueBossSound played %i", sound);
switch(sound){
case 2:{
}
case 10:{
CustomSoundEmitter(SFXArray[71].realPath, 65, false, 0, 1.0, 100); //Interrupting
CreateTimer(3.1, QueueBossSound, GetRandomInt(16,22));
}
case 11:{
CustomSoundEmitter(SFXArray[69].realPath, 65, false, 0, 1.0, 100); //FunnyLooking
CreateTimer(3.0, QueueBossSound, GetRandomInt(18, 21));
}
case 12:{
CustomSoundEmitter(SFXArray[75].realPath, 65, false, 0, 1.0, 100); //Racket
CreateTimer(3.2, QueueBossSound, GetRandomInt(16, 22));
}
case 13:{
CustomSoundEmitter(SFXArray[78].realPath, 65, false, 0, 1.0, 100); //WhatDoing
}
case 14:{
CustomSoundEmitter(SFXArray[79].realPath, 65, false, 0, 1.0, 100); //WhateverDoing
CreateTimer(4.3, QueueBossSound, GetRandomInt(16,17));
}
case 15:{
CustomSoundEmitter(SFXArray[72].realPath, 65, false, 0, 1.0, 100); //Join In
CreateTimer(3.2, QueueBossSound, 16);
}
//pt 2
case 16:{
CustomSoundEmitter(SFXArray[70].realPath, 65, false, 0, 1.0, 100); //Good show
}
case 17:{
CustomSoundEmitter(SFXArray[67].realPath, 65, false, 0, 1.0, 100); //Carryon
}
case 18:{
CustomSoundEmitter(SFXArray[66].realPath, 65, false, 0, 1.0, 100); //Carnage
}
case 19:{
CustomSoundEmitter(SFXArray[73].realPath, 65, false, 0, 1.0, 100); //Lets Start
}
case 20:{
CustomSoundEmitter(SFXArray[74].realPath, 65, false, 0, 1.0, 100); //Make Way
}
//Attack sounds, but also shared with spawn sounds
case 21:{
CustomSoundEmitter(SFXArray[76].realPath, 65, false, 0, 1.0, 100); //SliceYou
}
case 22:{
CustomSoundEmitter(SFXArray[77].realPath, 65, false, 0, 1.0, 100); //Stroll
}
//Attack sounds, used for random grunts
case 23:{
CustomSoundEmitter(SFXArray[80].realPath, 65, false, 0, 1.0, 100); //gah
}
case 24:{
CustomSoundEmitter(SFXArray[81].realPath, 65, false, 0, 1.0, 100); //guh
}
case 25:{
CustomSoundEmitter(SFXArray[82].realPath, 65, false, 0, 1.0, 100); //hah
}
//Defeats
case 26:{
CustomSoundEmitter(SFXArray[83].realPath, 65, false, 0, 1.0, 100); //BadSide
}
case 27:{
CustomSoundEmitter(SFXArray[84].realPath, 65, false, 0, 1.0, 100); //NotFair
}
case 28:{
CustomSoundEmitter(SFXArray[85].realPath, 65, false, 0, 1.0, 100); //Nothing
}
case 29:{
CustomSoundEmitter(SFXArray[86].realPath, 65, false, 0, 1.0, 100); //Scream
}
case 30:{
CustomSoundEmitter(SFXArray[87].realPath, 65, false, 0, 1.0, 100); //ToGo
}
//Special attack sounds like phase changes
case 31:{
CustomSoundEmitter(SFXArray[88].realPath, 65, false, 0, 1.0, 100); //Clever
}
case 32:{
CustomSoundEmitter(SFXArray[89].realPath, 65, false, 0, 1.0, 100); //Cmon
}
case 33:{
CustomSoundEmitter(SFXArray[90].realPath, 65, false, 0, 1.0, 100); //Die
}
case 34:{
CustomSoundEmitter(SFXArray[91].realPath, 65, false, 0, 1.0, 100); //GetOff
}
case 35:{
CustomSoundEmitter(SFXArray[92].realPath, 65, false, 0, 1.0, 100); //HearYouScream
}
case 36:{
CustomSoundEmitter(SFXArray[93].realPath, 65, false, 0, 1.0, 100); //IsntOver
}
case 37:{
CustomSoundEmitter(SFXArray[94].realPath, 65, false, 0, 1.0, 100); //Like
}
case 38:{
CustomSoundEmitter(SFXArray[95].realPath, 65, false, 0, 1.0, 100); //Little
}
case 39:{
CustomSoundEmitter(SFXArray[96].realPath, 65, false, 0, 1.0, 100); //Pay
}
case 40:{
CustomSoundEmitter(SFXArray[97].realPath, 65, false, 0, 1.0, 100); //Pointers
}
case 41:{
CustomSoundEmitter(SFXArray[98].realPath, 65, false, 0, 1.0, 100); //Seat
}
case 42:{
CustomSoundEmitter(SFXArray[99].realPath, 65, false, 0, 1.0, 100); //Size
}
case 43:{
CustomSoundEmitter(SFXArray[100].realPath, 65, false, 0, 1.0, 100); //There
}
case 44:{
CustomSoundEmitter(SFXArray[101].realPath, 65, false, 0, 1.0, 100); //TimeToDie
}
case 45:{
CustomSoundEmitter(SFXArray[102].realPath, 65, false, 0, 1.0, 100); //Way
}
//Ultimates
case 46:{
CustomSoundEmitter(SFXArray[103].realPath, 65, false, 0, 1.0, 100); //FinalWords
}
case 47:{
CustomSoundEmitter(SFXArray[104].realPath, 65, false, 0, 1.0, 100); //PartingGift
}
case 48:{
CustomSoundEmitter(SFXArray[105].realPath, 65, false, 0, 1.0, 100); //WatchAndLearn
}
}
return Plugin_Stop;
}
//Teleport all entities to the client every frame
public Action StartTickBuster(Handle timer){
BossHandler.tickBuster = true;
return Plugin_Stop;
}
//Used for stopping tank busters from ticking
public Action ResetTickBuster(Handle time){
BossHandler.tickBuster = false;
float fAirPart[3] = {
-1437.270019,
-3906.739990,
-1334.989990
};
float fVelocity[3];
int iAirParticle = FindEntityByTargetname("FB.TankBuster.AirParticle", "func_brush");
int iBuster = FindEntityByTargetname("FB.TankBuster", "trigger_hurt");
int iExploParticle = FindEntityByTargetname("FB.TankBuster.ExplodeParticle", "info_particle_system");
int iParticle = FindEntityByTargetname("FB.TankBuster.GroundParticle", "info_particle_system");
int iSign = FindEntityByTargetname("FB.TankBuster.Sign", "func_rotating");
int iSpin = FindEntityByTargetname("FB.TankBuster.Spinner", "func_rotating");
TeleportEntity(iAirParticle, fAirPart, fVelocity, fVelocity);
TeleportEntity(iParticle, fAirPart, fVelocity, fVelocity);
TeleportEntity(iExploParticle, fAirPart, fVelocity, fVelocity);
TeleportEntity(iBuster, fAirPart, fVelocity, fVelocity);
TeleportEntity(iSign, fAirPart, fVelocity, fVelocity);
TeleportEntity(iSpin, fAirPart, fVelocity, fVelocity);
return Plugin_Stop;
}
//Spawn metal face
public Action DelayedMetalFace(Handle timer){
GlobalAudio.setBGM(20, true);
CustomSoundEmitter(SFXArray[68].realPath, 65, false, 0, 1.0, 100); //Evil Laugh
CreateTimer(3.2, QueueBossSound, GetRandomInt(10,14));
sudo(1000);
BossHandler.shouldTickBGM = true;
return Plugin_Stop;
}