// Copyright (c) CypherCore All rights reserved. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. using Framework.Collections; using Framework.Constants; using Game.Networking; using Game.Spells; using System; using System.Numerics; namespace Game.Entities { public class CharmInfo { public CharmInfo(Unit unit) { _unit = unit; _CommandState = CommandStates.Follow; _petnumber = 0; _oldReactState = ReactStates.Passive; for (byte i = 0; i < SharedConst.MaxSpellCharm; ++i) { _charmspells[i] = new UnitActionBarEntry(); _charmspells[i].SetActionAndType(0, ActiveStates.Disabled); } for (var i = 0; i < SharedConst.ActionBarIndexMax; ++i) PetActionBar[i] = new UnitActionBarEntry(); Creature creature = _unit.ToCreature(); if (creature != null) { _oldReactState = creature.GetReactState(); creature.SetReactState(ReactStates.Passive); } } public void RestoreState() { if (_unit.IsTypeId(TypeId.Unit)) { Creature creature = _unit.ToCreature(); if (creature != null) creature.SetReactState(_oldReactState); } } public void InitPetActionBar() { // the first 3 SpellOrActions are attack, follow and stay for (byte i = 0; i < SharedConst.ActionBarIndexPetSpellStart - SharedConst.ActionBarIndexStart; ++i) SetActionBar((byte)(SharedConst.ActionBarIndexStart + i), (uint)CommandStates.Attack - i, ActiveStates.Command); // middle 4 SpellOrActions are spells/special attacks/abilities for (byte i = 0; i < SharedConst.ActionBarIndexPetSpellEnd - SharedConst.ActionBarIndexPetSpellStart; ++i) SetActionBar((byte)(SharedConst.ActionBarIndexPetSpellStart + i), 0, ActiveStates.Passive); // last 3 SpellOrActions are reactions for (byte i = 0; i < SharedConst.ActionBarIndexEnd - SharedConst.ActionBarIndexPetSpellEnd; ++i) SetActionBar((byte)(SharedConst.ActionBarIndexPetSpellEnd + i), (uint)CommandStates.Attack - i, ActiveStates.Reaction); } public void InitEmptyActionBar(bool withAttack = true) { if (withAttack) SetActionBar(SharedConst.ActionBarIndexStart, (uint)CommandStates.Attack, ActiveStates.Command); else SetActionBar(SharedConst.ActionBarIndexStart, 0, ActiveStates.Passive); for (byte x = SharedConst.ActionBarIndexStart + 1; x < SharedConst.ActionBarIndexEnd; ++x) SetActionBar(x, 0, ActiveStates.Passive); } public void InitPossessCreateSpells() { if (_unit.IsTypeId(TypeId.Unit)) { // Adding switch until better way is found. Malcrom // Adding entrys to this switch will prevent COMMAND_ATTACK being added to pet bar. switch (_unit.GetEntry()) { case 23575: // Mindless Abomination case 24783: // Trained Rock Falcon case 27664: // Crashin' Thrashin' Racer case 40281: // Crashin' Thrashin' Racer case 28511: // Eye of Acherus break; default: InitEmptyActionBar(); break; } for (byte i = 0; i < SharedConst.MaxCreatureSpells; ++i) { uint spellId = _unit.ToCreature().m_spells[i]; SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, _unit.GetMap().GetDifficultyID()); if (spellInfo != null) { if (spellInfo.HasAttribute(SpellAttr5.NotAvailableWhileCharmed)) continue; if (spellInfo.IsPassive()) _unit.CastSpell(_unit, spellInfo.Id, new CastSpellExtraArgs(true)); else AddSpellToActionBar(spellInfo, ActiveStates.Passive, i % SharedConst.ActionBarIndexMax); } } } else InitEmptyActionBar(); } public void InitCharmCreateSpells() { if (_unit.IsTypeId(TypeId.Player)) // charmed players don't have spells { InitEmptyActionBar(); return; } InitPetActionBar(); for (uint x = 0; x < SharedConst.MaxSpellCharm; ++x) { uint spellId = _unit.ToCreature().m_spells[x]; SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, _unit.GetMap().GetDifficultyID()); if (spellInfo == null) { _charmspells[x].SetActionAndType(spellId, ActiveStates.Disabled); continue; } if (spellInfo.HasAttribute(SpellAttr5.NotAvailableWhileCharmed)) continue; if (spellInfo.IsPassive()) { _unit.CastSpell(_unit, spellInfo.Id, new CastSpellExtraArgs(true)); _charmspells[x].SetActionAndType(spellId, ActiveStates.Passive); } else { _charmspells[x].SetActionAndType(spellId, ActiveStates.Disabled); ActiveStates newstate; if (!spellInfo.IsAutocastable()) newstate = ActiveStates.Passive; else { if (spellInfo.IsAutocastEnabledByDefault() && spellInfo.NeedsExplicitUnitTarget()) { newstate = ActiveStates.Enabled; ToggleCreatureAutocast(spellInfo, true); } else newstate = ActiveStates.Disabled; } AddSpellToActionBar(spellInfo, newstate); } } } public bool AddSpellToActionBar(SpellInfo spellInfo, ActiveStates newstate = ActiveStates.Decide, int preferredSlot = 0) { uint spell_id = spellInfo.Id; uint first_id = spellInfo.GetFirstRankSpell().Id; // new spell rank can be already listed for (byte i = 0; i < SharedConst.ActionBarIndexMax; ++i) { uint action = PetActionBar[i].GetAction(); if (action != 0) { if (PetActionBar[i].IsActionBarForSpell() && Global.SpellMgr.GetFirstSpellInChain(action) == first_id) { PetActionBar[i].SetAction(spell_id); return true; } } } // or use empty slot in other case for (byte i = 0; i < SharedConst.ActionBarIndexMax; ++i) { byte j = (byte)((preferredSlot + i) % SharedConst.ActionBarIndexMax); if (PetActionBar[j].GetAction() == 0 && PetActionBar[j].IsActionBarForSpell()) { newstate = new Func(() => { if (newstate != ActiveStates.Decide) return newstate; if (!spellInfo.IsAutocastable()) return ActiveStates.Passive; if (spellInfo.IsAutocastEnabledByDefault()) return ActiveStates.Enabled; return ActiveStates.Disabled; })(); SetActionBar(j, spell_id, newstate); return true; } } return false; } public bool RemoveSpellFromActionBar(uint spell_id) { uint first_id = Global.SpellMgr.GetFirstSpellInChain(spell_id); for (byte i = 0; i < SharedConst.ActionBarIndexMax; ++i) { uint action = PetActionBar[i].GetAction(); if (action != 0) { if (PetActionBar[i].IsActionBarForSpell() && Global.SpellMgr.GetFirstSpellInChain(action) == first_id) { SetActionBar(i, 0, ActiveStates.Passive); return true; } } } return false; } public void ToggleCreatureAutocast(SpellInfo spellInfo, bool apply) { if (spellInfo.IsPassive()) return; for (uint x = 0; x < SharedConst.MaxSpellCharm; ++x) if (spellInfo.Id == _charmspells[x].GetAction()) _charmspells[x].SetType(apply ? ActiveStates.Enabled : ActiveStates.Disabled); } public void SetPetNumber(uint petnumber, bool statwindow) { _petnumber = petnumber; if (statwindow) _unit.SetPetNumberForClient(_petnumber); else _unit.SetPetNumberForClient(0); } public void LoadPetActionBar(string data) { InitPetActionBar(); var tokens = new StringArray(data, ' '); if (tokens.Length != (SharedConst.ActionBarIndexEnd - SharedConst.ActionBarIndexStart) * 2) return; // non critical, will reset to default byte index = 0; for (byte i = 0; i < tokens.Length && index < SharedConst.ActionBarIndexEnd; ++i, ++index) { ActiveStates type = tokens[i++].ToEnum(); uint.TryParse(tokens[i], out uint action); PetActionBar[index].SetActionAndType(action, type); // check correctness if (PetActionBar[index].IsActionBarForSpell()) { SpellInfo spelInfo = Global.SpellMgr.GetSpellInfo(PetActionBar[index].GetAction(), _unit.GetMap().GetDifficultyID()); if (spelInfo == null) SetActionBar(index, 0, ActiveStates.Passive); else if (!spelInfo.IsAutocastable()) SetActionBar(index, PetActionBar[index].GetAction(), ActiveStates.Passive); } } } public void BuildActionBar(WorldPacket data) { for (int i = 0; i < SharedConst.ActionBarIndexMax; ++i) data.WriteUInt32(PetActionBar[i].packedData); } public void SetSpellAutocast(SpellInfo spellInfo, bool state) { for (byte i = 0; i < SharedConst.ActionBarIndexMax; ++i) { if (spellInfo.Id == PetActionBar[i].GetAction() && PetActionBar[i].IsActionBarForSpell()) { PetActionBar[i].SetType(state ? ActiveStates.Enabled : ActiveStates.Disabled); break; } } } public void SetIsCommandAttack(bool val) { _isCommandAttack = val; } public bool IsCommandAttack() { return _isCommandAttack; } public void SetIsCommandFollow(bool val) { _isCommandFollow = val; } public bool IsCommandFollow() { return _isCommandFollow; } public void SaveStayPosition() { //! At this point a new spline destination is enabled because of Unit.StopMoving() Vector3 stayPos = _unit.MoveSpline.FinalDestination(); if (_unit.MoveSpline.onTransport) { float o = 0; ITransport transport = _unit.GetDirectTransport(); if (transport != null) transport.CalculatePassengerPosition(ref stayPos.X, ref stayPos.Y, ref stayPos.Z, ref o); } _stayX = stayPos.X; _stayY = stayPos.Y; _stayZ = stayPos.Z; } public void GetStayPosition(out float x, out float y, out float z) { x = _stayX; y = _stayY; z = _stayZ; } public void SetIsAtStay(bool val) { _isAtStay = val; } public bool IsAtStay() { return _isAtStay; } public void SetIsFollowing(bool val) { _isFollowing = val; } public bool IsFollowing() { return _isFollowing; } public void SetIsReturning(bool val) { _isReturning = val; } public bool IsReturning() { return _isReturning; } public uint GetPetNumber() { return _petnumber; } public void SetCommandState(CommandStates st) { _CommandState = st; } public CommandStates GetCommandState() { return _CommandState; } public bool HasCommandState(CommandStates state) { return (_CommandState == state); } public void SetActionBar(byte index, uint spellOrAction, ActiveStates type) { PetActionBar[index].SetActionAndType(spellOrAction, type); } public UnitActionBarEntry GetActionBarEntry(byte index) { return PetActionBar[index]; } public UnitActionBarEntry GetCharmSpell(byte index) { return _charmspells[index]; } Unit _unit; UnitActionBarEntry[] PetActionBar = new UnitActionBarEntry[SharedConst.ActionBarIndexMax]; UnitActionBarEntry[] _charmspells = new UnitActionBarEntry[4]; CommandStates _CommandState; uint _petnumber; ReactStates _oldReactState; bool _isCommandAttack; bool _isCommandFollow; bool _isAtStay; bool _isFollowing; bool _isReturning; float _stayX; float _stayY; float _stayZ; } public class UnitActionBarEntry { public UnitActionBarEntry() { packedData = (uint)ActiveStates.Disabled << 24; } public ActiveStates GetActiveState() { return (ActiveStates)UNIT_ACTION_BUTTON_TYPE(packedData); } public uint GetAction() { return UNIT_ACTION_BUTTON_ACTION(packedData); } public bool IsActionBarForSpell() { ActiveStates Type = GetActiveState(); return Type == ActiveStates.Disabled || Type == ActiveStates.Enabled || Type == ActiveStates.Passive; } public void SetActionAndType(uint action, ActiveStates type) { packedData = MAKE_UNIT_ACTION_BUTTON(action, (uint)type); } public void SetType(ActiveStates type) { packedData = MAKE_UNIT_ACTION_BUTTON(UNIT_ACTION_BUTTON_ACTION(packedData), (uint)type); } public void SetAction(uint action) { packedData = (packedData & 0xFF000000) | UNIT_ACTION_BUTTON_ACTION(action); } public uint packedData; public static uint MAKE_UNIT_ACTION_BUTTON(uint action, uint type) { return (action | (type << 24)); } public static uint UNIT_ACTION_BUTTON_ACTION(uint packedData) { return (packedData & 0x00FFFFFF); } public static uint UNIT_ACTION_BUTTON_TYPE(uint packedData) { return ((packedData & 0xFF000000) >> 24); } } }