/* ================ rvTramGate::Event_Touch ================ */ void rvTramGate::Event_Touch( idEntity* other, trace_t* trace ) { if( !IsDoorMasterValid() ) { return; } if( IsClosed() ) { OpenGate(); GetDoorMaster()->ProcessEvent( &EV_Touch, this, trace ); } else if( IsOpen() ) { GetDoorMaster()->ProcessEvent( &EV_Touch, this, trace ); } }
void DoAction(int32 action) override { switch (action) { case ACTION_MINION_EVADE: if (_gateIsOpen || me->getThreatManager().isThreatListEmpty()) return EnterEvadeMode(EVADE_REASON_NO_HOSTILES); if (_gateCanOpen) OpenGate(); break; } }
/* ================ rvTramGate::Event_OpenGate ================ */ void rvTramGate::Event_OpenGate() { OpenGate(); }
void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; if (me->HasReactState(REACT_AGGRESSIVE) && !_gateIsOpen && !IsOnSameSide(me, me->GetVictim())) { // NBD: this should only happen in practice if there is nobody left alive on our side (we should open gate) // thus we only do a cursory check to make sure (edge cases?) if (Player* newTarget = FindEligibleTarget(me, _gateIsOpen)) { me->getThreatManager().resetAllAggro(); me->AddThreat(newTarget, 1.0f); AttackStart(newTarget); } else OpenGate(); } events.Update(diff); if (!_gateIsOpen && HealthBelowPct(30) && events.IsInPhase(PHASE_TWO)) OpenGate(); while (uint32 eventId = events.ExecuteEvent()) { switch (eventId) { case EVENT_SUMMON: { if (RAID_MODE(waves10,waves25).size() <= _waveCount) // bounds check { TC_LOG_INFO("scripts", "GothikAI: Wave count %d is out of range for difficulty %d.", _waveCount, GetDifficulty()); break; } std::list<Creature*> triggers; me->GetCreatureListWithEntryInGrid(triggers, NPC_TRIGGER, 150.0f); for (GothikWaveEntry entry : RAID_MODE(waves10, waves25)[_waveCount].first) for (uint8 i = 0; i < entry.second; ++i) { // GUID layout is as follows: // CGUID+4: center (back of platform) - primary rider spawn // CGUID+5: north (back of platform) - primary knight spawn // CGUID+6: center (front of platform) - second spawn // CGUID+7: south (front of platform) - primary trainee spawn uint32 targetDBGuid; switch (entry.first) { case NPC_LIVE_RIDER: // only spawns from center (back) > north targetDBGuid = (CGUID_TRIGGER + 4) + (i % 2); break; case NPC_LIVE_KNIGHT: // spawns north > center (front) > south targetDBGuid = (CGUID_TRIGGER + 5) + (i % 3); break; case NPC_LIVE_TRAINEE: // spawns south > center (front) > north targetDBGuid = (CGUID_TRIGGER + 7) - (i % 3); break; default: targetDBGuid = 0; } for (Creature* trigger : triggers) if (trigger && trigger->GetSpawnId() == targetDBGuid) { DoSummon(entry.first, trigger, 1.0f, 15 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN); break; } } if (uint8 timeToNext = RAID_MODE(waves10, waves25)[_waveCount].second) events.ScheduleEvent(EVENT_SUMMON, timeToNext * IN_MILLISECONDS, 0, PHASE_ONE); ++_waveCount; break; } case EVENT_DOORS_UNLOCK: _gateCanOpen = true; for (ObjectGuid summonGuid : summons) if (Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid)) if (summon->IsAlive() && (!summon->IsInCombat() || summon->IsInEvadeMode())) { OpenGate(); break; } break; case EVENT_PHASE_TWO: events.SetPhase(PHASE_TWO); events.ScheduleEvent(EVENT_TELEPORT, 20 * IN_MILLISECONDS, 0, PHASE_TWO); events.ScheduleEvent(EVENT_HARVEST, 15 * IN_MILLISECONDS, 0, PHASE_TWO); events.ScheduleEvent(EVENT_RESUME_ATTACK, 2 * IN_MILLISECONDS, 0, PHASE_TWO); Talk(SAY_PHASE_TWO); Talk(EMOTE_PHASE_TWO); me->SetReactState(REACT_PASSIVE); me->getThreatManager().resetAllAggro(); DoCastAOE(SPELL_TELEPORT_LIVE); break; case EVENT_TELEPORT: if (!HealthBelowPct(30)) { me->CastStop(); me->AttackStop(); me->StopMoving(); me->SetReactState(REACT_PASSIVE); me->getThreatManager().resetAllAggro(); DoCastAOE(_lastTeleportDead ? SPELL_TELEPORT_LIVE : SPELL_TELEPORT_DEAD); _lastTeleportDead = !_lastTeleportDead; events.CancelEvent(EVENT_BOLT); events.ScheduleEvent(EVENT_TELEPORT, 20 * IN_MILLISECONDS, 0, PHASE_TWO); events.ScheduleEvent(EVENT_RESUME_ATTACK, 2 * IN_MILLISECONDS, 0, PHASE_TWO); } break; case EVENT_HARVEST: DoCastAOE(SPELL_HARVEST_SOUL, true); // triggered allows this to go "through" shadow bolt events.ScheduleEvent(EVENT_HARVEST, 15 * IN_MILLISECONDS, 0, PHASE_TWO); break; case EVENT_RESUME_ATTACK: me->SetReactState(REACT_AGGRESSIVE); events.ScheduleEvent(EVENT_BOLT, 0, 0, PHASE_TWO); // return to the start of this method so victim side etc is re-evaluated return UpdateAI(0u); // tail recursion for efficiency case EVENT_BOLT: DoCastVictim(SPELL_SHADOW_BOLT); events.ScheduleEvent(EVENT_BOLT, 1 * IN_MILLISECONDS, 0, PHASE_TWO); break; case EVENT_INTRO_2: Talk(SAY_INTRO_2); break; case EVENT_INTRO_3: Talk(SAY_INTRO_3); break; case EVENT_INTRO_4: Talk(SAY_INTRO_4); break; } } }