Core/Spells: Implemented personal summons (#19231)
[trinitycore] / src / server / game / Entities / Creature / TemporarySummon.cpp
1 /*
2 * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
3 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "TemporarySummon.h"
20 #include "CreatureAI.h"
21 #include "DB2Structure.h"
22 #include "Log.h"
23 #include "Map.h"
24 #include "ObjectAccessor.h"
25 #include "Pet.h"
26 #include "Player.h"
27
28 TempSummon::TempSummon(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject) :
29 Creature(isWorldObject), m_Properties(properties), m_type(TEMPSUMMON_MANUAL_DESPAWN),
30 m_timer(0), m_lifetime(0), m_visibleBySummonerOnly(false)
31 {
32 if (owner)
33 m_summonerGUID = owner->GetGUID();
34
35 m_unitTypeMask |= UNIT_MASK_SUMMON;
36 }
37
38 Unit* TempSummon::GetSummoner() const
39 {
40 return !m_summonerGUID.IsEmpty() ? ObjectAccessor::GetUnit(*this, m_summonerGUID) : NULL;
41 }
42
43 Creature* TempSummon::GetSummonerCreatureBase() const
44 {
45 return !m_summonerGUID.IsEmpty() ? ObjectAccessor::GetCreature(*this, m_summonerGUID) : NULL;
46 }
47
48 void TempSummon::Update(uint32 diff)
49 {
50 Creature::Update(diff);
51
52 if (m_deathState == DEAD)
53 {
54 UnSummon();
55 return;
56 }
57 switch (m_type)
58 {
59 case TEMPSUMMON_MANUAL_DESPAWN:
60 break;
61 case TEMPSUMMON_TIMED_DESPAWN:
62 {
63 if (m_timer <= diff)
64 {
65 UnSummon();
66 return;
67 }
68
69 m_timer -= diff;
70 break;
71 }
72 case TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT:
73 {
74 if (!IsInCombat())
75 {
76 if (m_timer <= diff)
77 {
78 UnSummon();
79 return;
80 }
81
82 m_timer -= diff;
83 }
84 else if (m_timer != m_lifetime)
85 m_timer = m_lifetime;
86
87 break;
88 }
89
90 case TEMPSUMMON_CORPSE_TIMED_DESPAWN:
91 {
92 if (m_deathState == CORPSE)
93 {
94 if (m_timer <= diff)
95 {
96 UnSummon();
97 return;
98 }
99
100 m_timer -= diff;
101 }
102 break;
103 }
104 case TEMPSUMMON_CORPSE_DESPAWN:
105 {
106 // if m_deathState is DEAD, CORPSE was skipped
107 if (m_deathState == CORPSE || m_deathState == DEAD)
108 {
109 UnSummon();
110 return;
111 }
112
113 break;
114 }
115 case TEMPSUMMON_DEAD_DESPAWN:
116 {
117 if (m_deathState == DEAD)
118 {
119 UnSummon();
120 return;
121 }
122 break;
123 }
124 case TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN:
125 {
126 // if m_deathState is DEAD, CORPSE was skipped
127 if (m_deathState == CORPSE || m_deathState == DEAD)
128 {
129 UnSummon();
130 return;
131 }
132
133 if (!IsInCombat())
134 {
135 if (m_timer <= diff)
136 {
137 UnSummon();
138 return;
139 }
140 else
141 m_timer -= diff;
142 }
143 else if (m_timer != m_lifetime)
144 m_timer = m_lifetime;
145 break;
146 }
147 case TEMPSUMMON_TIMED_OR_DEAD_DESPAWN:
148 {
149 // if m_deathState is DEAD, CORPSE was skipped
150 if (m_deathState == DEAD)
151 {
152 UnSummon();
153 return;
154 }
155
156 if (!IsInCombat() && IsAlive())
157 {
158 if (m_timer <= diff)
159 {
160 UnSummon();
161 return;
162 }
163 else
164 m_timer -= diff;
165 }
166 else if (m_timer != m_lifetime)
167 m_timer = m_lifetime;
168 break;
169 }
170 default:
171 UnSummon();
172 TC_LOG_ERROR("entities.unit", "Temporary summoned creature (entry: %u) have unknown type %u of ", GetEntry(), m_type);
173 break;
174 }
175 }
176
177 void TempSummon::InitStats(uint32 duration)
178 {
179 ASSERT(!IsPet());
180
181 m_timer = duration;
182 m_lifetime = duration;
183
184 if (m_type == TEMPSUMMON_MANUAL_DESPAWN)
185 m_type = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
186
187 Unit* owner = GetSummoner();
188
189 if (owner && IsTrigger() && m_spells[0])
190 {
191 setFaction(owner->getFaction());
192 SetLevel(owner->getLevel());
193 if (owner->GetTypeId() == TYPEID_PLAYER)
194 m_ControlledByPlayer = true;
195 }
196
197 if (!m_Properties)
198 return;
199
200 if (owner)
201 {
202 int32 slot = m_Properties->Slot;
203 if (slot > 0)
204 {
205 if (!owner->m_SummonSlot[slot].IsEmpty() && owner->m_SummonSlot[slot] != GetGUID())
206 {
207 Creature* oldSummon = GetMap()->GetCreature(owner->m_SummonSlot[slot]);
208 if (oldSummon && oldSummon->IsSummon())
209 oldSummon->ToTempSummon()->UnSummon();
210 }
211 owner->m_SummonSlot[slot] = GetGUID();
212 }
213 }
214
215 if (m_Properties->Faction)
216 setFaction(m_Properties->Faction);
217 else if (IsVehicle() && owner) // properties should be vehicle
218 setFaction(owner->getFaction());
219 }
220
221 void TempSummon::InitSummon()
222 {
223 Unit* owner = GetSummoner();
224 if (owner)
225 {
226 if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsAIEnabled)
227 owner->ToCreature()->AI()->JustSummoned(this);
228 if (IsAIEnabled)
229 AI()->IsSummonedBy(owner);
230 }
231 }
232
233 void TempSummon::UpdateObjectVisibilityOnCreate()
234 {
235 WorldObject::UpdateObjectVisibility(true);
236 }
237
238 void TempSummon::SetTempSummonType(TempSummonType type)
239 {
240 m_type = type;
241 }
242
243 void TempSummon::UnSummon(uint32 msTime)
244 {
245 if (msTime)
246 {
247 ForcedUnsummonDelayEvent* pEvent = new ForcedUnsummonDelayEvent(*this);
248
249 m_Events.AddEvent(pEvent, m_Events.CalculateTime(msTime));
250 return;
251 }
252
253 //ASSERT(!IsPet());
254 if (IsPet())
255 {
256 ToPet()->Remove(PET_SAVE_NOT_IN_SLOT);
257 ASSERT(!IsInWorld());
258 return;
259 }
260
261 Unit* owner = GetSummoner();
262 if (owner && owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsAIEnabled)
263 owner->ToCreature()->AI()->SummonedCreatureDespawn(this);
264
265 AddObjectToRemoveList();
266 }
267
268 bool ForcedUnsummonDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
269 {
270 m_owner.UnSummon();
271 return true;
272 }
273
274 void TempSummon::RemoveFromWorld()
275 {
276 if (!IsInWorld())
277 return;
278
279 if (m_Properties)
280 {
281 int32 slot = m_Properties->Slot;
282 if (slot > 0)
283 if (Unit* owner = GetSummoner())
284 if (owner->m_SummonSlot[slot] == GetGUID())
285 owner->m_SummonSlot[slot].Clear();
286 }
287
288 //if (GetOwnerGUID())
289 // TC_LOG_ERROR("entities.unit", "Unit %u has owner guid when removed from world", GetEntry());
290
291 Creature::RemoveFromWorld();
292 }
293
294 Minion::Minion(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject)
295 : TempSummon(properties, owner, isWorldObject), m_owner(owner)
296 {
297 ASSERT(m_owner);
298 m_unitTypeMask |= UNIT_MASK_MINION;
299 m_followAngle = PET_FOLLOW_ANGLE;
300 /// @todo: Find correct way
301 InitCharmInfo();
302 }
303
304 void Minion::InitStats(uint32 duration)
305 {
306 TempSummon::InitStats(duration);
307
308 SetReactState(REACT_PASSIVE);
309
310 SetCreatorGUID(GetOwner()->GetGUID());
311 setFaction(GetOwner()->getFaction());
312
313 GetOwner()->SetMinion(this, true);
314 }
315
316 void Minion::RemoveFromWorld()
317 {
318 if (!IsInWorld())
319 return;
320
321 GetOwner()->SetMinion(this, false);
322 TempSummon::RemoveFromWorld();
323 }
324
325 bool Minion::IsGuardianPet() const
326 {
327 return IsPet() || (m_Properties && m_Properties->Control == SUMMON_CATEGORY_PET);
328 }
329
330 Guardian::Guardian(SummonPropertiesEntry const* properties, Unit* owner, bool isWorldObject) : Minion(properties, owner, isWorldObject)
331 , m_bonusSpellDamage(0)
332 {
333 memset(m_statFromOwner, 0, sizeof(float)*MAX_STATS);
334 m_unitTypeMask |= UNIT_MASK_GUARDIAN;
335 if (properties && (properties->Title == SUMMON_TYPE_PET || properties->Control == SUMMON_CATEGORY_PET))
336 {
337 m_unitTypeMask |= UNIT_MASK_CONTROLABLE_GUARDIAN;
338 InitCharmInfo();
339 }
340 }
341
342 void Guardian::InitStats(uint32 duration)
343 {
344 Minion::InitStats(duration);
345
346 InitStatsForLevel(GetOwner()->getLevel());
347
348 if (GetOwner()->GetTypeId() == TYPEID_PLAYER && HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
349 m_charmInfo->InitCharmCreateSpells();
350
351 SetReactState(REACT_AGGRESSIVE);
352 }
353
354 void Guardian::InitSummon()
355 {
356 TempSummon::InitSummon();
357
358 if (GetOwner()->GetTypeId() == TYPEID_PLAYER
359 && GetOwner()->GetMinionGUID() == GetGUID()
360 && !GetOwner()->GetCharmGUID())
361 {
362 GetOwner()->ToPlayer()->CharmSpellInitialize();
363 }
364 }
365
366 Puppet::Puppet(SummonPropertiesEntry const* properties, Unit* owner)
367 : Minion(properties, owner, false) //maybe true?
368 {
369 ASSERT(m_owner->GetTypeId() == TYPEID_PLAYER);
370 m_unitTypeMask |= UNIT_MASK_PUPPET;
371 }
372
373 void Puppet::InitStats(uint32 duration)
374 {
375 Minion::InitStats(duration);
376 SetLevel(GetOwner()->getLevel());
377 SetReactState(REACT_PASSIVE);
378 }
379
380 void Puppet::InitSummon()
381 {
382 Minion::InitSummon();
383 if (!SetCharmedBy(GetOwner(), CHARM_TYPE_POSSESS))
384 ABORT();
385 }
386
387 void Puppet::Update(uint32 time)
388 {
389 Minion::Update(time);
390 //check if caster is channelling?
391 if (IsInWorld())
392 {
393 if (!IsAlive())
394 {
395 UnSummon();
396 /// @todo why long distance .die does not remove it
397 }
398 }
399 }
400
401 void Puppet::RemoveFromWorld()
402 {
403 if (!IsInWorld())
404 return;
405
406 RemoveCharmedBy(NULL);
407 Minion::RemoveFromWorld();
408 }