Core/Spells: Implemented personal summons
This commit is contained in:
@@ -1438,6 +1438,32 @@ namespace Framework.Constants
|
||||
PreventEmoteSounds = 0x1000a
|
||||
}
|
||||
|
||||
public enum SummonPropFlags
|
||||
{
|
||||
None = 0x00000000, // 1342 Spells In 3.0.3
|
||||
Unk1 = 0x00000001, // 75 Spells In 3.0.3, Something Unfriendly
|
||||
Unk2 = 0x00000002, // 616 Spells In 3.0.3, Something Friendly
|
||||
Unk3 = 0x00000004, // 22 Spells In 3.0.3, No Idea...
|
||||
Unk4 = 0x00000008, // 49 Spells In 3.0.3, Some Mounts
|
||||
PersonalSpawn = 0x00000010, // Personal Spawn (Creature Visible Only By Summoner)
|
||||
Unk6 = 0x00000020, // 0 Spells In 3.3.5, Unused
|
||||
Unk7 = 0x00000040, // 12 Spells In 3.0.3, No Idea
|
||||
Unk8 = 0x00000080, // 4 Spells In 3.0.3, No Idea
|
||||
Unk9 = 0x00000100, // 51 Spells In 3.0.3, No Idea, Many Quest Related
|
||||
Unk10 = 0x00000200, // 51 Spells In 3.0.3, Something Defensive
|
||||
Unk11 = 0x00000400, // 3 Spells, Requires Something Near?
|
||||
Unk12 = 0x00000800, // 30 Spells In 3.0.3, No Idea
|
||||
Unk13 = 0x00001000, // Lightwell, Jeeves, Gnomish Alarm-O-Bot, Build Vehicles(Wintergrasp)
|
||||
Unk14 = 0x00002000, // Guides, Player Follows
|
||||
Unk15 = 0x00004000, // Force Of Nature, Shadowfiend, Feral Spirit, Summon Water Elemental
|
||||
Unk16 = 0x00008000, // Light/Dark Bullet, Soul/Fiery Consumption, Twisted Visage, Twilight Whelp. Phase Related?
|
||||
Unk17 = 0x00010000,
|
||||
Unk18 = 0x00020000,
|
||||
Unk19 = 0x00040000,
|
||||
Unk20 = 0x00080000,
|
||||
Unk21 = 0x00100000 // Totems
|
||||
}
|
||||
|
||||
public enum TaxiNodeFlags : byte
|
||||
{
|
||||
Alliance = 0x1,
|
||||
|
||||
@@ -37,7 +37,6 @@ namespace Game.Collision
|
||||
public abstract Vector3 GetPosition();
|
||||
public abstract float GetOrientation();
|
||||
public abstract float GetScale();
|
||||
public abstract void DebugVisualizeCorner(Vector3 corner);
|
||||
}
|
||||
|
||||
public class GameObjectModel : IModel
|
||||
@@ -196,7 +195,7 @@ namespace Game.Collision
|
||||
string magic = reader.ReadStringFromChars(8);
|
||||
if (magic != MapConst.VMapMagic)
|
||||
{
|
||||
Log.outError(LogFilter.Misc, $"File '{MapConst.GAMEOBJECT_MODELS}' has wrong header, expected {MapConst.VMapMagic}.");
|
||||
Log.outError(LogFilter.Misc, $"File '{filename}' has wrong header, expected {MapConst.VMapMagic}.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,11 +56,28 @@ namespace Game.Combat
|
||||
public void doAddThreat(Unit victim, float threat)
|
||||
{
|
||||
uint redirectThreadPct = victim.GetRedirectThreatPercent();
|
||||
Unit redirectTarget = victim.GetRedirectThreatTarget();
|
||||
|
||||
// If victim is personnal spawn, redirect all aggro to summoner
|
||||
TempSummon tempSummonVictim = victim.ToTempSummon();
|
||||
if (tempSummonVictim)
|
||||
{
|
||||
if (tempSummonVictim.IsVisibleBySummonerOnly())
|
||||
{
|
||||
// Personnal Spawns from same summoner can aggro each other
|
||||
if (!GetOwner().ToTempSummon() ||
|
||||
!GetOwner().ToTempSummon().IsVisibleBySummonerOnly() ||
|
||||
tempSummonVictim.GetSummonerGUID() != GetOwner().ToTempSummon().GetSummonerGUID())
|
||||
{
|
||||
redirectThreadPct = 100;
|
||||
redirectTarget = tempSummonVictim.GetSummoner();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// must check > 0.0f, otherwise dead loop
|
||||
if (threat > 0.0f && redirectThreadPct != 0)
|
||||
{
|
||||
Unit redirectTarget = victim.GetRedirectThreatTarget();
|
||||
if (redirectTarget != null)
|
||||
{
|
||||
float redirectThreat = MathFunctions.CalculatePct(threat, redirectThreadPct);
|
||||
|
||||
@@ -548,7 +548,7 @@ namespace Game.DataStorage
|
||||
public sealed class SummonPropertiesRecord
|
||||
{
|
||||
public uint Id;
|
||||
public uint Flags;
|
||||
public SummonPropFlags Flags;
|
||||
public SummonCategory Control;
|
||||
public ushort Faction;
|
||||
public SummonType Title;
|
||||
|
||||
@@ -1719,8 +1719,19 @@ namespace Game.Entities
|
||||
WorldObject viewpoint = this;
|
||||
Player player = ToPlayer();
|
||||
if (player != null)
|
||||
{
|
||||
viewpoint = player.GetViewpoint();
|
||||
|
||||
Creature creature = obj.ToCreature();
|
||||
if (creature)
|
||||
{
|
||||
TempSummon tempSummon = creature.ToTempSummon();
|
||||
if (tempSummon)
|
||||
if (tempSummon.IsVisibleBySummonerOnly() && GetGUID() != tempSummon.GetSummonerGUID())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (viewpoint == null)
|
||||
viewpoint = this;
|
||||
|
||||
@@ -1990,7 +2001,7 @@ namespace Game.Entities
|
||||
return null;
|
||||
}
|
||||
|
||||
public TempSummon SummonCreature(uint id, float x, float y, float z, float ang = 0, TempSummonType spwtype = TempSummonType.ManualDespawn, uint despwtime = 0)
|
||||
public TempSummon SummonCreature(uint id, float x, float y, float z, float ang = 0, TempSummonType spwtype = TempSummonType.ManualDespawn, uint despwtime = 0, bool visibleBySummonerOnly = false)
|
||||
{
|
||||
if (x == 0.0f && y == 0.0f && z == 0.0f)
|
||||
{
|
||||
@@ -1999,15 +2010,15 @@ namespace Game.Entities
|
||||
}
|
||||
Position pos = new Position();
|
||||
pos.Relocate(x, y, z, ang);
|
||||
return SummonCreature(id, pos, spwtype, despwtime, 0);
|
||||
return SummonCreature(id, pos, spwtype, despwtime, 0, visibleBySummonerOnly);
|
||||
}
|
||||
|
||||
public TempSummon SummonCreature(uint entry, Position pos, TempSummonType spwtype = TempSummonType.ManualDespawn, uint duration = 0, uint vehId = 0)
|
||||
public TempSummon SummonCreature(uint entry, Position pos, TempSummonType spwtype = TempSummonType.ManualDespawn, uint duration = 0, uint vehId = 0, bool visibleBySummonerOnly = false)
|
||||
{
|
||||
Map map = GetMap();
|
||||
if (map != null)
|
||||
{
|
||||
TempSummon summon = map.SummonCreature(entry, pos, null, duration, isTypeMask(TypeMask.Unit) ? ToUnit() : null);
|
||||
TempSummon summon = map.SummonCreature(entry, pos, null, duration, ToUnit(), 0, vehId, visibleBySummonerOnly);
|
||||
if (summon != null)
|
||||
{
|
||||
summon.SetTempSummonType(spwtype);
|
||||
|
||||
@@ -1716,7 +1716,8 @@ namespace Game.Entities
|
||||
|
||||
void CastAllObtainSpells()
|
||||
{
|
||||
for (byte slot = InventorySlots.ItemStart; slot < InventorySlots.ItemEnd; ++slot)
|
||||
int inventoryEnd = InventorySlots.ItemStart + GetInventorySlotCount();
|
||||
for (byte slot = InventorySlots.ItemStart; slot < inventoryEnd; ++slot)
|
||||
{
|
||||
Item item = GetItemByPos(InventorySlots.Bag0, slot);
|
||||
if (item)
|
||||
|
||||
@@ -4501,6 +4501,9 @@ namespace Game.Entities
|
||||
// update visibility
|
||||
UpdateObjectVisibility();
|
||||
|
||||
// recast lost by death auras of any items held in the inventory
|
||||
CastAllObtainSpells();
|
||||
|
||||
if (!applySickness)
|
||||
return;
|
||||
|
||||
|
||||
@@ -296,11 +296,15 @@ namespace Game.Entities
|
||||
|
||||
public uint GetTimer() { return m_timer; }
|
||||
|
||||
public void SetVisibleBySummonerOnly(bool visibleBySummonerOnly) { m_visibleBySummonerOnly = visibleBySummonerOnly; }
|
||||
public bool IsVisibleBySummonerOnly() { return m_visibleBySummonerOnly; }
|
||||
|
||||
public SummonPropertiesRecord m_Properties;
|
||||
TempSummonType m_type;
|
||||
uint m_timer;
|
||||
uint m_lifetime;
|
||||
ObjectGuid m_summonerGUID;
|
||||
bool m_visibleBySummonerOnly;
|
||||
}
|
||||
|
||||
public class Minion : TempSummon
|
||||
|
||||
@@ -3294,7 +3294,7 @@ namespace Game.Maps
|
||||
}
|
||||
}
|
||||
|
||||
public TempSummon SummonCreature(uint entry, Position pos, SummonPropertiesRecord properties = null, uint duration = 0, Unit summoner = null, uint spellId = 0, uint vehId = 0)
|
||||
public TempSummon SummonCreature(uint entry, Position pos, SummonPropertiesRecord properties = null, uint duration = 0, Unit summoner = null, uint spellId = 0, uint vehId = 0, bool visibleBySummonerOnly = false)
|
||||
{
|
||||
var mask = UnitTypeMask.Summon;
|
||||
if (properties != null)
|
||||
@@ -3333,7 +3333,7 @@ namespace Game.Maps
|
||||
mask = UnitTypeMask.Minion;
|
||||
break;
|
||||
default:
|
||||
if (Convert.ToBoolean(properties.Flags & 512)) // Mirror Image, Summon Gargoyle
|
||||
if (Convert.ToBoolean(properties.Flags & SummonPropFlags.Unk10)) // Mirror Image, Summon Gargoyle
|
||||
mask = UnitTypeMask.Guardian;
|
||||
break;
|
||||
}
|
||||
@@ -3374,10 +3374,10 @@ namespace Game.Maps
|
||||
PhasingHandler.InheritPhaseShift(summon, summoner);
|
||||
|
||||
summon.SetUInt32Value(UnitFields.CreatedBySpell, spellId);
|
||||
|
||||
summon.SetHomePosition(pos);
|
||||
|
||||
summon.InitStats(duration);
|
||||
summon.SetVisibleBySummonerOnly(visibleBySummonerOnly);
|
||||
|
||||
AddToMap(summon.ToCreature());
|
||||
summon.InitSummon();
|
||||
|
||||
|
||||
@@ -1710,6 +1710,8 @@ namespace Game.Spells
|
||||
if (m_originalCaster == null)
|
||||
return;
|
||||
|
||||
bool personalSpawn = (properties.Flags & SummonPropFlags.PersonalSpawn) != 0;
|
||||
|
||||
int duration = m_spellInfo.CalcDuration(m_originalCaster);
|
||||
|
||||
TempSummon summon = null;
|
||||
@@ -1750,7 +1752,7 @@ namespace Game.Spells
|
||||
case SummonCategory.Wild:
|
||||
case SummonCategory.Ally:
|
||||
case SummonCategory.Unk:
|
||||
if (Convert.ToBoolean(properties.Flags & 512))
|
||||
if (Convert.ToBoolean(properties.Flags & SummonPropFlags.Unk10))
|
||||
{
|
||||
SummonGuardian(effIndex, entry, properties, numSummons);
|
||||
break;
|
||||
@@ -1771,7 +1773,7 @@ namespace Game.Spells
|
||||
case SummonType.LightWell:
|
||||
case SummonType.Totem:
|
||||
{
|
||||
summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id);
|
||||
summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id, 0, personalSpawn);
|
||||
if (summon == null || !summon.IsTotem())
|
||||
return;
|
||||
|
||||
@@ -1784,7 +1786,7 @@ namespace Game.Spells
|
||||
}
|
||||
case SummonType.Minipet:
|
||||
{
|
||||
summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id);
|
||||
summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id, 0, personalSpawn);
|
||||
if (summon == null || !summon.HasUnitTypeMask(UnitTypeMask.Minion))
|
||||
return;
|
||||
|
||||
@@ -1811,7 +1813,7 @@ namespace Game.Spells
|
||||
// randomize position for multiple summons
|
||||
m_caster.GetRandomPoint(destTarget, radius, out pos);
|
||||
|
||||
summon = m_originalCaster.SummonCreature(entry, pos, summonType, (uint)duration);
|
||||
summon = m_originalCaster.SummonCreature(entry, pos, summonType, (uint)duration, 0, personalSpawn);
|
||||
if (summon == null)
|
||||
continue;
|
||||
|
||||
@@ -1832,7 +1834,7 @@ namespace Game.Spells
|
||||
SummonGuardian(effIndex, entry, properties, numSummons);
|
||||
break;
|
||||
case SummonCategory.Puppet:
|
||||
summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id);
|
||||
summon = m_caster.GetMap().SummonCreature(entry, destTarget, properties, (uint)duration, m_originalCaster, m_spellInfo.Id, 0, personalSpawn);
|
||||
break;
|
||||
case SummonCategory.Vehicle:
|
||||
// Summoning spells (usually triggered by npc_spellclick) that spawn a vehicle and that cause the clicker
|
||||
|
||||
Reference in New Issue
Block a user