Misc updates/fixes
This commit is contained in:
@@ -789,8 +789,8 @@ namespace Framework.Constants
|
|||||||
CommandLookupMapId = 875,
|
CommandLookupMapId = 875,
|
||||||
CommandLookupItemId = 876,
|
CommandLookupItemId = 876,
|
||||||
CommandLookupQuestId = 877,
|
CommandLookupQuestId = 877,
|
||||||
// 878 previously used, do not reuse
|
CommandDebugQuestreset = 878,
|
||||||
CommandDebugPoolstatus = 879,
|
// 879 previously used, do not reuse
|
||||||
CommandPdumpCopy = 880,
|
CommandPdumpCopy = 880,
|
||||||
CommandReloadVehicleTemplate = 881,
|
CommandReloadVehicleTemplate = 881,
|
||||||
// Custom Permissions 1000+
|
// Custom Permissions 1000+
|
||||||
|
|||||||
@@ -1097,8 +1097,9 @@ namespace Framework.Constants
|
|||||||
NpcinfoUnitFieldFlags3 = 5085,
|
NpcinfoUnitFieldFlags3 = 5085,
|
||||||
NpcinfoNpcFlags = 5086,
|
NpcinfoNpcFlags = 5086,
|
||||||
NpcinfoPhaseIds = 5087,
|
NpcinfoPhaseIds = 5087,
|
||||||
|
Scenario = 5088,
|
||||||
|
|
||||||
// Room For More Trinity Strings 5088-6603
|
// Room For More Trinity Strings 5089-6603
|
||||||
|
|
||||||
// Level Requirement Notifications
|
// Level Requirement Notifications
|
||||||
SayReq = 6604,
|
SayReq = 6604,
|
||||||
|
|||||||
@@ -61,26 +61,23 @@ namespace Framework.Constants
|
|||||||
//Spell targets used by SelectSpell
|
//Spell targets used by SelectSpell
|
||||||
public enum SelectTargetType
|
public enum SelectTargetType
|
||||||
{
|
{
|
||||||
DontCare = 0, //All target types allowed
|
DontCare = 0, //All target types allowed
|
||||||
|
Self, //Only Self casting
|
||||||
Self, //Only Self casting
|
SingleEnemy, //Only Single Enemy
|
||||||
|
AoeEnemy, //Only AoE Enemy
|
||||||
SingleEnemy, //Only Single Enemy
|
AnyEnemy, //AoE or Single Enemy
|
||||||
AoeEnemy, //Only AoE Enemy
|
SingleFriend, //Only Single Friend
|
||||||
AnyEnemy, //AoE or Single Enemy
|
AoeFriend, //Only AoE Friend
|
||||||
|
AnyFriend //AoE or Single Friend
|
||||||
SingleFriend, //Only Single Friend
|
|
||||||
AoeFriend, //Only AoE Friend
|
|
||||||
AnyFriend //AoE or Single Friend
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Spell Effects used by SelectSpell
|
//Spell Effects used by SelectSpell
|
||||||
public enum SelectEffect
|
public enum SelectEffect
|
||||||
{
|
{
|
||||||
DontCare = 0, //All spell effects allowed
|
DontCare = 0, //All spell effects allowed
|
||||||
Damage, //Spell does damage
|
Damage, //Spell does damage
|
||||||
Healing, //Spell does healing
|
Healing, //Spell does healing
|
||||||
Aura //Spell applies an aura
|
Aura //Spell applies an aura
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SpellCastSource
|
public enum SpellCastSource
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Google.Protobuf" Version="4.0.0-rc2" />
|
<PackageReference Include="Google.Protobuf" Version="4.0.0-rc2" />
|
||||||
<PackageReference Include="MySqlConnector" Version="2.1.0" />
|
<PackageReference Include="MySqlConnector" Version="2.1.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -109,33 +109,32 @@ namespace Game.AI
|
|||||||
|
|
||||||
public class CasterAI : CombatAI
|
public class CasterAI : CombatAI
|
||||||
{
|
{
|
||||||
float _attackDist;
|
float _attackDistance;
|
||||||
|
|
||||||
public CasterAI(Creature c)
|
public CasterAI(Creature creature) : base(creature)
|
||||||
: base(c)
|
|
||||||
{
|
{
|
||||||
_attackDist = SharedConst.MeleeRange;
|
_attackDistance = SharedConst.MeleeRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void InitializeAI()
|
public override void InitializeAI()
|
||||||
{
|
{
|
||||||
base.InitializeAI();
|
base.InitializeAI();
|
||||||
|
|
||||||
_attackDist = 30.0f;
|
_attackDistance = 30.0f;
|
||||||
foreach (var id in Spells)
|
foreach (var id in Spells)
|
||||||
{
|
{
|
||||||
AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID());
|
AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID());
|
||||||
if (info != null && info.condition == AICondition.Combat && _attackDist > info.maxRange)
|
if (info != null && info.condition == AICondition.Combat && _attackDistance > info.maxRange)
|
||||||
_attackDist = info.maxRange;
|
_attackDistance = info.maxRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_attackDist == 30.0f)
|
if (_attackDistance == 30.0f)
|
||||||
_attackDist = SharedConst.MeleeRange;
|
_attackDistance = SharedConst.MeleeRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void AttackStart(Unit victim)
|
public override void AttackStart(Unit victim)
|
||||||
{
|
{
|
||||||
AttackStartCaster(victim, _attackDist);
|
AttackStartCaster(victim, _attackDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void JustEngagedWith(Unit victim)
|
public override void JustEngagedWith(Unit victim)
|
||||||
@@ -198,18 +197,19 @@ namespace Game.AI
|
|||||||
{
|
{
|
||||||
float _minRange;
|
float _minRange;
|
||||||
|
|
||||||
public ArcherAI(Creature c) : base(c)
|
public ArcherAI(Creature creature) : base(creature)
|
||||||
{
|
{
|
||||||
if (me.m_spells[0] == 0)
|
if (creature.m_spells[0] == 0)
|
||||||
Log.outError(LogFilter.ScriptsAi, "ArcherAI set for creature (entry = {0}) with spell1=0. AI will do nothing", me.GetEntry());
|
Log.outError(LogFilter.ScriptsAi, $"ArcherAI set for creature with spell1=0. AI will do nothing ({me.GetGUID()})");
|
||||||
|
|
||||||
var spellInfo = Global.SpellMgr.GetSpellInfo(me.m_spells[0], me.GetMap().GetDifficultyID());
|
var spellInfo = Global.SpellMgr.GetSpellInfo(creature.m_spells[0], creature.GetMap().GetDifficultyID());
|
||||||
_minRange = spellInfo != null ? spellInfo.GetMinRange(false) : 0;
|
_minRange = spellInfo != null ? spellInfo.GetMinRange(false) : 0;
|
||||||
|
|
||||||
if (_minRange == 0)
|
if (_minRange == 0)
|
||||||
_minRange = SharedConst.MeleeRange;
|
_minRange = SharedConst.MeleeRange;
|
||||||
me.m_CombatDistance = spellInfo != null ? spellInfo.GetMaxRange(false) : 0;
|
|
||||||
me.m_SightDistance = me.m_CombatDistance;
|
creature.m_CombatDistance = spellInfo != null ? spellInfo.GetMaxRange(false) : 0;
|
||||||
|
creature.m_SightDistance = creature.m_CombatDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void AttackStart(Unit who)
|
public override void AttackStart(Unit who)
|
||||||
@@ -248,23 +248,21 @@ namespace Game.AI
|
|||||||
{
|
{
|
||||||
float _minRange;
|
float _minRange;
|
||||||
|
|
||||||
public TurretAI(Creature c)
|
public TurretAI(Creature creature) : base(creature)
|
||||||
: base(c)
|
|
||||||
{
|
{
|
||||||
if (me.m_spells[0] == 0)
|
if (creature.m_spells[0] == 0)
|
||||||
Log.outError(LogFilter.Server, "TurretAI set for creature (entry = {0}) with spell1=0. AI will do nothing", me.GetEntry());
|
Log.outError(LogFilter.Server, $"TurretAI set for creature with spell1=0. AI will do nothing ({creature.GetGUID()})");
|
||||||
|
|
||||||
var spellInfo = Global.SpellMgr.GetSpellInfo(me.m_spells[0], me.GetMap().GetDifficultyID());
|
var spellInfo = Global.SpellMgr.GetSpellInfo(creature.m_spells[0], creature.GetMap().GetDifficultyID());
|
||||||
_minRange = spellInfo != null ? spellInfo.GetMinRange(false) : 0;
|
_minRange = spellInfo != null ? spellInfo.GetMinRange(false) : 0;
|
||||||
me.m_CombatDistance = spellInfo != null ? spellInfo.GetMaxRange(false) : 0;
|
creature.m_CombatDistance = spellInfo != null ? spellInfo.GetMaxRange(false) : 0;
|
||||||
me.m_SightDistance = me.m_CombatDistance;
|
creature.m_SightDistance = creature.m_CombatDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanAIAttack(Unit victim)
|
public override bool CanAIAttack(Unit victim)
|
||||||
{
|
{
|
||||||
// todo use one function to replace it
|
// todo use one function to replace it
|
||||||
if (!me.IsWithinCombatRange(victim, me.m_CombatDistance)
|
if (!me.IsWithinCombatRange(victim, me.m_CombatDistance) || (_minRange != 0 && me.IsWithinCombatRange(victim, _minRange)))
|
||||||
|| (_minRange != 0 && me.IsWithinCombatRange(victim, _minRange)))
|
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ namespace Game.AI
|
|||||||
_moveInLOSLocked = false;
|
_moveInLOSLocked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Talk(uint id, WorldObject whisperTarget = null)
|
||||||
|
{
|
||||||
|
Global.CreatureTextMgr.SendChat(me, (byte)id, whisperTarget);
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnCharmed(bool isNew)
|
public override void OnCharmed(bool isNew)
|
||||||
{
|
{
|
||||||
if (isNew && !me.IsCharmed() && !me.LastCharmerGUID.IsEmpty())
|
if (isNew && !me.IsCharmed() && !me.LastCharmerGUID.IsEmpty())
|
||||||
@@ -56,27 +61,20 @@ namespace Game.AI
|
|||||||
if (lastCharmer != null)
|
if (lastCharmer != null)
|
||||||
me.EngageWithTarget(lastCharmer);
|
me.EngageWithTarget(lastCharmer);
|
||||||
}
|
}
|
||||||
me.LastCharmerGUID.Clear();
|
|
||||||
|
|
||||||
if (!me.IsInCombat())
|
me.LastCharmerGUID.Clear();
|
||||||
EnterEvadeMode(EvadeReason.NoHostiles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
base.OnCharmed(isNew);
|
base.OnCharmed(isNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Talk(uint id, WorldObject whisperTarget = null)
|
|
||||||
{
|
|
||||||
Global.CreatureTextMgr.SendChat(me, (byte)id, whisperTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DoZoneInCombat(Creature creature = null)
|
public void DoZoneInCombat(Creature creature = null)
|
||||||
{
|
{
|
||||||
if (!creature)
|
if (!creature)
|
||||||
creature = me;
|
creature = me;
|
||||||
|
|
||||||
Map map = creature.GetMap();
|
Map map = creature.GetMap();
|
||||||
if (!map.IsDungeon()) //use IsDungeon instead of Instanceable, in case Battlegrounds will be instantiated
|
if (!map.IsDungeon()) // use IsDungeon instead of Instanceable, in case Battlegrounds will be instantiated
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Server, "DoZoneInCombat call for map that isn't an instance (creature entry = {0})", creature.IsTypeId(TypeId.Unit) ? creature.ToCreature().GetEntry() : 0);
|
Log.outError(LogFilter.Server, "DoZoneInCombat call for map that isn't an instance (creature entry = {0})", creature.IsTypeId(TypeId.Unit) ? creature.ToCreature().GetEntry() : 0);
|
||||||
return;
|
return;
|
||||||
@@ -219,7 +217,7 @@ namespace Game.AI
|
|||||||
if (!_EnterEvadeMode(why))
|
if (!_EnterEvadeMode(why))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Log.outDebug(LogFilter.Unit, "Creature {0} enters evade mode.", me.GetEntry());
|
Log.outDebug(LogFilter.Unit, $"CreatureAI::EnterEvadeMode: entering evade mode (why: {why}) ({me.GetGUID()})");
|
||||||
|
|
||||||
if (me.GetVehicle() == null) // otherwise me will be in evade mode forever
|
if (me.GetVehicle() == null) // otherwise me will be in evade mode forever
|
||||||
{
|
{
|
||||||
@@ -374,12 +372,10 @@ namespace Game.AI
|
|||||||
}
|
}
|
||||||
alreadyChecked.Add(next);
|
alreadyChecked.Add(next);
|
||||||
}
|
}
|
||||||
else
|
else if (outOfBounds.Contains(next))
|
||||||
{
|
hasOutOfBoundsNeighbor = true;
|
||||||
if (outOfBounds.Contains(next))
|
|
||||||
hasOutOfBoundsNeighbor = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fill || hasOutOfBoundsNeighbor)
|
if (fill || hasOutOfBoundsNeighbor)
|
||||||
{
|
{
|
||||||
var pos = new Position(startPosition.GetPositionX() + front.Key * SharedConst.BoundaryVisualizeStepSize, startPosition.GetPositionY() + front.Value * SharedConst.BoundaryVisualizeStepSize, spawnZ);
|
var pos = new Position(startPosition.GetPositionX() + front.Key * SharedConst.BoundaryVisualizeStepSize, startPosition.GetPositionY() + front.Value * SharedConst.BoundaryVisualizeStepSize, spawnZ);
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ namespace Game.AI
|
|||||||
|
|
||||||
public GameObject me;
|
public GameObject me;
|
||||||
|
|
||||||
public GameObjectAI(GameObject gameObject)
|
public GameObjectAI(GameObject go)
|
||||||
{
|
{
|
||||||
me = gameObject;
|
me = go;
|
||||||
_scheduler = new TaskScheduler();
|
_scheduler = new TaskScheduler();
|
||||||
_events = new EventMap();
|
_events = new EventMap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace Game.AI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.outDebug(LogFilter.Unit, "Guard entry: {0} enters evade mode.", me.GetEntry());
|
Log.outTrace(LogFilter.ScriptsAi, $"GuardAI::EnterEvadeMode: {me.GetGUID()} enters evade mode.");
|
||||||
|
|
||||||
me.RemoveAllAuras();
|
me.RemoveAllAuras();
|
||||||
me.CombatStop(true);
|
me.CombatStop(true);
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ namespace Game.AI
|
|||||||
{
|
{
|
||||||
public class PassiveAI : CreatureAI
|
public class PassiveAI : CreatureAI
|
||||||
{
|
{
|
||||||
public PassiveAI(Creature c) : base(c)
|
public PassiveAI(Creature creature) : base(creature)
|
||||||
{
|
{
|
||||||
me.SetReactState(ReactStates.Passive);
|
creature.SetReactState(ReactStates.Passive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateAI(uint diff)
|
public override void UpdateAI(uint diff)
|
||||||
@@ -41,9 +41,9 @@ namespace Game.AI
|
|||||||
|
|
||||||
public class PossessedAI : CreatureAI
|
public class PossessedAI : CreatureAI
|
||||||
{
|
{
|
||||||
public PossessedAI(Creature c) : base(c)
|
public PossessedAI(Creature creature) : base(creature)
|
||||||
{
|
{
|
||||||
me.SetReactState(ReactStates.Passive);
|
creature.SetReactState(ReactStates.Passive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void AttackStart(Unit target)
|
public override void AttackStart(Unit target)
|
||||||
@@ -99,7 +99,7 @@ namespace Game.AI
|
|||||||
{
|
{
|
||||||
public NullCreatureAI(Creature creature) : base(creature)
|
public NullCreatureAI(Creature creature) : base(creature)
|
||||||
{
|
{
|
||||||
me.SetReactState(ReactStates.Passive);
|
creature.SetReactState(ReactStates.Passive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void MoveInLineOfSight(Unit unit) { }
|
public override void MoveInLineOfSight(Unit unit) { }
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace Game.AI
|
|||||||
List<ObjectGuid> _allySet = new();
|
List<ObjectGuid> _allySet = new();
|
||||||
uint _updateAlliesTimer;
|
uint _updateAlliesTimer;
|
||||||
|
|
||||||
public PetAI(Creature c) : base(c)
|
public PetAI(Creature creature) : base(creature)
|
||||||
{
|
{
|
||||||
UpdateAllies();
|
UpdateAllies();
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ namespace Game.AI
|
|||||||
|
|
||||||
if (NeedToStop())
|
if (NeedToStop())
|
||||||
{
|
{
|
||||||
Log.outDebug(LogFilter.Server, "Pet AI stopped attacking [{0}]", me.GetGUID().ToString());
|
Log.outTrace(LogFilter.ScriptsAi, $"PetAI::UpdateAI: AI stopped attacking {me.GetGUID()}");
|
||||||
StopAttack();
|
StopAttack();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -528,26 +528,28 @@ namespace Game.AI
|
|||||||
|
|
||||||
public override void ReceiveEmote(Player player, TextEmotes emoteId)
|
public override void ReceiveEmote(Player player, TextEmotes emoteId)
|
||||||
{
|
{
|
||||||
if (!me.GetOwnerGUID().IsEmpty() && me.GetOwnerGUID() == player.GetGUID())
|
if (me.GetOwnerGUID() != player.GetGUID())
|
||||||
switch (emoteId)
|
return;
|
||||||
{
|
|
||||||
case TextEmotes.Cower:
|
switch (emoteId)
|
||||||
if (me.IsPet() && me.ToPet().IsPetGhoul())
|
{
|
||||||
me.HandleEmoteCommand(Emote.OneshotOmnicastGhoul);
|
case TextEmotes.Cower:
|
||||||
break;
|
if (me.IsPet() && me.ToPet().IsPetGhoul())
|
||||||
case TextEmotes.Angry:
|
me.HandleEmoteCommand(Emote.OneshotOmnicastGhoul);
|
||||||
if (me.IsPet() && me.ToPet().IsPetGhoul())
|
break;
|
||||||
me.HandleEmoteCommand(Emote.StateStun);
|
case TextEmotes.Angry:
|
||||||
break;
|
if (me.IsPet() && me.ToPet().IsPetGhoul())
|
||||||
case TextEmotes.Glare:
|
me.HandleEmoteCommand(Emote.StateStun);
|
||||||
if (me.IsPet() && me.ToPet().IsPetGhoul())
|
break;
|
||||||
me.HandleEmoteCommand(Emote.StateStun);
|
case TextEmotes.Glare:
|
||||||
break;
|
if (me.IsPet() && me.ToPet().IsPetGhoul())
|
||||||
case TextEmotes.Soothe:
|
me.HandleEmoteCommand(Emote.StateStun);
|
||||||
if (me.IsPet() && me.ToPet().IsPetGhoul())
|
break;
|
||||||
me.HandleEmoteCommand(Emote.OneshotOmnicastGhoul);
|
case TextEmotes.Soothe:
|
||||||
break;
|
if (me.IsPet() && me.ToPet().IsPetGhoul())
|
||||||
}
|
me.HandleEmoteCommand(Emote.OneshotOmnicastGhoul);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NeedToStop()
|
bool NeedToStop()
|
||||||
@@ -595,17 +597,17 @@ namespace Game.AI
|
|||||||
if (player)
|
if (player)
|
||||||
group = player.GetGroup();
|
group = player.GetGroup();
|
||||||
|
|
||||||
//only pet and owner/not in group.ok
|
// only pet and owner/not in group.ok
|
||||||
if (_allySet.Count == 2 && !group)
|
if (_allySet.Count == 2 && !group)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//owner is in group; group members filled in already (no raid . subgroupcount = whole count)
|
// owner is in group; group members filled in already (no raid . subgroupcount = whole count)
|
||||||
if (group && !group.IsRaidGroup() && _allySet.Count == (group.GetMembersCount() + 2))
|
if (group && !group.IsRaidGroup() && _allySet.Count == (group.GetMembersCount() + 2))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_allySet.Clear();
|
_allySet.Clear();
|
||||||
_allySet.Add(me.GetGUID());
|
_allySet.Add(me.GetGUID());
|
||||||
if (group) //add group
|
if (group) // add group
|
||||||
{
|
{
|
||||||
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next())
|
for (GroupReference refe = group.GetFirstMember(); refe != null; refe = refe.Next())
|
||||||
{
|
{
|
||||||
@@ -619,7 +621,7 @@ namespace Game.AI
|
|||||||
_allySet.Add(target.GetGUID());
|
_allySet.Add(target.GetGUID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else //remove group
|
else // remove group
|
||||||
_allySet.Add(owner.GetGUID());
|
_allySet.Add(owner.GetGUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,12 +633,12 @@ namespace Game.AI
|
|||||||
base.OnCharmed(isNew);
|
base.OnCharmed(isNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Quick access to set all flags to FALSE
|
||||||
|
/// </summary>
|
||||||
void ClearCharmInfoFlags()
|
void ClearCharmInfoFlags()
|
||||||
{
|
{
|
||||||
// Quick access to set all flags to FALSE
|
|
||||||
|
|
||||||
CharmInfo ci = me.GetCharmInfo();
|
CharmInfo ci = me.GetCharmInfo();
|
||||||
|
|
||||||
if (ci != null)
|
if (ci != null)
|
||||||
{
|
{
|
||||||
ci.SetIsAtStay(false);
|
ci.SetIsAtStay(false);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace Game.AI
|
|||||||
|
|
||||||
public TotemAI(Creature creature) : base(creature)
|
public TotemAI(Creature creature) : base(creature)
|
||||||
{
|
{
|
||||||
|
Cypher.Assert(creature.IsTotem(), $"TotemAI: AI assigned to a no-totem creature ({creature.GetGUID()})!");
|
||||||
_victimGuid = ObjectGuid.Empty;
|
_victimGuid = ObjectGuid.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,8 +52,7 @@ namespace Game.AI
|
|||||||
Unit victim = !_victimGuid.IsEmpty() ? Global.ObjAccessor.GetUnit(me, _victimGuid) : null;
|
Unit victim = !_victimGuid.IsEmpty() ? Global.ObjAccessor.GetUnit(me, _victimGuid) : null;
|
||||||
|
|
||||||
// Search victim if no, not attackable, or out of range, or friendly (possible in case duel end)
|
// Search victim if no, not attackable, or out of range, or friendly (possible in case duel end)
|
||||||
if (victim == null || !victim.IsTargetableForAttack() || !me.IsWithinDistInMap(victim, max_range) ||
|
if (victim == null || !victim.IsTargetableForAttack() || !me.IsWithinDistInMap(victim, max_range) || me.IsFriendlyTo(victim) || !me.CanSeeOrDetect(victim))
|
||||||
me.IsFriendlyTo(victim) || !me.CanSeeOrDetect(victim))
|
|
||||||
{
|
{
|
||||||
var u_check = new NearestAttackableUnitInObjectRangeCheck(me, me.GetCharmerOrOwnerOrSelf(), max_range);
|
var u_check = new NearestAttackableUnitInObjectRangeCheck(me, me.GetCharmerOrOwnerOrSelf(), max_range);
|
||||||
var checker = new UnitLastSearcher(me, u_check);
|
var checker = new UnitLastSearcher(me, u_check);
|
||||||
|
|||||||
@@ -1317,8 +1317,8 @@ namespace Game.AI
|
|||||||
_castCheckTimer = 0;
|
_castCheckTimer = 0;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (IsRangedAttacker())
|
if (IsRangedAttacker()) // chase to zero if the target isn't in line of sight
|
||||||
{ // chase to zero if the target isn't in line of sight
|
{
|
||||||
bool inLOS = me.IsWithinLOSInMap(target);
|
bool inLOS = me.IsWithinLOSInMap(target);
|
||||||
if (_chaseCloser != !inLOS)
|
if (_chaseCloser != !inLOS)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ namespace Game.AI
|
|||||||
|
|
||||||
if (!CliDB.SoundKitStorage.ContainsKey(soundId))
|
if (!CliDB.SoundKitStorage.ContainsKey(soundId))
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Scripts, "Invalid soundId {0} used in DoPlaySoundToSet (Source: TypeId {1}, GUID {2})", soundId, source.GetTypeId(), source.GetGUID().ToString());
|
Log.outError(LogFilter.ScriptsAi, $"ScriptedAI::DoPlaySoundToSet: Invalid soundId {soundId} used in DoPlaySoundToSet (Source: {source.GetGUID()})");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,9 +307,24 @@ namespace Game.AI
|
|||||||
if (mechanic != 0 && tempSpell.Mechanic != mechanic)
|
if (mechanic != 0 && tempSpell.Mechanic != mechanic)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// Continue if we don't have the mana to actually cast this spell
|
||||||
|
bool hasPower = true;
|
||||||
|
foreach (SpellPowerCost cost in tempSpell.CalcPowerCost(me, tempSpell.GetSchoolMask()))
|
||||||
|
{
|
||||||
|
if (cost.Amount > me.GetPower(cost.Power))
|
||||||
|
{
|
||||||
|
hasPower = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasPower)
|
||||||
|
continue;
|
||||||
|
|
||||||
//Check if the spell meets our range requirements
|
//Check if the spell meets our range requirements
|
||||||
if (rangeMin != 0 && me.GetSpellMinRangeForTarget(target, tempSpell) < rangeMin)
|
if (rangeMin != 0 && me.GetSpellMinRangeForTarget(target, tempSpell) < rangeMin)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (rangeMax != 0 && me.GetSpellMaxRangeForTarget(target, tempSpell) > rangeMax)
|
if (rangeMax != 0 && me.GetSpellMaxRangeForTarget(target, tempSpell) > rangeMax)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -350,8 +365,7 @@ namespace Game.AI
|
|||||||
if (player != null)
|
if (player != null)
|
||||||
player.TeleportTo(unit.GetMapId(), x, y, z, o, TeleportToOptions.NotLeaveCombat);
|
player.TeleportTo(unit.GetMapId(), x, y, z, o, TeleportToOptions.NotLeaveCombat);
|
||||||
else
|
else
|
||||||
Log.outError(LogFilter.Scripts, "Creature {0} (Entry: {1}) Tried to teleport non-player unit (Type: {2} GUID: {3}) to X: {4} Y: {5} Z: {6} O: {7}. Aborted.",
|
Log.outError(LogFilter.ScriptsAi, $"ScriptedAI::DoTeleportPlayer: Creature {me.GetGUID()} Tried to teleport non-player unit ({unit.GetGUID()}) to X: {x} Y: {y} Z: {z} O: {o}. Aborted.");
|
||||||
me.GetGUID(), me.GetEntry(), unit.GetTypeId(), unit.GetGUID(), x, y, z, o);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DoTeleportAll(float x, float y, float z, float o)
|
public void DoTeleportAll(float x, float y, float z, float o)
|
||||||
@@ -631,7 +645,7 @@ namespace Game.AI
|
|||||||
{
|
{
|
||||||
if (delayToRespawn < TimeSpan.FromSeconds(2))
|
if (delayToRespawn < TimeSpan.FromSeconds(2))
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Scripts, "_DespawnAtEvade called with delay of {0} seconds, defaulting to 2.", delayToRespawn);
|
Log.outError(LogFilter.ScriptsAi, $"BossAI::_DespawnAtEvade: called with delay of {delayToRespawn} seconds, defaulting to 2 (me: {me.GetGUID()})");
|
||||||
delayToRespawn = TimeSpan.FromSeconds(2);
|
delayToRespawn = TimeSpan.FromSeconds(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -641,7 +655,7 @@ namespace Game.AI
|
|||||||
TempSummon whoSummon = who.ToTempSummon();
|
TempSummon whoSummon = who.ToTempSummon();
|
||||||
if (whoSummon)
|
if (whoSummon)
|
||||||
{
|
{
|
||||||
Log.outWarn(LogFilter.ScriptsAi, "_DespawnAtEvade called on a temporary summon.");
|
Log.outWarn(LogFilter.ScriptsAi, $"BossAI::_DespawnAtEvade: called on a temporary summon (who: {who.GetGUID()})");
|
||||||
whoSummon.UnSummon();
|
whoSummon.UnSummon();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ namespace Game.AI
|
|||||||
{
|
{
|
||||||
AddEscortState(EscortState.Returning);
|
AddEscortState(EscortState.Returning);
|
||||||
ReturnToLastPoint();
|
ReturnToLastPoint();
|
||||||
Log.outDebug(LogFilter.Scripts, "EscortAI.EnterEvadeMode has left combat and is now returning to last point");
|
Log.outDebug(LogFilter.ScriptsAi, $"EscortAI.EnterEvadeMode has left combat and is now returning to last point {me.GetGUID()}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -195,7 +195,7 @@ namespace Game.AI
|
|||||||
|
|
||||||
if (_despawnAtEnd)
|
if (_despawnAtEnd)
|
||||||
{
|
{
|
||||||
Log.outDebug(LogFilter.Scripts, "EscortAI.UpdateAI: reached end of waypoints, despawning at end");
|
Log.outDebug(LogFilter.ScriptsAi, $"EscortAI::UpdateAI: reached end of waypoints, despawning at end ({me.GetGUID()})");
|
||||||
if (_returnToStart)
|
if (_returnToStart)
|
||||||
{
|
{
|
||||||
Position respawnPosition = new();
|
Position respawnPosition = new();
|
||||||
@@ -203,7 +203,7 @@ namespace Game.AI
|
|||||||
me.GetRespawnPosition(out respawnPosition.posX, out respawnPosition.posY, out respawnPosition.posZ, out orientation);
|
me.GetRespawnPosition(out respawnPosition.posX, out respawnPosition.posY, out respawnPosition.posZ, out orientation);
|
||||||
respawnPosition.SetOrientation(orientation);
|
respawnPosition.SetOrientation(orientation);
|
||||||
me.GetMotionMaster().MovePoint(EscortPointIds.Home, respawnPosition);
|
me.GetMotionMaster().MovePoint(EscortPointIds.Home, respawnPosition);
|
||||||
Log.outDebug(LogFilter.Scripts, $"EscortAI.UpdateAI: returning to spawn location: {respawnPosition}");
|
Log.outDebug(LogFilter.ScriptsAi, $"EscortAI::UpdateAI: returning to spawn location: {respawnPosition} ({me.GetGUID()})");
|
||||||
}
|
}
|
||||||
else if (_instantRespawn)
|
else if (_instantRespawn)
|
||||||
me.Respawn();
|
me.Respawn();
|
||||||
@@ -211,7 +211,7 @@ namespace Game.AI
|
|||||||
me.DespawnOrUnsummon();
|
me.DespawnOrUnsummon();
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.outDebug(LogFilter.Scripts, "EscortAI.UpdateAI: reached end of waypoints");
|
Log.outDebug(LogFilter.ScriptsAi, $"EscortAI::UpdateAI: reached end of waypoints ({me.GetGUID()})");
|
||||||
RemoveEscortState(EscortState.Escorting);
|
RemoveEscortState(EscortState.Escorting);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -242,7 +242,7 @@ namespace Game.AI
|
|||||||
{
|
{
|
||||||
if (!IsPlayerOrGroupInRange())
|
if (!IsPlayerOrGroupInRange())
|
||||||
{
|
{
|
||||||
Log.outDebug(LogFilter.Scripts, "EscortAI failed because player/group was to far away or not found");
|
Log.outDebug(LogFilter.ScriptsAi, $"EscortAI::UpdateAI: failed because player/group was to far away or not found ({me.GetGUID()})");
|
||||||
|
|
||||||
bool isEscort = false;
|
bool isEscort = false;
|
||||||
CreatureData creatureData = me.GetCreatureData();
|
CreatureData creatureData = me.GetCreatureData();
|
||||||
@@ -293,7 +293,7 @@ namespace Game.AI
|
|||||||
|
|
||||||
if (Id == EscortPointIds.LastPoint)
|
if (Id == EscortPointIds.LastPoint)
|
||||||
{
|
{
|
||||||
Log.outDebug(LogFilter.Scripts, "EscortAI.MovementInform has returned to original position before combat");
|
Log.outDebug(LogFilter.ScriptsAi, $"EscortAI::MovementInform has returned to original position before combat ({me.GetGUID()})");
|
||||||
|
|
||||||
me.SetWalk(!_running);
|
me.SetWalk(!_running);
|
||||||
RemoveEscortState(EscortState.Returning);
|
RemoveEscortState(EscortState.Returning);
|
||||||
@@ -301,16 +301,16 @@ namespace Game.AI
|
|||||||
}
|
}
|
||||||
else if (Id == EscortPointIds.Home)
|
else if (Id == EscortPointIds.Home)
|
||||||
{
|
{
|
||||||
Log.outDebug(LogFilter.Scripts, "EscortAI.MovementInform: returned to home location and restarting waypoint path");
|
Log.outDebug(LogFilter.ScriptsAi, $"EscortAI::MovementInform: returned to home location and restarting waypoint path ({me.GetGUID()})");
|
||||||
_started = false;
|
_started = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (moveType == MovementGeneratorType.Waypoint)
|
else if (moveType == MovementGeneratorType.Waypoint)
|
||||||
{
|
{
|
||||||
Cypher.Assert(Id < _path.nodes.Count, $"EscortAI.MovementInform: referenced movement id ({Id}) points to non-existing node in loaded path");
|
Cypher.Assert(Id < _path.nodes.Count, $"EscortAI::MovementInform: referenced movement id ({Id}) points to non-existing node in loaded path ({me.GetGUID()})");
|
||||||
WaypointNode waypoint = _path.nodes[(int)Id];
|
WaypointNode waypoint = _path.nodes[(int)Id];
|
||||||
|
|
||||||
Log.outDebug(LogFilter.Scripts, $"EscortAI.MovementInform: waypoint node {waypoint.id} reached");
|
Log.outDebug(LogFilter.ScriptsAi, $"EscortAI::MovementInform: waypoint node {waypoint.id} reached ({me.GetGUID()})");
|
||||||
|
|
||||||
// last point
|
// last point
|
||||||
if (Id == _path.nodes.Count - 1)
|
if (Id == _path.nodes.Count - 1)
|
||||||
@@ -387,15 +387,15 @@ namespace Game.AI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (me.GetVictim())
|
if (me.IsEngaged())
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Scripts, $"EscortAI.Start: (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) attempts to Start while in combat");
|
Log.outError(LogFilter.ScriptsAi, $"EscortAI::Start: (script: {me.GetScriptName()} attempts to Start while in combat ({me.GetGUID()})");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasEscortState(EscortState.Escorting))
|
if (HasEscortState(EscortState.Escorting))
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Scripts, $"EscortAI.Start: (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) attempts to Start while already escorting");
|
Log.outError(LogFilter.ScriptsAi, $"EscortAI::Start: (script: {me.GetScriptName()} attempts to Start while already escorting ({me.GetGUID()})");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,7 +406,7 @@ namespace Game.AI
|
|||||||
|
|
||||||
if (_path.nodes.Empty())
|
if (_path.nodes.Empty())
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Scripts, $"EscortAI.Start: (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) starts with 0 waypoints (possible missing entry in script_waypoint. Quest: {(quest != null ? quest.Id : 0)}).");
|
Log.outError(LogFilter.ScriptsAi, $"EscortAI::Start: (script: {me.GetScriptName()} starts with 0 waypoints (possible missing entry in script_waypoint. Quest: {(quest != null ? quest.Id : 0)} ({me.GetGUID()})");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,7 +418,7 @@ namespace Game.AI
|
|||||||
_returnToStart = canLoopPath;
|
_returnToStart = canLoopPath;
|
||||||
|
|
||||||
if (_returnToStart && _instantRespawn)
|
if (_returnToStart && _instantRespawn)
|
||||||
Log.outError(LogFilter.Scripts, $"EscortAI.Start: (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.");
|
Log.outError(LogFilter.ScriptsAi, $"EscortAI::Start: (script: {me.GetScriptName()} is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn ({me.GetGUID()})");
|
||||||
|
|
||||||
me.GetMotionMaster().MoveIdle();
|
me.GetMotionMaster().MoveIdle();
|
||||||
me.GetMotionMaster().Clear(MovementGeneratorPriority.Normal);
|
me.GetMotionMaster().Clear(MovementGeneratorPriority.Normal);
|
||||||
@@ -432,7 +432,7 @@ namespace Game.AI
|
|||||||
me.SetImmuneToNPC(false);
|
me.SetImmuneToNPC(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.outDebug(LogFilter.Scripts, $"EscortAI.Start: (script: {me.GetScriptName()}, creature entry: {me.GetEntry()}) started with {_path.nodes.Count} waypoints. ActiveAttacker = {_activeAttacker}, Run = {_running}, Player = {_playerGUID}");
|
Log.outDebug(LogFilter.ScriptsAi, $"EscortAI::Start: (script: {me.GetScriptName()}, started with {_path.nodes.Count} waypoints. ActiveAttacker = {_activeAttacker}, Run = {_running}, Player = {_playerGUID} ({me.GetGUID()})");
|
||||||
|
|
||||||
// set initial speed
|
// set initial speed
|
||||||
me.SetWalk(!_running);
|
me.SetWalk(!_running);
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ namespace Game.AI
|
|||||||
{
|
{
|
||||||
if (HasFollowState(FollowState.Complete) && !HasFollowState(FollowState.PostEvent))
|
if (HasFollowState(FollowState.Complete) && !HasFollowState(FollowState.PostEvent))
|
||||||
{
|
{
|
||||||
Log.outDebug(LogFilter.Scripts, "FollowerAI is set completed, despawns.");
|
Log.outDebug(LogFilter.ScriptsAi, $"FollowerAI::UpdateAI: is set completed, despawns. ({me.GetGUID()})");
|
||||||
me.DespawnOrUnsummon();
|
me.DespawnOrUnsummon();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,7 @@ namespace Game.AI
|
|||||||
|
|
||||||
if (maxRangeExceeded || questAbandoned)
|
if (maxRangeExceeded || questAbandoned)
|
||||||
{
|
{
|
||||||
Log.outDebug(LogFilter.Scripts, $"FollowerAI::UpdateAI: failed because player/group was to far away or not found ({me.GetGUID()})");
|
Log.outDebug(LogFilter.ScriptsAi, $"FollowerAI::UpdateAI: failed because player/group was to far away or not found ({me.GetGUID()})");
|
||||||
me.DespawnOrUnsummon();
|
me.DespawnOrUnsummon();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1079,9 +1079,9 @@ namespace Game.AI
|
|||||||
GetScript().OnReset();
|
GetScript().OnReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasEscortState(SmartEscortState uiEscortState) { return (_escortState & uiEscortState) != 0; }
|
public bool HasEscortState(SmartEscortState escortState) { return (_escortState & escortState) != 0; }
|
||||||
public void AddEscortState(SmartEscortState uiEscortState) { _escortState |= uiEscortState; }
|
public void AddEscortState(SmartEscortState escortState) { _escortState |= escortState; }
|
||||||
public void RemoveEscortState(SmartEscortState uiEscortState) { _escortState &= ~uiEscortState; }
|
public void RemoveEscortState(SmartEscortState escortState) { _escortState &= ~escortState; }
|
||||||
public void SetAutoAttack(bool on) { _canAutoAttack = on; }
|
public void SetAutoAttack(bool on) { _canAutoAttack = on; }
|
||||||
|
|
||||||
public bool CanCombatMove() { return _canCombatMove; }
|
public bool CanCombatMove() { return _canCombatMove; }
|
||||||
@@ -1110,7 +1110,7 @@ namespace Game.AI
|
|||||||
// Gossip
|
// Gossip
|
||||||
bool _gossipReturn;
|
bool _gossipReturn;
|
||||||
|
|
||||||
public SmartGameObjectAI(GameObject g) : base(g) { }
|
public SmartGameObjectAI(GameObject go) : base(go) { }
|
||||||
|
|
||||||
public override void UpdateAI(uint diff)
|
public override void UpdateAI(uint diff)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ namespace Game
|
|||||||
if (p)
|
if (p)
|
||||||
{
|
{
|
||||||
WorldSession s = p.GetSession();
|
WorldSession s = p.GetSession();
|
||||||
s.KickPlayer(); // mark session to remove at next session list update
|
s.KickPlayer("AccountMgr::DeleteAccount Deleting the account"); // mark session to remove at next session list update
|
||||||
s.LogoutPlayer(false); // logout player without waiting next session list update
|
s.LogoutPlayer(false); // logout player without waiting next session list update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,11 +27,8 @@ namespace Game.Chat
|
|||||||
class CastCommands
|
class CastCommands
|
||||||
{
|
{
|
||||||
[Command("", RBACPermissions.CommandCast)]
|
[Command("", RBACPermissions.CommandCast)]
|
||||||
static bool HandleCastCommand(CommandHandler handler, StringArguments args)
|
static bool HandleCastCommand(CommandHandler handler, SpellInfo spell, string triggeredStr)
|
||||||
{
|
{
|
||||||
if (args.Empty())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Unit target = handler.GetSelectedUnit();
|
Unit target = handler.GetSelectedUnit();
|
||||||
if (!target)
|
if (!target)
|
||||||
{
|
{
|
||||||
@@ -39,22 +36,19 @@ namespace Game.Chat
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
|
if (!CheckSpellExistsAndIsValid(handler, spell))
|
||||||
uint spellId = handler.ExtractSpellIdFromLink(args);
|
|
||||||
if (spellId == 0)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!CheckSpellExistsAndIsValid(handler, spellId))
|
TriggerCastFlags triggerFlags = TriggerCastFlags.None;
|
||||||
return false;
|
if (!triggeredStr.IsEmpty())
|
||||||
|
|
||||||
string triggeredStr = args.NextString();
|
|
||||||
if (!string.IsNullOrEmpty(triggeredStr))
|
|
||||||
{
|
{
|
||||||
if (triggeredStr != "triggered")
|
if ("triggered".Contains(triggeredStr)) // check if "triggered" starts with *triggeredStr (e.g. "trig", "trigger", etc.)
|
||||||
|
triggerFlags = TriggerCastFlags.FullDebugMask;
|
||||||
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.GetSession().GetPlayer().CastSpell(target, spellId, !triggeredStr.IsEmpty());
|
handler.GetSession().GetPlayer().CastSpell(target, spell.Id, new CastSpellExtraArgs(triggerFlags));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +222,11 @@ namespace Game.Chat
|
|||||||
|
|
||||||
static bool CheckSpellExistsAndIsValid(CommandHandler handler, uint spellId)
|
static bool CheckSpellExistsAndIsValid(CommandHandler handler, uint spellId)
|
||||||
{
|
{
|
||||||
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None);
|
return CheckSpellExistsAndIsValid(handler, Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CheckSpellExistsAndIsValid(CommandHandler handler, SpellInfo spellInfo)
|
||||||
|
{
|
||||||
if (spellInfo == null)
|
if (spellInfo == null)
|
||||||
{
|
{
|
||||||
handler.SendSysMessage(CypherStrings.CommandNospellfound);
|
handler.SendSysMessage(CypherStrings.CommandNospellfound);
|
||||||
@@ -237,7 +235,7 @@ namespace Game.Chat
|
|||||||
|
|
||||||
if (!Global.SpellMgr.IsSpellValid(spellInfo, handler.GetPlayer()))
|
if (!Global.SpellMgr.IsSpellValid(spellInfo, handler.GetPlayer()))
|
||||||
{
|
{
|
||||||
handler.SendSysMessage(CypherStrings.CommandSpellBroken, spellId);
|
handler.SendSysMessage(CypherStrings.CommandSpellBroken, spellInfo.Id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ namespace Game.Chat
|
|||||||
target.SetName(newName);
|
target.SetName(newName);
|
||||||
session = target.GetSession();
|
session = target.GetSession();
|
||||||
if (session != null)
|
if (session != null)
|
||||||
session.KickPlayer();
|
session.KickPlayer("HandleCharacterRenameCommand GM Command renaming character");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -463,7 +463,7 @@ namespace Game.Chat
|
|||||||
{
|
{
|
||||||
characterGuid = player.GetGUID();
|
characterGuid = player.GetGUID();
|
||||||
accountId = player.GetSession().GetAccountId();
|
accountId = player.GetSession().GetAccountId();
|
||||||
player.GetSession().KickPlayer();
|
player.GetSession().KickPlayer("HandleCharacterEraseCommand GM Command deleting character");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1331,6 +1331,41 @@ namespace Game.Chat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandGroup("pvp", RBACPermissions.CommandDebug)]
|
||||||
|
class PvpCommands
|
||||||
|
{
|
||||||
|
[Command("warmode", RBACPermissions.CommandDebug)]
|
||||||
|
static bool HandleDebugWarModeFactionBalanceCommand(CommandHandler handler, string command, int rewardValue = 0)
|
||||||
|
{
|
||||||
|
// USAGE: .debug pvp fb <alliance|horde|neutral|off> [pct]
|
||||||
|
// neutral Sets faction balance off.
|
||||||
|
// alliance Set faction balance to alliance.
|
||||||
|
// horde Set faction balance to horde.
|
||||||
|
// off Reset the faction balance and use the calculated value of it
|
||||||
|
switch (command.ToLower())
|
||||||
|
{
|
||||||
|
default: // workaround for Variant of only ExactSequences not being supported
|
||||||
|
handler.SendSysMessage(CypherStrings.BadValue);
|
||||||
|
return false;
|
||||||
|
case "alliance":
|
||||||
|
Global.WorldMgr.SetForcedWarModeFactionBalanceState(TeamId.Alliance, rewardValue);
|
||||||
|
break;
|
||||||
|
case "horde":
|
||||||
|
Global.WorldMgr.SetForcedWarModeFactionBalanceState(TeamId.Horde, rewardValue);
|
||||||
|
break;
|
||||||
|
case "neutral":
|
||||||
|
Global.WorldMgr.SetForcedWarModeFactionBalanceState(TeamId.Neutral);
|
||||||
|
break;
|
||||||
|
case "off":
|
||||||
|
Global.WorldMgr.DisableForcedWarModeFactionBalanceState();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[CommandGroup("send", RBACPermissions.CommandDebugSend)]
|
[CommandGroup("send", RBACPermissions.CommandDebugSend)]
|
||||||
class SendCommands
|
class SendCommands
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ namespace Game.Chat.Commands
|
|||||||
return DoTeleport(handler, new Position(at.Pos.X, at.Pos.Y, at.Pos.Z), at.ContinentID);
|
return DoTeleport(handler, new Position(at.Pos.X, at.Pos.Y, at.Pos.Z), at.ContinentID);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Command("areatrigger", RBACPermissions.CommandGo)]
|
[Command("boss", RBACPermissions.CommandGo)]
|
||||||
static bool HandleGoBossCommand(CommandHandler handler, string[] needles)
|
static bool HandleGoBossCommand(CommandHandler handler, string[] needles)
|
||||||
{
|
{
|
||||||
if (needles.Empty())
|
if (needles.Empty())
|
||||||
|
|||||||
@@ -1115,7 +1115,7 @@ namespace Game.Chat
|
|||||||
else
|
else
|
||||||
handler.SendSysMessage(CypherStrings.CommandKickmessage, playerName);
|
handler.SendSysMessage(CypherStrings.CommandKickmessage, playerName);
|
||||||
|
|
||||||
target.GetSession().KickPlayer();
|
target.GetSession().KickPlayer("HandleKickPlayerCommand GM Command");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,50 @@ namespace Game.Chat
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool CheckAllLinks(string str)
|
||||||
|
{
|
||||||
|
// Step 1: Disallow all control sequences except ||, |H, |h, |c and |r
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
while ((pos = str.IndexOf('|', pos)) != -1)
|
||||||
|
{
|
||||||
|
char next = str[pos + 1];
|
||||||
|
if (next == 'H' || next == 'h' || next == 'c' || next == 'r' || next == '|')
|
||||||
|
pos += 2;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Parse all link sequences
|
||||||
|
// They look like this: |c<color>|H<linktag>:<linkdata>|h[<linktext>]|h|r
|
||||||
|
// - <color> is 8 hex characters AARRGGBB
|
||||||
|
// - <linktag> is arbitrary length [a-z_]
|
||||||
|
// - <linkdata> is arbitrary length, no | contained
|
||||||
|
// - <linktext> is printable
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
while ((pos = str.IndexOf('|', pos)) != -1)
|
||||||
|
{
|
||||||
|
if (str[pos + 1] == '|') // this is an escaped pipe character (||)
|
||||||
|
{
|
||||||
|
pos += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
HyperlinkInfo info = ParseHyperlink(str.Substring(pos));
|
||||||
|
if (info == null)// todo fix me || !ValidateLinkInfo(info))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// tag is fine, find the next one
|
||||||
|
pos = str.Length - info.next.Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// all tags are valid
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static byte toHex(char c) { return (byte)((c >= '0' && c <= '9') ? c - '0' + 0x10 : (c >= 'a' && c <= 'f') ? c - 'a' + 0x1a : 0x00); }
|
static byte toHex(char c) { return (byte)((c >= '0' && c <= '9') ? c - '0' + 0x10 : (c >= 'a' && c <= 'f') ? c - 'a' + 0x1a : 0x00); }
|
||||||
|
|
||||||
//|color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r
|
//|color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r
|
||||||
|
|||||||
@@ -686,6 +686,7 @@ namespace Game
|
|||||||
if (PlayerLoading() || GetPlayer() != null)
|
if (PlayerLoading() || GetPlayer() != null)
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Network, "Player tries to login again, AccountId = {0}", GetAccountId());
|
Log.outError(LogFilter.Network, "Player tries to login again, AccountId = {0}", GetAccountId());
|
||||||
|
KickPlayer("WorldSession::HandlePlayerLoginOpcode Another client logging in");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -695,7 +696,7 @@ namespace Game
|
|||||||
if (!_legitCharacters.Contains(playerLogin.Guid))
|
if (!_legitCharacters.Contains(playerLogin.Guid))
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Network, "Account ({0}) can't login with that character ({1}).", GetAccountId(), playerLogin.Guid.ToString());
|
Log.outError(LogFilter.Network, "Account ({0}) can't login with that character ({1}).", GetAccountId(), playerLogin.Guid.ToString());
|
||||||
KickPlayer();
|
KickPlayer("WorldSession::HandlePlayerLoginOpcode Trying to login with a character of another account");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,7 +707,7 @@ namespace Game
|
|||||||
{
|
{
|
||||||
if (!PlayerLoading() || GetPlayer())
|
if (!PlayerLoading() || GetPlayer())
|
||||||
{
|
{
|
||||||
KickPlayer();
|
KickPlayer("WorldSession::HandleContinuePlayerLogin incorrect player state when logging in");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,7 +727,7 @@ namespace Game
|
|||||||
if (!pCurrChar.LoadFromDB(playerGuid, holder))
|
if (!pCurrChar.LoadFromDB(playerGuid, holder))
|
||||||
{
|
{
|
||||||
SetPlayer(null);
|
SetPlayer(null);
|
||||||
KickPlayer();
|
KickPlayer("WorldSession::HandlePlayerLogin Player::LoadFromDB failed");
|
||||||
m_playerLoading.Clear();
|
m_playerLoading.Clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1061,7 +1062,7 @@ namespace Game
|
|||||||
{
|
{
|
||||||
if (!PlayerLoading() || GetPlayer())
|
if (!PlayerLoading() || GetPlayer())
|
||||||
{
|
{
|
||||||
KickPlayer();
|
KickPlayer("WorldSession::AbortLogin incorrect player state when logging in");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1220,7 +1221,7 @@ namespace Game
|
|||||||
{
|
{
|
||||||
Log.outError(LogFilter.Network, "Account {0}, IP: {1} tried to rename character {2}, but it does not belong to their account!",
|
Log.outError(LogFilter.Network, "Account {0}, IP: {1} tried to rename character {2}, but it does not belong to their account!",
|
||||||
GetAccountId(), GetRemoteAddress(), request.RenameInfo.Guid.ToString());
|
GetAccountId(), GetRemoteAddress(), request.RenameInfo.Guid.ToString());
|
||||||
KickPlayer();
|
KickPlayer("WorldSession::HandleCharRenameOpcode rename character from a different account");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1402,7 +1403,7 @@ namespace Game
|
|||||||
{
|
{
|
||||||
Log.outError(LogFilter.Network, "Account {0}, IP: {1} tried to customise {2}, but it does not belong to their account!",
|
Log.outError(LogFilter.Network, "Account {0}, IP: {1} tried to customise {2}, but it does not belong to their account!",
|
||||||
GetAccountId(), GetRemoteAddress(), packet.CustomizeInfo.CharGUID.ToString());
|
GetAccountId(), GetRemoteAddress(), packet.CustomizeInfo.CharGUID.ToString());
|
||||||
KickPlayer();
|
KickPlayer("WorldSession::HandleCharCustomize Trying to customise character of another account");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1673,7 +1674,7 @@ namespace Game
|
|||||||
{
|
{
|
||||||
Log.outError(LogFilter.Network, "Account {0}, IP: {1} tried to factionchange character {2}, but it does not belong to their account!",
|
Log.outError(LogFilter.Network, "Account {0}, IP: {1} tried to factionchange character {2}, but it does not belong to their account!",
|
||||||
GetAccountId(), GetRemoteAddress(), packet.RaceOrFactionChangeInfo.Guid.ToString());
|
GetAccountId(), GetRemoteAddress(), packet.RaceOrFactionChangeInfo.Guid.ToString());
|
||||||
KickPlayer();
|
KickPlayer("WorldSession::HandleCharFactionOrRaceChange Trying to change faction of character of another account");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ namespace Game
|
|||||||
if (string.IsNullOrEmpty(packet.Info.Target))
|
if (string.IsNullOrEmpty(packet.Info.Target))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!ValidateHyperlinksAndMaybeKick(packet.Info.Subject) || !ValidateHyperlinksAndMaybeKick(packet.Info.Body))
|
||||||
|
return;
|
||||||
|
|
||||||
Player player = GetPlayer();
|
Player player = GetPlayer();
|
||||||
if (player.GetLevel() < WorldConfig.GetIntValue(WorldCfg.MailLevelReq))
|
if (player.GetLevel() < WorldConfig.GetIntValue(WorldCfg.MailLevelReq))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -541,7 +541,7 @@ namespace Game
|
|||||||
{
|
{
|
||||||
Log.outDebug(LogFilter.Server, "Player {0} from account id {1} kicked for incorrect speed (must be {2} instead {3})",
|
Log.outDebug(LogFilter.Server, "Player {0} from account id {1} kicked for incorrect speed (must be {2} instead {3})",
|
||||||
GetPlayer().GetName(), GetPlayer().GetSession().GetAccountId(), GetPlayer().GetSpeed(move_type), packet.Speed);
|
GetPlayer().GetName(), GetPlayer().GetSession().GetAccountId(), GetPlayer().GetSpeed(move_type), packet.Speed);
|
||||||
GetPlayer().GetSession().KickPlayer();
|
GetPlayer().GetSession().KickPlayer("WorldSession::HandleForceSpeedChangeAck Incorrect speed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -675,7 +675,7 @@ namespace Game
|
|||||||
if (Math.Abs(expectedModMagnitude - setModMovementForceMagnitudeAck.Speed) > 0.01f)
|
if (Math.Abs(expectedModMagnitude - setModMovementForceMagnitudeAck.Speed) > 0.01f)
|
||||||
{
|
{
|
||||||
Log.outDebug(LogFilter.Misc, $"Player {_player.GetName()} from account id {_player.GetSession().GetAccountId()} kicked for incorrect movement force magnitude (must be {expectedModMagnitude} instead {setModMovementForceMagnitudeAck.Speed})");
|
Log.outDebug(LogFilter.Misc, $"Player {_player.GetName()} from account id {_player.GetSession().GetAccountId()} kicked for incorrect movement force magnitude (must be {expectedModMagnitude} instead {setModMovementForceMagnitudeAck.Speed})");
|
||||||
_player.GetSession().KickPlayer();
|
_player.GetSession().KickPlayer("WorldSession::HandleMoveSetModMovementForceMagnitudeAck Incorrect magnitude");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,14 +52,24 @@ namespace Game.Movement
|
|||||||
owner.CombatStopWithPets();
|
owner.CombatStopWithPets();
|
||||||
owner.AddUnitFlag(UnitFlags.RemoveClientControl | UnitFlags.TaxiFlight);
|
owner.AddUnitFlag(UnitFlags.RemoveClientControl | UnitFlags.TaxiFlight);
|
||||||
|
|
||||||
MoveSplineInit init = new(owner);
|
|
||||||
uint end = GetPathAtMapEnd();
|
uint end = GetPathAtMapEnd();
|
||||||
init.args.path = new Vector3[end];
|
uint currentNodeId = GetCurrentNode();
|
||||||
for (int i = (int)GetCurrentNode(); i != end; ++i)
|
|
||||||
|
if (currentNodeId == end)
|
||||||
|
{
|
||||||
|
Log.outDebug(LogFilter.Movement, $"FlightPathMovementGenerator::DoReset: trying to start a flypath from the end point. {owner.GetDebugInfo()}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveSplineInit init = new(owner);
|
||||||
|
// Providing a starting vertex since the taxi paths do not provide such
|
||||||
|
init.Path().Add(new Vector3(owner.GetPositionX(), owner.GetPositionY(), owner.GetPositionZ()));
|
||||||
|
for (int i = (int)currentNodeId; i != (uint)end; ++i)
|
||||||
{
|
{
|
||||||
Vector3 vertice = new(_path[i].Loc.X, _path[i].Loc.Y, _path[i].Loc.Z);
|
Vector3 vertice = new(_path[i].Loc.X, _path[i].Loc.Y, _path[i].Loc.Z);
|
||||||
init.args.path[i] = vertice;
|
init.Path().Add(vertice);
|
||||||
}
|
}
|
||||||
|
|
||||||
init.SetFirstPointId((int)GetCurrentNode());
|
init.SetFirstPointId((int)GetCurrentNode());
|
||||||
init.SetFly();
|
init.SetFly();
|
||||||
init.SetSmooth();
|
init.SetSmooth();
|
||||||
|
|||||||
@@ -798,11 +798,10 @@ namespace Game.Movement
|
|||||||
|
|
||||||
MoveSplineInit init = new(_owner);
|
MoveSplineInit init = new(_owner);
|
||||||
|
|
||||||
init.args.path = new Vector3[stepCount + 1];
|
|
||||||
|
|
||||||
// add the owner's current position as starting point as it gets removed after entering the cycle
|
// add the owner's current position as starting point as it gets removed after entering the cycle
|
||||||
init.args.path[0] = new Vector3(_owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ());
|
init.Path().Add(new Vector3(_owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ()));
|
||||||
for (byte i = 1; i < stepCount; angle += step, ++i)
|
|
||||||
|
for (byte i = 0; i < stepCount; angle += step, ++i)
|
||||||
{
|
{
|
||||||
Vector3 point = new();
|
Vector3 point = new();
|
||||||
point.X = (float)(x + radius * Math.Cos(angle));
|
point.X = (float)(x + radius * Math.Cos(angle));
|
||||||
@@ -813,7 +812,7 @@ namespace Game.Movement
|
|||||||
else
|
else
|
||||||
point.Z = _owner.GetMapHeight(point.X, point.Y, z) + _owner.GetHoverOffset();
|
point.Z = _owner.GetMapHeight(point.X, point.Y, z) + _owner.GetHoverOffset();
|
||||||
|
|
||||||
init.args.path[i] = point;
|
init.Path().Add(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_owner.IsFlying())
|
if (_owner.IsFlying())
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace Game.Movement
|
|||||||
effect_start_time = 0;
|
effect_start_time = 0;
|
||||||
spell_effect_extra = args.spellEffectExtra;
|
spell_effect_extra = args.spellEffectExtra;
|
||||||
anim_tier = args.animTier;
|
anim_tier = args.animTier;
|
||||||
splineIsFacingOnly = args.path.Length == 2 && args.facing.type != MonsterMoveType.Normal && ((args.path[1] - args.path[0]).Length() < 0.1f);
|
splineIsFacingOnly = args.path.Count == 2 && args.facing.type != MonsterMoveType.Normal && ((args.path[1] - args.path[0]).Length() < 0.1f);
|
||||||
|
|
||||||
// Check if its a stop spline
|
// Check if its a stop spline
|
||||||
if (args.flags.HasFlag(SplineFlag.Done))
|
if (args.flags.HasFlag(SplineFlag.Done))
|
||||||
@@ -91,11 +91,11 @@ namespace Game.Movement
|
|||||||
int cyclic_point = 0;
|
int cyclic_point = 0;
|
||||||
if (splineflags.HasFlag(SplineFlag.EnterCycle))
|
if (splineflags.HasFlag(SplineFlag.EnterCycle))
|
||||||
cyclic_point = 1; // shouldn't be modified, came from client
|
cyclic_point = 1; // shouldn't be modified, came from client
|
||||||
spline.InitCyclicSpline(args.path, args.path.Length, modes[Convert.ToInt32(args.flags.IsSmooth())], cyclic_point, args.initialOrientation);
|
spline.InitCyclicSpline(args.path.ToArray(), args.path.Count, modes[Convert.ToInt32(args.flags.IsSmooth())], cyclic_point, args.initialOrientation);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
spline.InitSpline(args.path, args.path.Length, modes[Convert.ToInt32(args.flags.IsSmooth())], args.initialOrientation);
|
spline.InitSpline(args.path.ToArray(), args.path.Count, modes[Convert.ToInt32(args.flags.IsSmooth())], args.initialOrientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
// init spline timestamps
|
// init spline timestamps
|
||||||
@@ -298,7 +298,7 @@ namespace Game.Movement
|
|||||||
splineflags.SetUnsetFlag(SplineFlag.EnterCycle, false);
|
splineflags.SetUnsetFlag(SplineFlag.EnterCycle, false);
|
||||||
|
|
||||||
MoveSplineInitArgs args = new(spline.GetPointCount());
|
MoveSplineInitArgs args = new(spline.GetPointCount());
|
||||||
args.path = spline.GetPoints().AsSpan().Slice(spline.First() + 1, spline.Last()).ToArray();
|
args.path.AddRange(spline.GetPoints().AsSpan().Slice(spline.First() + 1, spline.Last()).ToArray());
|
||||||
args.facing = facing;
|
args.facing = facing;
|
||||||
args.flags = splineflags;
|
args.flags = splineflags;
|
||||||
args.path_Idx_offset = point_Idx_offset;
|
args.path_Idx_offset = point_Idx_offset;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ using Framework.Constants;
|
|||||||
using Game.Entities;
|
using Game.Entities;
|
||||||
using Game.Networking.Packets;
|
using Game.Networking.Packets;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Game.Movement
|
namespace Game.Movement
|
||||||
@@ -94,7 +95,7 @@ namespace Game.Movement
|
|||||||
}
|
}
|
||||||
|
|
||||||
// should i do the things that user should do? - no.
|
// should i do the things that user should do? - no.
|
||||||
if (args.path.Length == 0)
|
if (args.path.Count == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// correct first vertex
|
// correct first vertex
|
||||||
@@ -246,9 +247,9 @@ namespace Game.Movement
|
|||||||
}
|
}
|
||||||
|
|
||||||
args.path_Idx_offset = 0;
|
args.path_Idx_offset = 0;
|
||||||
args.path = new Vector3[2];
|
args.path.Add(default);
|
||||||
TransportPathTransform transform = new(unit, args.TransformForTransport);
|
TransportPathTransform transform = new(unit, args.TransformForTransport);
|
||||||
args.path[1] = transform.Calc(dest);
|
args.path.Add(transform.Calc(dest));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetFall()
|
public void SetFall()
|
||||||
@@ -282,10 +283,9 @@ namespace Game.Movement
|
|||||||
public void MovebyPath(Vector3[] controls, int path_offset = 0)
|
public void MovebyPath(Vector3[] controls, int path_offset = 0)
|
||||||
{
|
{
|
||||||
args.path_Idx_offset = path_offset;
|
args.path_Idx_offset = path_offset;
|
||||||
args.path = new Vector3[controls.Length];
|
|
||||||
TransportPathTransform transform = new(unit, args.TransformForTransport);
|
TransportPathTransform transform = new(unit, args.TransformForTransport);
|
||||||
for (var i = 0; i < controls.Length; i++)
|
for (var i = 0; i < controls.Length; i++)
|
||||||
args.path[i] = transform.Calc(controls[i]);
|
args.path.Add(transform.Calc(controls[i]));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,7 +333,7 @@ namespace Game.Movement
|
|||||||
args.spellEffectExtra.Set(spellEffectExtraData);
|
args.spellEffectExtra.Set(spellEffectExtraData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector3[] Path() { return args.path; }
|
public List<Vector3> Path() { return args.path; }
|
||||||
|
|
||||||
public MoveSplineInitArgs args = new();
|
public MoveSplineInitArgs args = new();
|
||||||
Unit unit;
|
Unit unit;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
using Framework.Dynamic;
|
using Framework.Dynamic;
|
||||||
using Game.Entities;
|
using Game.Entities;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Game.Movement
|
namespace Game.Movement
|
||||||
@@ -33,10 +34,9 @@ namespace Game.Movement
|
|||||||
initialOrientation = 0.0f;
|
initialOrientation = 0.0f;
|
||||||
HasVelocity = false;
|
HasVelocity = false;
|
||||||
TransformForTransport = true;
|
TransformForTransport = true;
|
||||||
path = new Vector3[path_capacity];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector3[] path;
|
public List<Vector3> path = new();
|
||||||
public FacingInfo facing = new();
|
public FacingInfo facing = new();
|
||||||
public MoveSplineFlag flags = new();
|
public MoveSplineFlag flags = new();
|
||||||
public int path_Idx_offset;
|
public int path_Idx_offset;
|
||||||
@@ -65,7 +65,7 @@ namespace Game.Movement
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CHECK(path.Length > 1))
|
if (!CHECK(path.Count > 1))
|
||||||
return false;
|
return false;
|
||||||
if (!CHECK(velocity >= 0.01f))
|
if (!CHECK(velocity >= 0.01f))
|
||||||
return false;
|
return false;
|
||||||
@@ -78,8 +78,8 @@ namespace Game.Movement
|
|||||||
|
|
||||||
bool _checkPathLengths()
|
bool _checkPathLengths()
|
||||||
{
|
{
|
||||||
if (path.Length > 2 || facing.type == Framework.Constants.MonsterMoveType.Normal)
|
if (path.Count > 2 || facing.type == Framework.Constants.MonsterMoveType.Normal)
|
||||||
for (uint i = 0; i < path.Length - 1; ++i)
|
for (int i = 0; i < path.Count - 1; ++i)
|
||||||
if ((path[i + 1] - path[i]).Length() < 0.1f)
|
if ((path[i + 1] - path[i]).Length() < 0.1f)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ namespace Game
|
|||||||
if (session.PlayerLoading())
|
if (session.PlayerLoading())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
session.KickPlayer();
|
session.KickPlayer("World::RemoveSession");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -208,7 +208,7 @@ namespace Game
|
|||||||
// if player is in loading and want to load again, return
|
// if player is in loading and want to load again, return
|
||||||
if (!RemoveSession(s.GetAccountId()))
|
if (!RemoveSession(s.GetAccountId()))
|
||||||
{
|
{
|
||||||
s.KickPlayer();
|
s.KickPlayer("World::AddSession_ Couldn't remove the other session while on loading screen");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1243,18 +1243,17 @@ namespace Game
|
|||||||
Log.outInfo(LogFilter.ServerLoading, @"VMap data directory is: {0}\vmaps", GetDataPath());
|
Log.outInfo(LogFilter.ServerLoading, @"VMap data directory is: {0}\vmaps", GetDataPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetForcedWarModeFactionBalanceState(int team, int reward)
|
public void SetForcedWarModeFactionBalanceState(int team, int reward = 0)
|
||||||
{
|
{
|
||||||
_warModeDominantFaction = team;
|
_warModeDominantFaction = team;
|
||||||
_warModeOutnumberedFactionReward = reward;
|
_warModeOutnumberedFactionReward = reward;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DisableForcedWarModeFactionBalanceState()
|
public void DisableForcedWarModeFactionBalanceState()
|
||||||
{
|
{
|
||||||
UpdateWarModeRewardValues();
|
UpdateWarModeRewardValues();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void LoadAutobroadcasts()
|
public void LoadAutobroadcasts()
|
||||||
{
|
{
|
||||||
uint oldMSTime = Time.GetMSTime();
|
uint oldMSTime = Time.GetMSTime();
|
||||||
@@ -1597,7 +1596,7 @@ namespace Game
|
|||||||
|
|
||||||
// session not removed at kick and will removed in next update tick
|
// session not removed at kick and will removed in next update tick
|
||||||
foreach (var session in m_sessions.Values)
|
foreach (var session in m_sessions.Values)
|
||||||
session.KickPlayer();
|
session.KickPlayer("World::KickAll");
|
||||||
}
|
}
|
||||||
|
|
||||||
void KickAllLess(AccountTypes sec)
|
void KickAllLess(AccountTypes sec)
|
||||||
@@ -1605,7 +1604,7 @@ namespace Game
|
|||||||
// session not removed at kick and will removed in next update tick
|
// session not removed at kick and will removed in next update tick
|
||||||
foreach (var session in m_sessions.Values)
|
foreach (var session in m_sessions.Values)
|
||||||
if (session.GetSecurity() < sec)
|
if (session.GetSecurity() < sec)
|
||||||
session.KickPlayer();
|
session.KickPlayer("World::KickAllLess");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ban an account or ban an IP address, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban
|
/// Ban an account or ban an IP address, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban
|
||||||
@@ -1689,7 +1688,7 @@ namespace Game
|
|||||||
if (sess)
|
if (sess)
|
||||||
{
|
{
|
||||||
if (sess.GetPlayerName() != author)
|
if (sess.GetPlayerName() != author)
|
||||||
sess.KickPlayer();
|
sess.KickPlayer("World::BanAccount Banning account");
|
||||||
}
|
}
|
||||||
} while (resultAccounts.NextRow());
|
} while (resultAccounts.NextRow());
|
||||||
|
|
||||||
@@ -1762,7 +1761,7 @@ namespace Game
|
|||||||
DB.Characters.CommitTransaction(trans);
|
DB.Characters.CommitTransaction(trans);
|
||||||
|
|
||||||
if (pBanned)
|
if (pBanned)
|
||||||
pBanned.GetSession().KickPlayer();
|
pBanned.GetSession().KickPlayer("World::BanCharacter Banning character");
|
||||||
|
|
||||||
return BanReturn.Success;
|
return BanReturn.Success;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ using System.IO;
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Game.Chat;
|
||||||
|
|
||||||
namespace Game
|
namespace Game
|
||||||
{
|
{
|
||||||
@@ -419,8 +420,10 @@ namespace Game
|
|||||||
|
|
||||||
public void AddInstanceConnection(WorldSocket sock) { m_Socket[(int)ConnectionType.Instance] = sock; }
|
public void AddInstanceConnection(WorldSocket sock) { m_Socket[(int)ConnectionType.Instance] = sock; }
|
||||||
|
|
||||||
public void KickPlayer()
|
public void KickPlayer(string reason)
|
||||||
{
|
{
|
||||||
|
Log.outInfo(LogFilter.Network, $"Account: {GetAccountId()} Character: '{(_player ? _player.GetName() : "<none>")}' {(_player ? _player.GetGUID() : "")} kicked with reason: {reason}");
|
||||||
|
|
||||||
for (byte i = 0; i < 2; ++i)
|
for (byte i = 0; i < 2; ++i)
|
||||||
{
|
{
|
||||||
if (m_Socket[i] != null)
|
if (m_Socket[i] != null)
|
||||||
@@ -588,6 +591,19 @@ namespace Game
|
|||||||
return m_muteTime <= GameTime.GetGameTime();
|
return m_muteTime <= GameTime.GetGameTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ValidateHyperlinksAndMaybeKick(string str)
|
||||||
|
{
|
||||||
|
if (Hyperlink.CheckAllLinks(str))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Log.outError(LogFilter.Network, $"Player {GetPlayer().GetName()} {GetPlayer().GetGUID()} sent a message with an invalid link:\n{str}");
|
||||||
|
|
||||||
|
if (WorldConfig.GetIntValue(WorldCfg.ChatStrictLinkCheckingKick) != 0)
|
||||||
|
KickPlayer("WorldSession::ValidateHyperlinksAndMaybeKick Invalid chat link");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public bool DisallowHyperlinksAndMaybeKick(string str)
|
public bool DisallowHyperlinksAndMaybeKick(string str)
|
||||||
{
|
{
|
||||||
if (!str.Contains('|'))
|
if (!str.Contains('|'))
|
||||||
@@ -596,7 +612,7 @@ namespace Game
|
|||||||
Log.outError(LogFilter.Network, $"Player {GetPlayer().GetName()} ({GetPlayer().GetGUID()}) sent a message which illegally contained a hyperlink:\n{str}");
|
Log.outError(LogFilter.Network, $"Player {GetPlayer().GetName()} ({GetPlayer().GetGUID()}) sent a message which illegally contained a hyperlink:\n{str}");
|
||||||
|
|
||||||
if (WorldConfig.GetIntValue(WorldCfg.ChatStrictLinkCheckingKick) != 0)
|
if (WorldConfig.GetIntValue(WorldCfg.ChatStrictLinkCheckingKick) != 0)
|
||||||
KickPlayer();
|
KickPlayer("WorldSession::DisallowHyperlinksAndMaybeKick Illegal chat link");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ namespace Game
|
|||||||
{
|
{
|
||||||
Log.outWarn(LogFilter.Warden, "{0} (latency: {1}, IP: {2}) exceeded Warden module response delay for more than {3} - disconnecting client",
|
Log.outWarn(LogFilter.Warden, "{0} (latency: {1}, IP: {2}) exceeded Warden module response delay for more than {3} - disconnecting client",
|
||||||
_session.GetPlayerInfo(), _session.GetLatency(), _session.GetRemoteAddress(), Time.secsToTimeString(maxClientResponseDelay, TimeFormat.ShortText));
|
_session.GetPlayerInfo(), _session.GetLatency(), _session.GetRemoteAddress(), Time.secsToTimeString(maxClientResponseDelay, TimeFormat.ShortText));
|
||||||
_session.KickPlayer();
|
_session.KickPlayer("Warden::Update Warden module response delay exceeded");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_clientResponseTimer += diff;
|
_clientResponseTimer += diff;
|
||||||
@@ -172,7 +172,7 @@ namespace Game
|
|||||||
case WardenActions.Log:
|
case WardenActions.Log:
|
||||||
return "None";
|
return "None";
|
||||||
case WardenActions.Kick:
|
case WardenActions.Kick:
|
||||||
_session.KickPlayer();
|
_session.KickPlayer("Warden::Penalty");
|
||||||
return "Kick";
|
return "Kick";
|
||||||
case WardenActions.Ban:
|
case WardenActions.Ban:
|
||||||
{
|
{
|
||||||
|
|||||||
+105
-158
@@ -33,21 +33,21 @@ namespace Scripts.World.NpcSpecial
|
|||||||
{
|
{
|
||||||
enum SpawnType
|
enum SpawnType
|
||||||
{
|
{
|
||||||
TripwireRooftop, // no warning, summon Creature at smaller range
|
Tripwire, // no warning, summon Creature at smaller range
|
||||||
AlarmBot, // cast guards mark and summon npc - if player shows up with that buff duration < 5 seconds attack
|
AlarmBot, // cast guards mark and summon npc - if player shows up with that buff duration < 5 seconds attack
|
||||||
}
|
}
|
||||||
|
|
||||||
class SpawnAssociation
|
class AirForceSpawn
|
||||||
{
|
{
|
||||||
public SpawnAssociation(uint _thisCreatureEntry, uint _spawnedCreatureEntry, SpawnType _spawnType)
|
public AirForceSpawn(uint _myEntry, uint _otherEntry, SpawnType _spawnType)
|
||||||
{
|
{
|
||||||
thisCreatureEntry = _thisCreatureEntry;
|
myEntry = _myEntry;
|
||||||
spawnedCreatureEntry = _spawnedCreatureEntry;
|
otherEntry = _otherEntry;
|
||||||
spawnType = _spawnType;
|
spawnType = _spawnType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint thisCreatureEntry;
|
public uint myEntry;
|
||||||
public uint spawnedCreatureEntry;
|
public uint otherEntry;
|
||||||
public SpawnType spawnType;
|
public SpawnType spawnType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,39 +364,39 @@ namespace Scripts.World.NpcSpecial
|
|||||||
|
|
||||||
struct Misc
|
struct Misc
|
||||||
{
|
{
|
||||||
public static SpawnAssociation[] spawnAssociations =
|
public static AirForceSpawn[] AirforceSpawns =
|
||||||
{
|
{
|
||||||
new SpawnAssociation(2614, 15241, SpawnType.AlarmBot), //Air Force Alarm Bot (Alliance)
|
new AirForceSpawn(2614, 15241, SpawnType.AlarmBot), //Air Force Alarm Bot (Alliance)
|
||||||
new SpawnAssociation(2615, 15242, SpawnType.AlarmBot), //Air Force Alarm Bot (Horde)
|
new AirForceSpawn(2615, 15242, SpawnType.AlarmBot), //Air Force Alarm Bot (Horde)
|
||||||
new SpawnAssociation(21974, 21976, SpawnType.AlarmBot), //Air Force Alarm Bot (Area 52)
|
new AirForceSpawn(21974, 21976, SpawnType.AlarmBot), //Air Force Alarm Bot (Area 52)
|
||||||
new SpawnAssociation(21993, 15242, SpawnType.AlarmBot), //Air Force Guard Post (Horde - Bat Rider)
|
new AirForceSpawn(21993, 15242, SpawnType.AlarmBot), //Air Force Guard Post (Horde - Bat Rider)
|
||||||
new SpawnAssociation(21996, 15241, SpawnType.AlarmBot), //Air Force Guard Post (Alliance - Gryphon)
|
new AirForceSpawn(21996, 15241, SpawnType.AlarmBot), //Air Force Guard Post (Alliance - Gryphon)
|
||||||
new SpawnAssociation(21997, 21976, SpawnType.AlarmBot), //Air Force Guard Post (Goblin - Area 52 - Zeppelin)
|
new AirForceSpawn(21997, 21976, SpawnType.AlarmBot), //Air Force Guard Post (Goblin - Area 52 - Zeppelin)
|
||||||
new SpawnAssociation(21999, 15241, SpawnType.TripwireRooftop), //Air Force Trip Wire - Rooftop (Alliance)
|
new AirForceSpawn(21999, 15241, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Alliance)
|
||||||
new SpawnAssociation(22001, 15242, SpawnType.TripwireRooftop), //Air Force Trip Wire - Rooftop (Horde)
|
new AirForceSpawn(22001, 15242, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Horde)
|
||||||
new SpawnAssociation(22002, 15242, SpawnType.TripwireRooftop), //Air Force Trip Wire - Ground (Horde)
|
new AirForceSpawn(22002, 15242, SpawnType.Tripwire), //Air Force Trip Wire - Ground (Horde)
|
||||||
new SpawnAssociation(22003, 15241, SpawnType.TripwireRooftop), //Air Force Trip Wire - Ground (Alliance)
|
new AirForceSpawn(22003, 15241, SpawnType.Tripwire), //Air Force Trip Wire - Ground (Alliance)
|
||||||
new SpawnAssociation(22063, 21976, SpawnType.TripwireRooftop), //Air Force Trip Wire - Rooftop (Goblin - Area 52)
|
new AirForceSpawn(22063, 21976, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Goblin - Area 52)
|
||||||
new SpawnAssociation(22065, 22064, SpawnType.AlarmBot), //Air Force Guard Post (Ethereal - Stormspire)
|
new AirForceSpawn(22065, 22064, SpawnType.AlarmBot), //Air Force Guard Post (Ethereal - Stormspire)
|
||||||
new SpawnAssociation(22066, 22067, SpawnType.AlarmBot), //Air Force Guard Post (Scryer - Dragonhawk)
|
new AirForceSpawn(22066, 22067, SpawnType.AlarmBot), //Air Force Guard Post (Scryer - Dragonhawk)
|
||||||
new SpawnAssociation(22068, 22064, SpawnType.TripwireRooftop), //Air Force Trip Wire - Rooftop (Ethereal - Stormspire)
|
new AirForceSpawn(22068, 22064, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Ethereal - Stormspire)
|
||||||
new SpawnAssociation(22069, 22064, SpawnType.AlarmBot), //Air Force Alarm Bot (Stormspire)
|
new AirForceSpawn(22069, 22064, SpawnType.AlarmBot), //Air Force Alarm Bot (Stormspire)
|
||||||
new SpawnAssociation(22070, 22067, SpawnType.TripwireRooftop), //Air Force Trip Wire - Rooftop (Scryer)
|
new AirForceSpawn(22070, 22067, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Scryer)
|
||||||
new SpawnAssociation(22071, 22067, SpawnType.AlarmBot), //Air Force Alarm Bot (Scryer)
|
new AirForceSpawn(22071, 22067, SpawnType.AlarmBot), //Air Force Alarm Bot (Scryer)
|
||||||
new SpawnAssociation(22078, 22077, SpawnType.AlarmBot), //Air Force Alarm Bot (Aldor)
|
new AirForceSpawn(22078, 22077, SpawnType.AlarmBot), //Air Force Alarm Bot (Aldor)
|
||||||
new SpawnAssociation(22079, 22077, SpawnType.AlarmBot), //Air Force Guard Post (Aldor - Gryphon)
|
new AirForceSpawn(22079, 22077, SpawnType.AlarmBot), //Air Force Guard Post (Aldor - Gryphon)
|
||||||
new SpawnAssociation(22080, 22077, SpawnType.TripwireRooftop), //Air Force Trip Wire - Rooftop (Aldor)
|
new AirForceSpawn(22080, 22077, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Aldor)
|
||||||
new SpawnAssociation(22086, 22085, SpawnType.AlarmBot), //Air Force Alarm Bot (Sporeggar)
|
new AirForceSpawn(22086, 22085, SpawnType.AlarmBot), //Air Force Alarm Bot (Sporeggar)
|
||||||
new SpawnAssociation(22087, 22085, SpawnType.AlarmBot), //Air Force Guard Post (Sporeggar - Spore Bat)
|
new AirForceSpawn(22087, 22085, SpawnType.AlarmBot), //Air Force Guard Post (Sporeggar - Spore Bat)
|
||||||
new SpawnAssociation(22088, 22085, SpawnType.TripwireRooftop), //Air Force Trip Wire - Rooftop (Sporeggar)
|
new AirForceSpawn(22088, 22085, SpawnType.Tripwire), //Air Force Trip Wire - Rooftop (Sporeggar)
|
||||||
new SpawnAssociation(22090, 22089, SpawnType.AlarmBot), //Air Force Guard Post (Toshley's Station - Flying Machine)
|
new AirForceSpawn(22090, 22089, SpawnType.AlarmBot), //Air Force Guard Post (Toshley's Station - Flying Machine)
|
||||||
new SpawnAssociation(22124, 22122, SpawnType.AlarmBot), //Air Force Alarm Bot (Cenarion)
|
new AirForceSpawn(22124, 22122, SpawnType.AlarmBot), //Air Force Alarm Bot (Cenarion)
|
||||||
new SpawnAssociation(22125, 22122, SpawnType.AlarmBot), //Air Force Guard Post (Cenarion - Stormcrow)
|
new AirForceSpawn(22125, 22122, SpawnType.AlarmBot), //Air Force Guard Post (Cenarion - Stormcrow)
|
||||||
new SpawnAssociation(22126, 22122, SpawnType.AlarmBot) //Air Force Trip Wire - Rooftop (Cenarion Expedition)
|
new AirForceSpawn(22126, 22122, SpawnType.AlarmBot) //Air Force Trip Wire - Rooftop (Cenarion Expedition)
|
||||||
};
|
};
|
||||||
|
|
||||||
public const float RangeTripwire = 15.0f;
|
public const float RangeTripwire = 15.0f;
|
||||||
public const float RangeGuardsMark = 100.0f;
|
public const float RangeAlarmbot = 100.0f;
|
||||||
|
|
||||||
//ChickenCluck
|
//ChickenCluck
|
||||||
public const uint FactionFriendly = 35;
|
public const uint FactionFriendly = 35;
|
||||||
@@ -467,153 +467,100 @@ namespace Scripts.World.NpcSpecial
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Script]
|
[Script]
|
||||||
class npc_air_force_bots : ScriptedAI
|
class npc_air_force_bots : NullCreatureAI
|
||||||
{
|
{
|
||||||
SpawnAssociation SpawnAssoc;
|
AirForceSpawn _spawn;
|
||||||
ObjectGuid SpawnedGUID;
|
ObjectGuid _myGuard;
|
||||||
List<ObjectGuid> inLineOfSightSinceLastUpdate = new();
|
List<ObjectGuid> _toAttack = new();
|
||||||
|
|
||||||
public npc_air_force_bots(Creature creature) : base(creature)
|
static AirForceSpawn FindSpawnFor(uint entry)
|
||||||
{
|
{
|
||||||
SpawnAssoc = null;
|
foreach (AirForceSpawn spawn in Misc.AirforceSpawns)
|
||||||
SpawnedGUID.Clear();
|
|
||||||
|
|
||||||
// find the correct spawnhandling
|
|
||||||
foreach (var association in Misc.spawnAssociations)
|
|
||||||
{
|
{
|
||||||
if (association.thisCreatureEntry == creature.GetEntry())
|
if (spawn.myEntry == entry)
|
||||||
{
|
{
|
||||||
SpawnAssoc = association;
|
Cypher.Assert(Global.ObjectMgr.GetCreatureTemplate(spawn.otherEntry) != null, $"Invalid creature entry {spawn.otherEntry} in 'npc_air_force_bots' script");
|
||||||
break;
|
return spawn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Cypher.Assert(false, $"Unhandled creature with entry {entry} is assigned 'npc_air_force_bots' script");
|
||||||
if (SpawnAssoc == null)
|
|
||||||
Log.outError(LogFilter.Sql, "TCSR: Creature template entry {0} has ScriptName npc_air_force_bots, but it's not handled by that script", creature.GetEntry());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CreatureTemplate spawnedTemplate = Global.ObjectMgr.GetCreatureTemplate(SpawnAssoc.spawnedCreatureEntry);
|
|
||||||
if (spawnedTemplate == null)
|
|
||||||
{
|
|
||||||
Log.outError(LogFilter.Sql, "TCSR: Creature template entry {0} does not exist in DB, which is required by npc_air_force_bots", SpawnAssoc.spawnedCreatureEntry);
|
|
||||||
SpawnAssoc = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Reset() { }
|
|
||||||
|
|
||||||
Creature SummonGuard()
|
|
||||||
{
|
|
||||||
Creature summoned = me.SummonCreature(SpawnAssoc.spawnedCreatureEntry, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.TimedDespawnOutOfCombat, 300000);
|
|
||||||
|
|
||||||
if (summoned)
|
|
||||||
SpawnedGUID = summoned.GetGUID();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.outError(LogFilter.Sql, "npc_air_force_bots: wasn't able to spawn Creature {0}", SpawnAssoc.spawnedCreatureEntry);
|
|
||||||
SpawnAssoc = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return summoned;
|
|
||||||
}
|
|
||||||
|
|
||||||
Creature GetSummonedGuard()
|
|
||||||
{
|
|
||||||
Creature creature = ObjectAccessor.GetCreature(me, SpawnedGUID);
|
|
||||||
if (creature && creature.IsAlive())
|
|
||||||
return creature;
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public npc_air_force_bots(Creature creature) : base(creature)
|
||||||
|
{
|
||||||
|
_spawn = FindSpawnFor(creature.GetEntry());
|
||||||
|
}
|
||||||
|
|
||||||
|
Creature GetOrSummonGuard()
|
||||||
|
{
|
||||||
|
Creature guard = ObjectAccessor.GetCreature(me, _myGuard);
|
||||||
|
|
||||||
|
if (guard == null && (guard = me.SummonCreature(_spawn.otherEntry, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.TimedDespawnOutOfCombat, 300000)))
|
||||||
|
_myGuard = guard.GetGUID();
|
||||||
|
|
||||||
|
return guard;
|
||||||
|
}
|
||||||
|
|
||||||
public override void UpdateAI(uint diff)
|
public override void UpdateAI(uint diff)
|
||||||
{
|
{
|
||||||
base.UpdateAI(diff);
|
if (_toAttack.Empty())
|
||||||
|
return;
|
||||||
|
|
||||||
inLineOfSightSinceLastUpdate.Clear();
|
Creature guard = GetOrSummonGuard();
|
||||||
|
if (guard == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Keep the list of targets for later on when the guards will be alive
|
||||||
|
if (!guard.IsAlive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (var i = 0; i < _toAttack.Count; ++i)
|
||||||
|
{
|
||||||
|
ObjectGuid guid = _toAttack[i];
|
||||||
|
|
||||||
|
Unit target = Global.ObjAccessor.GetUnit(me, guid);
|
||||||
|
if (!target)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (guard.IsEngagedBy(target))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
guard.EngageWithTarget(target);
|
||||||
|
if (_spawn.spawnType == SpawnType.AlarmBot)
|
||||||
|
guard.CastSpell(target, SpellIds.GuardsMark, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_toAttack.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void MoveInLineOfSight(Unit who)
|
public override void MoveInLineOfSight(Unit who)
|
||||||
{
|
{
|
||||||
if (SpawnAssoc == null)
|
// guards are only spawned against players
|
||||||
|
if (!who.IsPlayer())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (me.IsValidAttackTarget(who))
|
// we're already scheduled to attack this player on our next tick, don't bother checking
|
||||||
{
|
if (_toAttack.Contains(who.GetGUID()))
|
||||||
Player playerTarget = who.ToPlayer();
|
return;
|
||||||
|
|
||||||
// airforce guards only spawn for players
|
// check if they're in range
|
||||||
if (!playerTarget)
|
if (!who.IsWithinDistInMap(me, (_spawn.spawnType == SpawnType.AlarmBot) ? Misc.RangeAlarmbot : Misc.RangeTripwire))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Creature lastSpawnedGuard = SpawnedGUID.IsEmpty() ? null : GetSummonedGuard();
|
// check if they're hostile
|
||||||
|
if (!(me.IsHostileTo(who) || who.IsHostileTo(me)))
|
||||||
|
return;
|
||||||
|
|
||||||
// prevent calling Unit::GetUnit at next MoveInLineOfSight call - speedup
|
// check if they're a valid attack target
|
||||||
if (!lastSpawnedGuard)
|
if (!me.IsValidAttackTarget(who))
|
||||||
SpawnedGUID.Clear();
|
return;
|
||||||
|
|
||||||
switch (SpawnAssoc.spawnType)
|
if ((_spawn.spawnType == SpawnType.Tripwire) && who.IsFlying())
|
||||||
{
|
return;
|
||||||
case SpawnType.AlarmBot:
|
|
||||||
{
|
|
||||||
// handle only 1 change for world update for each target
|
|
||||||
if (inLineOfSightSinceLastUpdate.Contains(who.GetGUID()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
inLineOfSightSinceLastUpdate.Add(who.GetGUID());
|
_toAttack.Add(who.GetGUID());
|
||||||
|
|
||||||
if (!who.IsWithinDistInMap(me, Misc.RangeGuardsMark))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Aura markAura = who.GetAura(SpellIds.GuardsMark);
|
|
||||||
if (markAura != null)
|
|
||||||
{
|
|
||||||
// the target wasn't able to move out of our range within 25 seconds
|
|
||||||
if (!lastSpawnedGuard)
|
|
||||||
{
|
|
||||||
lastSpawnedGuard = SummonGuard();
|
|
||||||
|
|
||||||
if (!lastSpawnedGuard)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (markAura.GetDuration() < Misc.AuraDurationTimeLeft)
|
|
||||||
if (!lastSpawnedGuard.GetVictim())
|
|
||||||
lastSpawnedGuard.GetAI().AttackStart(who);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!lastSpawnedGuard)
|
|
||||||
lastSpawnedGuard = SummonGuard();
|
|
||||||
|
|
||||||
if (!lastSpawnedGuard)
|
|
||||||
return;
|
|
||||||
|
|
||||||
lastSpawnedGuard.CastSpell(who, SpellIds.GuardsMark, true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SpawnType.TripwireRooftop:
|
|
||||||
{
|
|
||||||
if (!who.IsWithinDistInMap(me, Misc.RangeTripwire))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!lastSpawnedGuard)
|
|
||||||
lastSpawnedGuard = SummonGuard();
|
|
||||||
|
|
||||||
if (!lastSpawnedGuard)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// ROOFTOP only triggers if the player is on the ground
|
|
||||||
if (!playerTarget.IsFlying() && !lastSpawnedGuard.GetVictim())
|
|
||||||
lastSpawnedGuard.GetAI().AttackStart(who);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1831,9 +1831,10 @@ Respawn.DynamicMinimumGameObject = 10
|
|||||||
# CHAT SETTINGS
|
# CHAT SETTINGS
|
||||||
#
|
#
|
||||||
# ChatFakeMessagePreventing
|
# ChatFakeMessagePreventing
|
||||||
# Description: Chat protection from creating fake messages using a lot spaces or other
|
# Description: Additional protection from creating fake chat messages using spaces.
|
||||||
# invisible symbols. Not applied to the addon language, but may break old
|
# Collapses multiple subsequent whitespaces into a single whitespace.
|
||||||
# addons that use normal languages for sending data to other clients.
|
# Not applied to the addon language, but may break old addons that use
|
||||||
|
# "normal" chat messages for sending data to other clients.
|
||||||
# Default: 1 - (Enabled, Blizzlike)
|
# Default: 1 - (Enabled, Blizzlike)
|
||||||
# 0 - (Disabled)
|
# 0 - (Disabled)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user