TrinityCore
Loading...
Searching...
No Matches
boss_krikthir_the_gatewatcher.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/*
19 * Comment: Find in the future best timers and the event is not implemented.
20 */
21
22#include "ScriptMgr.h"
23#include "azjol_nerub.h"
24#include "Containers.h"
25#include "InstanceScript.h"
26#include "PassiveAI.h"
27#include "ScriptedCreature.h"
28#include "SpellAuras.h"
29#include "SpellScript.h"
30#include "TemporarySummon.h"
31
33{
34 // Krik'thir the Gatewatcher
39
40 // Watchers - Shared
43
44 // Watcher Gashra
46 // Watcher Narjil
48 // Watcher Silthik
50
51 // Anubar Skirmisher
54
55 // Anubar Shadowcaster
58
59 // Anubar Warrior
62};
63
65{
66 // Krik'thir the Gatewatcher
68 SPELL_SWARM = 52440,
71 SPELL_FRENZY = 28747,
72
73 // Watchers - Shared
77
78 // Watcher Gashra
79 SPELL_ENRAGE = 52470,
80 // Watcher Narjil
82 // Watcher Silthik
84
85 // Anub'ar Warrior
86 SPELL_CLEAVE = 49806,
87 SPELL_STRIKE = 52532,
88
89 // Anub'ar Skirmisher
90 SPELL_CHARGE = 52538,
94
95 // Anub'ar Shadowcaster
98
99 // Skittering Infector
100 SPELL_ACID_SPLASH = 52446
102
104{
107
117
127
128struct boss_krik_thir : public BossAI
129{
131
133 {
135 return;
136
137 for (uint8 i = 1; i <= 3; ++i)
138 {
139 std::list<TempSummon*> adds;
140 me->SummonCreatureGroup(i, &adds);
141 for (TempSummon* add : adds)
142 add->AI()->SetData(DATA_PET_GROUP, i);
143 }
144 }
145
146 void Reset() override
147 {
149 _hadFrenzy = false;
150 _petsInCombat = false;
151 _watchersActive = 0;
153 }
154
155 void JustAppeared() override
156 {
158 SummonAdds();
159 }
160
161 void KilledUnit(Unit* victim) override
162 {
163 if (victim->GetTypeId() == TYPEID_PLAYER)
164 Talk(SAY_SLAY);
165 }
166
167 void JustDied(Unit* /*killer*/) override
168 {
169 summons.clear();
170 _JustDied();
172 }
173
186
187 void MoveInLineOfSight(Unit* who) override
188 {
190 {
192 return;
193 }
194
195 if (me->CanStartAttack(who, false) && me->IsWithinDistInMap(who, me->GetAttackDistance(who) + me->m_CombatDistance))
196 JustEngagedWith(who);
197 }
198
199 void EnterEvadeMode(EvadeReason /*why*/) override
200 {
203 }
204
205 void DoAction(int32 action) override
206 {
207 switch (action)
208 {
211 {
214 }
215 break;
219 if (!_watchersActive) // something is wrong
220 {
222 return;
223 }
224 if (!--_watchersActive) // if there are no watchers currently in combat...
225 events.RescheduleEvent(EVENT_SEND_GROUP, Seconds(5)); // ...send the next watcher after the targets sooner
226 break;
229 break;
231 if (_petsInCombat || me->IsInCombat())
232 break;
233 _petsInCombat = true;
236 break;
237 case ACTION_PET_EVADE:
239 break;
240 }
241 }
242
243 void UpdateAI(uint32 diff) override
244 {
245 if (!UpdateVictim() && !_petsInCombat)
246 return;
247
248 events.Update(diff);
249
251 return;
252
253 if (me->HealthBelowPct(10) && !_hadFrenzy)
254 {
255 _hadFrenzy = true;
257 }
258
259 while (uint32 eventId = events.ExecuteEvent())
260 {
261 switch (eventId)
262 {
263 case EVENT_SEND_GROUP:
265 events.Repeat(Seconds(70));
266 break;
267
268 case EVENT_SWARM:
271 break;
272
273 case EVENT_MIND_FLAY:
276 break;
277
278 case EVENT_FRENZY:
281 events.Repeat(Seconds(15));
282 break;
283 }
284
286 return;
287 }
288 }
289
290 void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
291 {
292 if (spellInfo->Id == SPELL_SUBBOSS_AGGRO_TRIGGER)
294 }
295
296 void SpellHitTarget(WorldObject* /*target*/, SpellInfo const* spellInfo) override
297 {
298 if (spellInfo->Id == SPELL_SUBBOSS_AGGRO_TRIGGER)
300 }
301
302 private:
306};
307
309{
310 npc_gatewatcher_petAI(Creature* creature, bool isWatcher) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), _petGroup(0), _isWatcher(isWatcher) { }
311
312 virtual void _JustEngagedWith() = 0;
313 void JustEngagedWith(Unit* who) override
314 {
315 if (_isWatcher)
316 {
317 _isWatcher = false;
318 if (TempSummon* meSummon = me->ToTempSummon())
319 if (Creature* summoner = meSummon->GetSummonerCreatureBase())
320 summoner->AI()->DoAction(ACTION_WATCHER_ENGAGED);
321 }
322
324 {
325 std::list<Creature*> others;
326 me->GetCreatureListWithEntryInGrid(others, 0, 40.0f);
327 for (Creature* other : others)
328 if (other->AI()->GetData(DATA_PET_GROUP) == _petGroup)
329 {
330 other->SetReactState(REACT_AGGRESSIVE);
331 other->AI()->AttackStart(who);
332 }
333
334 if (TempSummon* meSummon = me->ToTempSummon())
335 if (Creature* summoner = meSummon->GetSummonerCreatureBase())
336 summoner->AI()->DoAction(ACTION_PET_ENGAGED);
337 }
340 }
341
342 void SetData(uint32 data, uint32 value) override
343 {
344 if (data == DATA_PET_GROUP)
345 {
346 _petGroup = value;
348 }
349 }
350
351 uint32 GetData(uint32 data) const override
352 {
353 if (data == DATA_PET_GROUP)
354 return _petGroup;
355 return 0;
356 }
357
358 void MoveInLineOfSight(Unit* who) override
359 {
361 {
363 return;
364 }
365
366 if (me->CanStartAttack(who, false) && me->IsWithinDistInMap(who, me->GetAttackDistance(who) + me->m_CombatDistance))
367 JustEngagedWith(who);
368 }
369
370 void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override
371 {
372 if (spellInfo->Id == SPELL_SUBBOSS_AGGRO_TRIGGER)
374 }
375
376 void EnterEvadeMode(EvadeReason why) override
377 {
378 if (TempSummon* meSummon = me->ToTempSummon())
379 {
380 if (Creature* summoner = meSummon->GetSummonerCreatureBase())
381 summoner->AI()->DoAction(ACTION_PET_EVADE);
382 else
384 return;
385 }
387 }
388
393};
394
396{
397 npc_watcher_gashra(Creature* creature) : npc_gatewatcher_petAI(creature, true) { }
398
399 void Reset() override
400 {
401 _events.Reset();
402 }
403
410
411 void JustDied(Unit* /*killer*/) override
412 {
414 if (krikthir && krikthir->IsAlive())
415 krikthir->AI()->DoAction(ACTION_GASHRA_DIED);
416 }
417
418 void UpdateAI(uint32 diff) override
419 {
420 if (!UpdateVictim())
421 return;
422
423 _events.Update(diff);
424
426 return;
427
428 while (uint32 eventId = _events.ExecuteEvent())
429 {
430 switch (eventId)
431 {
432 case EVENT_ENRAGE:
435 break;
436 case EVENT_WEB_WRAP:
437 if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f))
438 DoCast(target, SPELL_WEB_WRAP);
440 break;
444 break;
445 default:
446 break;
447 }
448
450 return;
451 }
452 }
453
454 private:
456};
457
459{
461 {
462 }
463
464 void Reset() override
465 {
466 _events.Reset();
467 }
468
475
476 void JustDied(Unit* /*killer*/) override
477 {
479 if (krikthir && krikthir->IsAlive())
480 krikthir->AI()->DoAction(ACTION_NARJIL_DIED);
481 }
482
483 void UpdateAI(uint32 diff) override
484 {
485 if (!UpdateVictim())
486 return;
487
488 _events.Update(diff);
489
491 return;
492
493 while (uint32 eventId = _events.ExecuteEvent())
494 {
495 switch (eventId)
496 {
500 break;
501 case EVENT_WEB_WRAP:
502 if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
503 DoCast(target, SPELL_WEB_WRAP);
505 break;
509 break;
510 default:
511 break;
512 }
513
515 return;
516 }
517 }
518
519 private:
521};
522
524{
526 {
527 }
528
529 void Reset() override
530 {
531 _events.Reset();
532 }
533
540
541 void JustDied(Unit* /*killer*/) override
542 {
544 if (krikthir && krikthir->IsAlive())
545 krikthir->AI()->DoAction(ACTION_SILTHIK_DIED);
546 }
547
548 void UpdateAI(uint32 diff) override
549 {
550 if (!UpdateVictim())
551 return;
552
553 _events.Update(diff);
554
556 return;
557
558 while (uint32 eventId = _events.ExecuteEvent())
559 {
560 switch (eventId)
561 {
565 break;
566 case EVENT_WEB_WRAP:
567 if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
568 DoCast(target, SPELL_WEB_WRAP);
570 break;
574 break;
575 default:
576 break;
577 }
578
580 return;
581 }
582 }
583
584 private:
586};
587
589{
590 npc_anub_ar_warrior(Creature* creature) : npc_gatewatcher_petAI(creature, false) { }
591
592 void Reset() override
593 {
594 _events.Reset();
595 }
596
602
603 void UpdateAI(uint32 diff) override
604 {
605 if (!UpdateVictim())
606 return;
607
608 _events.Update(diff);
609
611 return;
612
613 while (uint32 eventId = _events.ExecuteEvent())
614 {
615 switch (eventId)
616 {
617 case EVENT_CLEAVE:
620 break;
621 case EVENT_STRIKE:
624 break;
625 default:
626 break;
627 }
628
630 return;
631 }
632 }
633};
634
636{
637 npc_anub_ar_skirmisher(Creature* creature) : npc_gatewatcher_petAI(creature, false) { }
638
639 void Reset() override
640 {
641 _events.Reset();
642 }
643
649
650 void UpdateAI(uint32 diff) override
651 {
652 if (!UpdateVictim())
653 return;
654
655 _events.Update(diff);
656
658 return;
659
660 while (uint32 eventId = _events.ExecuteEvent())
661 {
662 switch (eventId)
663 {
665 if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
666 DoCast(target, SPELL_CHARGE);
668 break;
669 case EVENT_BACKSTAB:
670 if (me->GetVictim() && me->GetVictim()->isInBack(me))
673 break;
674 default:
675 break;
676 }
677
679 return;
680 }
681 }
682
683 void SpellHitTarget(WorldObject* target, SpellInfo const* spellInfo) override
684 {
685 Unit* unitTarget = target->ToUnit();
686 if (!unitTarget)
687 return;
688
689 if (spellInfo->Id == SPELL_CHARGE)
690 DoCast(unitTarget, SPELL_FIXATE_TRIGGER);
691 }
692};
693
695{
697
698 void Reset() override
699 {
700 _events.Reset();
701 }
702
708
709 void UpdateAI(uint32 diff) override
710 {
711 if (!UpdateVictim())
712 return;
713
714 _events.Update(diff);
715
717 return;
718
719 while (uint32 eventId = _events.ExecuteEvent())
720 {
721 switch (eventId)
722 {
724 if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
725 DoCast(target, SPELL_SHADOW_BOLT);
727 break;
731 break;
732 default:
733 break;
734 }
735
737 return;
738 }
739 }
740};
741
743{
744 npc_skittering_swarmer(Creature* creature) : ScriptedAI(creature) { }
745
746 void InitializeAI() override
747 {
749 if (Creature* gatewatcher = me->GetInstanceScript()->GetCreature(DATA_KRIKTHIR))
750 {
751 if (Unit* target = gatewatcher->getAttackerForHelper())
752 AttackStart(target);
753 gatewatcher->AI()->JustSummoned(me);
754 }
755 }
756};
757
759{
760 npc_skittering_infector(Creature* creature) : ScriptedAI(creature) { }
761
762 void InitializeAI() override
763 {
765 if (Creature* gatewatcher = me->GetInstanceScript()->GetCreature(DATA_KRIKTHIR))
766 {
767 if (Unit* target = gatewatcher->getAttackerForHelper())
768 AttackStart(target);
769 gatewatcher->AI()->JustSummoned(me);
770 }
771 }
772
773 void JustDied(Unit* killer) override
774 {
776 ScriptedAI::JustDied(killer);
777 }
778};
779
781{
783
784 void JustDied(Unit* /*killer*/) override
785 {
786 if (TempSummon* meSummon = me->ToTempSummon())
787 if (Unit* summoner = meSummon->GetSummonerUnit())
788 summoner->RemoveAurasDueToSpell(SPELL_WEB_WRAP_WRAPPED);
789 }
790};
791
792// 52343 - Krik'Thir Subboss Aggro Trigger
794{
795 void HandleTargets(std::list<WorldObject*>& targetList)
796 {
797 // Remove any Watchers that are already in combat
798 auto it = targetList.begin();
799 while (it != targetList.end())
800 {
801 if (Creature* creature = (*it)->ToCreature())
802 if (creature->IsAlive() && !creature->IsInCombat())
803 {
804 ++it;
805 continue;
806 }
807 it = targetList.erase(it);
808 }
809
810 // Default to Krik'thir himself if he isn't engaged
811 WorldObject* target = nullptr;
812 if (GetCaster() && !GetCaster()->IsInCombat())
813 target = GetCaster();
814 // Unless there are Watchers that aren't engaged yet
815 if (!targetList.empty())
816 {
817 // If there are, pick one of them at random
819 }
820 // And hit only that one
821 targetList.clear();
822 if (target)
823 targetList.push_back(target);
824 }
825
830};
831
832// 52536 - Fixate Trigger
834{
835 bool Validate(SpellInfo const* /*spell*/) override
836 {
838 }
839
840 void HandleScript(SpellEffIndex /*effIndex*/)
841 {
842 if (Unit* target = GetHitUnit())
843 target->CastSpell(GetCaster(), SPELL_FIXATE_TRIGGERED, true);
844 }
845
850};
851
852// 52086 - Web Wrap
854{
855 bool Validate(SpellInfo const* /*spell*/) override
856 {
858 }
859
860 void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
861 {
862 if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE)
863 return;
864
865 if (Unit* target = GetTarget())
866 target->CastSpell(target, SPELL_WEB_WRAP_WRAPPED, true);
867 }
868
873};
874
876{
877 public:
878 achievement_watch_him_die() : AchievementCriteriaScript("achievement_watch_him_die") { }
879
880 bool OnCheck(Player* /*player*/, Unit* target) override
881 {
882 if (!target)
883 return false;
884
885 InstanceScript* instance = target->GetInstanceScript();
886 if (!instance)
887 return false;
888
890 {
891 if (Creature* watcher = instance->GetCreature(watcherData))
892 if (watcher->IsAlive())
893 continue;
894 return false;
895 }
896
897 return true;
898 }
899};
900
uint8_t uint8
Definition Define.h:156
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
std::chrono::seconds Seconds
Seconds shorthand typedef.
Definition Duration.h:28
@ DONE
@ TYPEID_PLAYER
Definition ObjectGuid.h:44
Spells
Definition PlayerAI.cpp:32
Milliseconds randtime(Milliseconds min, Milliseconds max)
Definition Random.cpp:62
#define RegisterSpellScript(spell_script)
Definition ScriptMgr.h:1383
SpellEffIndex
@ EFFECT_0
@ TARGET_UNIT_SRC_AREA_ENTRY
@ SPELL_EFFECT_SCRIPT_EFFECT
AuraEffectHandleModes
@ AURA_EFFECT_HANDLE_REAL
@ AURA_REMOVE_BY_EXPIRE
@ SPELL_AURA_MOD_ROOT
#define SpellEffectFn(F, I, N)
#define SpellObjectAreaTargetSelectFn(F, I, N)
#define AuraEffectRemoveFn(F, I, N, M)
EvadeReason
@ REACT_PASSIVE
@ REACT_AGGRESSIVE
@ UNIT_STATE_CASTING
Definition Unit.h:276
ANDataTypes
Definition azjol_nerub.h:29
@ DATA_KRIKTHIR
Definition azjol_nerub.h:31
@ DATA_GATEWATCHER_GREET
Definition azjol_nerub.h:41
@ DATA_WATCHER_SILTHIK
Definition azjol_nerub.h:38
@ DATA_WATCHER_GASHRA
Definition azjol_nerub.h:37
@ DATA_WATCHER_NARJIL
Definition azjol_nerub.h:36
#define RegisterAzjolNerubCreatureAI(ai_name)
Definition azjol_nerub.h:75
@ ACTION_GATEWATCHER_GREET
Definition azjol_nerub.h:66
void AddSC_boss_krik_thir()
Yells
AuraApplication const * GetTargetApplication() const
Unit * GetTarget() const
HookList< EffectApplyHandler > OnEffectRemove
TypeID GetTypeId() const
Definition BaseEntity.h:166
InstanceScript *const instance
void JustEngagedWith(Unit *who) override
void _DespawnAtEvade(Seconds delayToRespawn=30s, Creature *who=nullptr)
SummonList summons
EventMap events
void Reset() override
virtual void MoveInLineOfSight(Unit *)
virtual void JustEngagedWith(Unit *)
Definition CreatureAI.h:101
void DoZoneInCombat()
Definition CreatureAI.h:169
virtual void JustDied(Unit *)
Definition CreatureAI.h:107
virtual void EnterEvadeMode(EvadeReason why=EvadeReason::Other)
virtual void JustAppeared()
bool UpdateVictim()
Creature *const me
Definition CreatureAI.h:63
float GetAttackDistance(Unit const *player) const
bool HasReactState(ReactStates state) const
Definition Creature.h:176
void SetReactState(ReactStates st)
Definition Creature.h:174
void DespawnOrUnsummon(Milliseconds timeToDespawn=0s, Seconds forceRespawnTime=0s)
float m_CombatDistance
Definition Creature.h:427
CreatureAI * AI() const
Definition Creature.h:228
bool CanStartAttack(Unit const *u, bool force) const
uint32 ExecuteEvent()
Definition EventMap.cpp:77
void Update(uint32 time)
Definition EventMap.h:61
void Repeat(Milliseconds time)
Definition EventMap.cpp:67
void ScheduleEvent(uint32 eventId, Milliseconds time, uint32 group=0, uint8 phase=0)
Definition EventMap.cpp:40
void CancelEvent(uint32 eventId)
Definition EventMap.cpp:135
void RescheduleEvent(uint32 eventId, Milliseconds time, uint32 group=0, uint8 phase=0)
Definition EventMap.cpp:56
void Reset()
Definition EventMap.cpp:25
Creature * GetCreature(uint32 type)
EncounterState GetBossState(uint32 id) const
Creature * ToCreature()
Definition Object.h:121
Unit * ToUnit()
Definition Object.h:116
uint32 const Id
Definition SpellInfo.h:328
static bool ValidateSpellInfo(std::initializer_list< uint32 > spellIds)
Unit * GetCaster() const
Unit * GetHitUnit() const
HookList< EffectHandler > OnEffectHitTarget
HookList< ObjectAreaTargetSelectHandler > OnObjectAreaTargetSelect
void DoZoneInCombat(uint32 entry=0)
SpellCastResult DoCastSelf(uint32 spellId, CastSpellExtraArgs const &args={})
Definition UnitAI.h:160
virtual void InitializeAI()
Definition UnitAI.cpp:43
virtual void DoAction(int32 param)
Definition UnitAI.h:73
SpellCastResult DoCastVictim(uint32 spellId, CastSpellExtraArgs const &args={})
Definition UnitAI.cpp:180
Unit * SelectTarget(SelectTargetMethod targetType, uint32 offset=0, float dist=0.0f, bool playerOnly=false, bool withTank=true, int32 aura=0)
Definition UnitAI.cpp:79
SpellCastResult DoCastAOE(uint32 spellId, CastSpellExtraArgs const &args={})
Definition UnitAI.h:162
SpellCastResult DoCast(uint32 spellId)
Definition UnitAI.cpp:89
Definition Unit.h:635
Unit * getAttackerForHelper() const
Definition Unit.cpp:5830
bool IsAlive() const
Definition Unit.h:1185
TempSummon * ToTempSummon()
Definition Unit.h:1828
Unit * GetVictim() const
Definition Unit.h:726
bool HealthBelowPct(float pct) const
Definition Unit.h:792
bool HasUnitState(const uint32 f) const
Definition Unit.h:743
bool IsInCombat() const
Definition Unit.h:1058
InstanceScript * GetInstanceScript() const
Definition Object.cpp:396
void GetCreatureListWithEntryInGrid(Container &creatureContainer, uint32 entry, float maxSearchRange=250.0f) const
Definition Object.cpp:2658
bool isInBack(WorldObject const *target, float arc=float(M_PI)) const
Definition Object.cpp:675
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition Object.cpp:501
void SummonCreatureGroup(uint8 group, std::list< TempSummon * > *list=nullptr)
Definition Object.cpp:1507
virtual uint32 GetData(uint32) const
Definition ZoneScript.h:99
virtual void SetData(uint32, uint32)
Definition ZoneScript.h:100
bool OnCheck(Player *, Unit *target) override
bool Validate(SpellInfo const *) override
void HandleTargets(std::list< WorldObject * > &targetList)
bool Validate(SpellInfo const *) override
void HandleEffectRemove(AuraEffect const *, AuraEffectHandleModes)
auto SelectRandomContainerElement(C const &container) -> std::add_const_t< decltype(*std::ranges::begin(container))> &
Definition Containers.h:110
void AttackStart(Unit *) override
== Triggered Actions Requested ==================
void EnterEvadeMode(EvadeReason) override
void MoveInLineOfSight(Unit *who) override
void UpdateAI(uint32 diff) override
void SpellHit(WorldObject *, SpellInfo const *spellInfo) override
void KilledUnit(Unit *victim) override
void JustDied(Unit *) override
void JustEngagedWith(Unit *who) override
void SpellHitTarget(WorldObject *, SpellInfo const *spellInfo) override
void DoAction(int32 action) override
boss_krik_thir(Creature *creature)
void UpdateAI(uint32 diff) override
void SpellHitTarget(WorldObject *target, SpellInfo const *spellInfo) override
void UpdateAI(uint32 diff) override
void JustEngagedWith(Unit *who) override
void EnterEvadeMode(EvadeReason why) override
npc_gatewatcher_petAI(Creature *creature, bool isWatcher)
void SetData(uint32 data, uint32 value) override
virtual void _JustEngagedWith()=0
void SpellHit(WorldObject *, SpellInfo const *spellInfo) override
void MoveInLineOfSight(Unit *who) override
uint32 GetData(uint32 data) const override
void JustDied(Unit *killer) override
void UpdateAI(uint32 diff) override
void UpdateAI(uint32 diff) override
void UpdateAI(uint32 diff) override