diff -r 3e5b2f720126 src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_jedoga_shadowseeker.cpp --- a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_jedoga_shadowseeker.cpp Mon Sep 21 13:12:55 2009 +1000 +++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_jedoga_shadowseeker.cpp Mon Sep 21 13:42:00 2009 +1000 @@ -37,7 +37,13 @@ #define H_SPELL_LIGHTNING_BOLT 60032 #define SPELL_THUNDERSHOCK 56926//AOE works #define H_SPELL_THUNDERSHOCK 60029//AOE works -#define SPELL_GIFT_OF_THE_HERALD 56219//triggert if sucessfull sacreficed +#define SPELL_SACRIFICE_VISUAL 56133 +#define SPELL_SACRIFICE_BEAM 56150 +#define SPELL_SPHERE_VISUAL 56075 +//#define SPELL_GIFT_OF_THE_HERALD 56219//triggert if sucessfull sacreficed + +#define ENTRY_JEDOGA_CONTROLLER 30181 +#define ENTRY_VOLUNTEER 30385 //not in db #define SAY_AGGRO -1619000 @@ -55,29 +61,459 @@ #define SAY_PREACHING_4 -1619012 #define SAY_PREACHING_5 -1619013 +const float SacrificeCirclePos[3]= {376.27, -710.54, -16.18}; + +const int SACRIFICE_MAX = 18; +const float SacrificePos[SACRIFICE_MAX][4]= +{ + {364.32, -734.92, -16.18, 1.0}, + {359.94, -732.04, -16.18, 1.0}, + {370.31, -735.29, -16.18, 1.0}, + {375.80, -731.66, -16.18, 1.0}, + {355.60, -729.24, -16.18, 1.0}, + {350.92, -725.21, -16.17, 1.0}, + {346.17, -722.51, -16.17, 1.0}, + {363.97, -731.15, -16.18, 1.0}, + {359.33, -728.43, -16.19, 1.0}, + {385.48, -674.57, -16.17, 3.85}, + {388.65, -677.98, -16.17, 3.85}, + {391.90, -681.57, -16.17, 3.85}, + {395.48, -685.29, -16.17, 3.85}, + {399.20, -689.71, -16.17, 3.85}, + {401.85, -694.93, -16.17, 3.85}, + {400.91, -699.98, -16.18, 3.85}, + {388.38, -681.55, -16.18, 3.85}, + {391.95, -685.57, -16.18, 3.85} +}; + +const int VOLUNTEER_MAX = 12; +const float VolunteerPos[VOLUNTEER_MAX][4]= +{ + {442.21,-698.70,-18.03,2.9}, + {439.77,-701.98,-18.03,2.9}, + {437.56,-705.94,-18.04,2.9}, + {434.79,-709.87,-18.04,2.9}, + {430.96,-714.57,-18.04,2.9}, + {427.45,-718.38,-18.03,2.9}, + {397.80,-759.05,-18.03,1.9}, + {395.78,-762.84,-18.03,1.9}, + {393.72,-766.62,-18.04,1.9}, + {389.41,-769.50,-18.04,1.9}, + {386.60,-772.91,-18.03,1.9}, + {382.70,-774.98,-18.03,1.9} +}; + +enum VolunteerMode { + MODE_VOLUNTEER_STAIR, + MODE_VOLUNTEER_SACRIFICE, + MODE_VOLUNTEER_CHOSEN +}; + +enum SacrificePhase { + SACRIFICE_START, + SACRIFICE_ASCEND, + SACRIFICE_CALL, + SACRIFICE_CHOOSE, + SACRIFICE_ARRIVED, + SACRIFICE_OFFERED, + SACRIFICE_DESCEND, + SACRIFICE_IN_TRANSIT +}; + +// TODO instance data saves + +struct TRINITY_DLL_DECL mob_twilight_volunteer : public ScriptedAI +{ + VolunteerMode mode; + ScriptedInstance* pInstance; + + mob_twilight_volunteer(Creature *c) : ScriptedAI(c) + { + mode=MODE_VOLUNTEER_STAIR; + pInstance = c->GetInstanceData(); + } + + void SetMode(VolunteerMode newmode) + { + mode=newmode; + switch (mode) { + case MODE_VOLUNTEER_SACRIFICE: + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + DoCast(m_creature, SPELL_SPHERE_VISUAL); + m_creature->SetStandState(UNIT_STAND_STATE_KNEEL); + break; + case MODE_VOLUNTEER_CHOSEN: + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->SetStandState(UNIT_STAND_STATE_STAND); + m_creature->RemoveAurasDueToSpell(SPELL_SPHERE_VISUAL); + m_creature->SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE); + m_creature->GetMotionMaster()->MovePoint(0, SacrificeCirclePos[0],SacrificeCirclePos[1],SacrificeCirclePos[2]); + break; + case MODE_VOLUNTEER_STAIR: + // Standard combat AI + default : ; + } + } + + void Reset() {} + + void EnterCombat(Unit* who) + { + switch (mode) { + case MODE_VOLUNTEER_STAIR: + // use superclass + ScriptedAI::EnterCombat(who); + break; + case MODE_VOLUNTEER_SACRIFICE: + case MODE_VOLUNTEER_CHOSEN: + default : ; + } + } + + void AttackStart(Unit* who) + { + switch (mode) { + case MODE_VOLUNTEER_STAIR: + // use superclass + ScriptedAI::AttackStart(who); + break; + case MODE_VOLUNTEER_SACRIFICE: + case MODE_VOLUNTEER_CHOSEN: + default : ; + } + } + + void UpdateAI(const uint32 diff) + { + if (!UpdateVictim()) { + return; + } + + switch (mode) { + case MODE_VOLUNTEER_STAIR: + // no abilities in wowhead but comments say they're casters + DoMeleeAttackIfReady(); + break; + case MODE_VOLUNTEER_SACRIFICE: + case MODE_VOLUNTEER_CHOSEN: + default : ; + } + } + + void MovementInform(uint32 type, uint32 id); // defined below + void JustDied(Unit* killer); // defined below + +}; + struct TRINITY_DLL_DECL boss_jedogaAI : public ScriptedAI { - boss_jedogaAI(Creature *c) : ScriptedAI(c) {} + boss_jedogaAI(Creature *c) : ScriptedAI(c) + { + pInstance = c->GetInstanceData(); + bHeroicMode = c->GetMap()->IsHeroic(); + uiPreachIndex=0; + for(uint8 i=0; iForcedDespawn(); + } + uiSacrificeSpellVisualTarget=0; + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + void EnterCombat(Unit* who) { DoScriptText(SAY_AGGRO, m_creature); + m_creature->RemoveAurasDueToSpell(SPELL_SPHERE_VISUAL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + // summon twilight volunteers + SummonVolunteers(); + SummonSacrifices(); } - void AttackStart(Unit* who) {} - void MoveInLineOfSight(Unit* who) {} + void UpdateAI(const uint32 diff) { - //Return since we have no target - if (!UpdateVictim()) + if (!UpdateVictim()) { + preach(diff); // only preach if OOC return; + } + if (bInSacrifice) { + if (uiWaitTimer >= diff) { + uiWaitTimer -= diff; + } + else + { + float x,y,z,o; + m_creature->GetHomePosition(x,y,z,o); + switch (sacrificePhase) { + case SACRIFICE_START : // stop attacking, become immune + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE); + m_creature->GetMotionMaster()->MovePoint(0, x,y,SacrificeCirclePos[2]); + sacrificePhase=SACRIFICE_IN_TRANSIT; + // wait for movement inform + } + break; + case SACRIFICE_ASCEND : + { + DoCast(m_creature, SPELL_SPHERE_VISUAL); + m_creature->GetMotionMaster()->MovePoint(1, x,y,z); + sacrificePhase=SACRIFICE_IN_TRANSIT; + // wait for movement inform + } + break; + case SACRIFICE_CALL : // call for a sacrifice + { + // summon sacrifice bunny + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + m_creature->GetMotionMaster()->Clear(true); + // TODO TEMPSUMMON_TIMED_DESPAWN? + Creature * target = m_creature->SummonCreature(ENTRY_JEDOGA_CONTROLLER,SacrificeCirclePos[0],SacrificeCirclePos[1],SacrificeCirclePos[2],5.7f,TEMPSUMMON_TIMED_DESPAWN,30000); + if (target) + uiSacrificeSpellVisualTarget=target->GetGUID(); + DoScriptText(SAY_C_SACRIFICE_1, m_creature); + uiWaitTimer=1000; + sacrificePhase=SACRIFICE_CHOOSE; + } + break; + case SACRIFICE_CHOOSE : // choose target + { + m_creature->GetMotionMaster()->Clear(true); + DoScriptText(SAY_C_SACRIFICE_2, m_creature); + // TODO - Find purple targeting beam spell visual and cast on target + Creature * victim = Unit::GetCreature(*m_creature, auiSacrifices[auiChosen[uiSacrificeIndex-1]]); + CAST_AI(mob_twilight_volunteer, (victim->AI()))->SetMode(MODE_VOLUNTEER_CHOSEN); + sacrificePhase=SACRIFICE_IN_TRANSIT; + } + break; + case SACRIFICE_ARRIVED : // auiChosen arrived at circle + { + DoScriptText(SAY_SACRIFICE_2, m_creature); + uiWaitTimer=1000; + sacrificePhase=SACRIFICE_OFFERED; + } + break; + case SACRIFICE_OFFERED : // killed in the name of yogg saron + { + DoScriptText(SAY_SACRIFICE_1, m_creature); + // TODO - Find black life draining beam spell and cast on target to kill them by draining life + DoCast(m_creature,SPELL_SACRIFICE_BEAM); + Creature * c = Creature::GetCreature(*m_creature, auiSacrifices[auiChosen[uiSacrificeIndex-1]]); + if (c) + c->ForcedDespawn(); + c = Creature::GetCreature(*m_creature,uiSacrificeSpellVisualTarget); + if (c) + c->ForcedDespawn(); + } + // n.b. there should be no break here, this must fall into the next case + case SACRIFICE_DESCEND : + m_creature->RemoveAurasDueToSpell(SPELL_SPHERE_VISUAL); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + auiSacrifices[auiChosen[uiSacrificeIndex-1]]=0; + sacrificePhase=SACRIFICE_START; + bInSacrifice=false; + break; + case SACRIFICE_IN_TRANSIT : ; // Do nothing, waiting for MovementInform + } + return; + } + } + + // start sacrifice event at 75%, 50%, 25% + if (uiSacrificeIndex<4 && m_creature->GetHealth() < m_creature->GetMaxHealth() * (3 - uiSacrificeIndex) / 4) + { + bInSacrifice=true; + uiSacrificeIndex++; + sacrificePhase=SACRIFICE_START; + uiWaitTimer=1000; + return; + } + + if (uiCycloneStrikeTimer < diff) + { + DoCast(m_creature, bHeroicMode ? H_SPELL_CYCLONE_STRIKE : SPELL_CYCLONE_STRIKE); + uiCycloneStrikeTimer=20000 + (rand() % 5) * 1000; + } else uiCycloneStrikeTimer -= diff; + + if (uiLightningBoltTimer < diff) + { + Unit * target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (target) + DoCast(target, bHeroicMode ? H_SPELL_LIGHTNING_BOLT : SPELL_LIGHTNING_BOLT); + uiLightningBoltTimer=20000 + (rand() % 5) * 1000; + } else uiLightningBoltTimer -= diff; + + if (uiThunderShockTimer < diff) + { + Unit * target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (target) + DoCast(target, bHeroicMode ? H_SPELL_THUNDERSHOCK : SPELL_THUNDERSHOCK); + uiThunderShockTimer=30000 + (rand() % 5) * 1000; + } else uiThunderShockTimer -= diff; + DoMeleeAttackIfReady(); } + void MovementInform(uint32 type, uint32 id) + { + switch (id) + { + case 0 : sacrificePhase=SACRIFICE_ASCEND; break; + case 1 : sacrificePhase=SACRIFICE_CALL; break; + default : ; + } + } + + void JustSummoned(Creature * creature) + { + if (creature->GetEntry() == ENTRY_JEDOGA_CONTROLLER) + { + creature->AI()->DoCast(creature, SPELL_SACRIFICE_VISUAL); + } + } + + void ChosenUpdate(bool arrived) + { + sacrificePhase = arrived ? SACRIFICE_ARRIVED : SACRIFICE_DESCEND; + } + + void preach(const uint32 diff) + { + if (uiPreachTimer < diff) + { + switch((uiPreachIndex++) % 5) + { + case 0: DoScriptText(SAY_PREACHING_1, m_creature);break; + case 1: DoScriptText(SAY_PREACHING_2, m_creature);break; + case 2: DoScriptText(SAY_PREACHING_3, m_creature);break; + case 3: DoScriptText(SAY_PREACHING_4, m_creature);break; + case 4: DoScriptText(SAY_PREACHING_5, m_creature);break; + default : ; + } + uiPreachTimer=60000; + } else uiPreachTimer -= diff; + } + + void DespawnAllVolunteers() + { + for(uint8 i=0; iDestroyForNearbyPlayers(); + CAST_SUM(sacrifice)->UnSummon(); + auiSacrifices[i] = 0; + } + } + for(uint8 i=0; iDestroyForNearbyPlayers(); + CAST_SUM(volunteer)->UnSummon(); + auiVolunteers[i] = 0; + } + } + } + + void SummonVolunteers() + { + for(uint8 i=0; iisDead()) + volunteer = m_creature->SummonCreature(ENTRY_VOLUNTEER,VolunteerPos[i][0],VolunteerPos[i][1],VolunteerPos[i][2],VolunteerPos[i][3],TEMPSUMMON_CORPSE_TIMED_DESPAWN,300000); + if (volunteer) + auiVolunteers[i] = volunteer->GetGUID(); + else + auiVolunteers[i] = 0; + } + } + + void SummonSacrifices() + { + for(uint8 i=0; iisDead()) + sacrifice = m_creature->SummonCreature(ENTRY_VOLUNTEER,SacrificePos[i][0],SacrificePos[i][1],SacrificePos[i][2],SacrificePos[i][3],TEMPSUMMON_CORPSE_TIMED_DESPAWN,300000); + if (sacrifice) + { + auiSacrifices[i] = sacrifice->GetGUID(); + CAST_AI(mob_twilight_volunteer, (sacrifice->AI()))->SetMode(MODE_VOLUNTEER_SACRIFICE); + } + else + auiSacrifices[i] = 0; + } + } + void JustDied(Unit* killer) { DoScriptText(SAY_DEATH, m_creature); + DespawnAllVolunteers(); } void KilledUnit(Unit *victim) @@ -94,16 +530,58 @@ } }; +void mob_twilight_volunteer::MovementInform(uint32 type, uint32 id) +{ + if (id==0) { + Creature* jedoga = Unit::GetCreature(*m_creature, pInstance ? pInstance->GetData64(DATA_JEDOGA_SHADOWSEEKER) : 0); + CAST_AI(boss_jedogaAI, (jedoga->AI()))->ChosenUpdate(true); + } +} + +void mob_twilight_volunteer::JustDied(Unit* killer) +{ + Creature* jedoga = Unit::GetCreature(*m_creature, pInstance ? pInstance->GetData64(DATA_JEDOGA_SHADOWSEEKER) : 0); + CAST_AI(boss_jedogaAI, (jedoga->AI()))->ChosenUpdate(false); +} + +struct TRINITY_DLL_DECL mob_jedoga_controller : public ScriptedAI +{ + mob_jedoga_controller(Creature *c) : ScriptedAI(c) {} + + void Reset() {} + void UpdateAI(const uint32 diff) {} + void JustDied(Unit* killer) {} +}; + + CreatureAI* GetAI_boss_jedoga(Creature* pCreature) { return new boss_jedogaAI (pCreature); } +CreatureAI* GetAI_mob_twilight_volunteer(Creature* pCreature) +{ + return new mob_twilight_volunteer (pCreature); +} +CreatureAI* GetAI_mob_jedoga_controller(Creature* pCreature) +{ + return new mob_jedoga_controller (pCreature); +} void AddSC_boss_jedoga() { Script *newscript; newscript = new Script; + newscript->Name="mob_jedoga_controller"; + newscript->GetAI = &GetAI_mob_jedoga_controller; + newscript->RegisterSelf(); + + newscript = new Script; + newscript->Name="mob_twilight_volunteer"; + newscript->GetAI = &GetAI_mob_twilight_volunteer; + newscript->RegisterSelf(); + + newscript = new Script; newscript->Name="boss_jedoga"; newscript->GetAI = &GetAI_boss_jedoga; newscript->RegisterSelf(); diff -r 3e5b2f720126 src/bindings/scripts/system/ScriptLoader.cpp --- a/src/bindings/scripts/system/ScriptLoader.cpp Mon Sep 21 13:12:55 2009 +1000 +++ b/src/bindings/scripts/system/ScriptLoader.cpp Mon Sep 21 13:42:00 2009 +1000 @@ -263,6 +263,7 @@ //northrend extern void AddSC_instance_ahnkahet(); //Azjol-Nerub Ahn'kahet extern void AddSC_boss_taldaram(); +extern void AddSC_boss_jedoga(); extern void AddSC_boss_elder_nadox(); extern void AddSC_boss_volazj(); extern void AddSC_boss_anubrekhan(); //Naxxramas @@ -665,6 +666,7 @@ //northrend AddSC_instance_ahnkahet(); //Azjol-Nerub Ahn'kahet AddSC_boss_taldaram(); + AddSC_boss_jedoga(); AddSC_boss_elder_nadox(); AddSC_boss_volazj(); AddSC_boss_anubrekhan(); //Naxxramas