public override void UpdateAI(uint diff) { if (!me.IsAlive() || me.GetCharmInfo() == null) { return; } Unit owner = me.GetCharmerOrOwner(); if (m_updateAlliesTimer <= diff) { // UpdateAllies self set update timer UpdateAllies(); } else { m_updateAlliesTimer -= diff; } if (me.GetVictim() && me.GetVictim().IsAlive()) { // is only necessary to stop casting, the pet must not exit combat if (!me.GetCurrentSpell(CurrentSpellTypes.Channeled) && // ignore channeled spells (Pin, Seduction) (me.GetVictim() && me.GetVictim().HasBreakableByDamageCrowdControlAura(me))) { me.InterruptNonMeleeSpells(false); return; } if (_needToStop()) { Log.outDebug(LogFilter.Server, "Pet AI stopped attacking [{0}]", me.GetGUID().ToString()); _stopAttack(); return; } // Check before attacking to prevent pets from leaving stay position if (me.GetCharmInfo().HasCommandState(CommandStates.Stay)) { if (me.GetCharmInfo().IsCommandAttack() || (me.GetCharmInfo().IsAtStay() && me.IsWithinMeleeRange(me.GetVictim()))) { DoMeleeAttackIfReady(); } } else { DoMeleeAttackIfReady(); } } else { if (me.HasReactState(ReactStates.Aggressive) || me.GetCharmInfo().IsAtStay()) { // Every update we need to check targets only in certain cases // Aggressive - Allow auto select if owner or pet don't have a target // Stay - Only pick from pet or owner targets / attackers so targets won't run by // while chasing our owner. Don't do auto select. // All other cases (ie: defensive) - Targets are assigned by AttackedBy(), OwnerAttackedBy(), OwnerAttacked(), etc. Unit nextTarget = SelectNextTarget(me.HasReactState(ReactStates.Aggressive)); if (nextTarget) { AttackStart(nextTarget); } else { HandleReturnMovement(); } } else { HandleReturnMovement(); } } // Autocast (casted only in combat or persistent spells in any state) if (!me.HasUnitState(UnitState.Casting)) { List <Tuple <Unit, Spell> > targetSpellStore = new List <Tuple <Unit, Spell> >(); for (byte i = 0; i < me.GetPetAutoSpellSize(); ++i) { uint spellID = me.GetPetAutoSpellOnPos(i); if (spellID == 0) { continue; } SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellID); if (spellInfo == null) { continue; } if (me.GetCharmInfo() != null && me.GetSpellHistory().HasGlobalCooldown(spellInfo)) { continue; } // check spell cooldown if (!me.GetSpellHistory().IsReady(spellInfo)) { continue; } if (spellInfo.IsPositive()) { if (spellInfo.CanBeUsedInCombat()) { // Check if we're in combat or commanded to attack if (!me.IsInCombat() && !me.GetCharmInfo().IsCommandAttack()) { continue; } } Spell spell = new Spell(me, spellInfo, TriggerCastFlags.None); bool spellUsed = false; // Some spells can target enemy or friendly (DK Ghoul's Leap) // Check for enemy first (pet then owner) Unit target = me.getAttackerForHelper(); if (!target && owner) { target = owner.getAttackerForHelper(); } if (target) { if (CanAIAttack(target) && spell.CanAutoCast(target)) { targetSpellStore.Add(Tuple.Create(target, spell)); spellUsed = true; } } if (spellInfo.HasEffect(SpellEffectName.JumpDest)) { if (!spellUsed) { spell.Dispose(); } continue; // Pets must only jump to target } // No enemy, check friendly if (!spellUsed) { foreach (var tar in m_AllySet) { Unit ally = Global.ObjAccessor.GetUnit(me, tar); //only buff targets that are in combat, unless the spell can only be cast while out of combat if (!ally) { continue; } if (spell.CanAutoCast(ally)) { targetSpellStore.Add(Tuple.Create(ally, spell)); spellUsed = true; break; } } } // No valid targets at all if (!spellUsed) { spell.Dispose(); } } else if (me.GetVictim() && CanAIAttack(me.GetVictim()) && spellInfo.CanBeUsedInCombat()) { Spell spell = new Spell(me, spellInfo, TriggerCastFlags.None); if (spell.CanAutoCast(me.GetVictim())) { targetSpellStore.Add(Tuple.Create(me.GetVictim(), spell)); } else { spell.Dispose(); } } } //found units to cast on to if (!targetSpellStore.Empty()) { int index = RandomHelper.IRand(0, targetSpellStore.Count - 1); var tss = targetSpellStore[index]; (Unit target, Spell spell) = tss; targetSpellStore.RemoveAt(index); SpellCastTargets targets = new SpellCastTargets(); targets.SetUnitTarget(target); spell.prepare(targets); } // deleted cached Spell objects foreach (var pair in targetSpellStore) { pair.Item2.Dispose(); } } // Update speed as needed to prevent dropping too far behind and despawning me.UpdateSpeed(UnitMoveType.Run); me.UpdateSpeed(UnitMoveType.Walk); me.UpdateSpeed(UnitMoveType.Flight); }
// Called when a player uses the item. public virtual bool OnUse(Player player, Item item, SpellCastTargets targets, ObjectGuid castId) { return(false); }
void HandleUseItem(UseItem packet) { Player user = GetPlayer(); // ignore for remote control state if (user.m_unitMovedByMe != user) { return; } Item item = user.GetUseableItemByPos(packet.PackSlot, packet.Slot); if (item == null) { user.SendEquipError(InventoryResult.ItemNotFound); return; } if (item.GetGUID() != packet.CastItem) { user.SendEquipError(InventoryResult.ItemNotFound); return; } ItemTemplate proto = item.GetTemplate(); if (proto == null) { user.SendEquipError(InventoryResult.ItemNotFound, item); return; } // some item classes can be used only in equipped state if (proto.GetInventoryType() != InventoryType.NonEquip && !item.IsEquipped()) { user.SendEquipError(InventoryResult.ItemNotFound, item); return; } InventoryResult msg = user.CanUseItem(item); if (msg != InventoryResult.Ok) { user.SendEquipError(msg, item); return; } // only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB) if (proto.GetClass() == ItemClass.Consumable && !proto.GetFlags().HasAnyFlag(ItemFlags.IgnoreDefaultArenaRestrictions) && user.InArena()) { user.SendEquipError(InventoryResult.NotDuringArenaMatch, item); return; } // don't allow items banned in arena if (proto.GetFlags().HasAnyFlag(ItemFlags.NotUseableInArena) && user.InArena()) { user.SendEquipError(InventoryResult.NotDuringArenaMatch, item); return; } if (user.IsInCombat()) { for (int i = 0; i < proto.Effects.Count; ++i) { SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(proto.Effects[i].SpellID); if (spellInfo != null) { if (!spellInfo.CanBeUsedInCombat()) { user.SendEquipError(InventoryResult.NotInCombat, item); return; } } } } // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory) if (item.GetBonding() == ItemBondingType.OnUse || item.GetBonding() == ItemBondingType.OnAcquire || item.GetBonding() == ItemBondingType.Quest) { if (!item.IsSoulBound()) { item.SetState(ItemUpdateState.Changed, user); item.SetBinding(true); GetCollectionMgr().AddItemAppearance(item); } } SpellCastTargets targets = new SpellCastTargets(user, packet.Cast); // Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state. if (!Global.ScriptMgr.OnItemUse(user, item, targets, packet.Cast.CastID)) { // no script or script not process request by self user.CastItemUseSpell(item, targets, packet.Cast.CastID, packet.Cast.Misc); } }
void HandleCastSpell(CastSpell cast) { // ignore for remote control state (for player case) Unit mover = GetPlayer().m_unitMovedByMe; if (mover != GetPlayer() && mover.IsTypeId(TypeId.Player)) { return; } SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(cast.Cast.SpellID); if (spellInfo == null) { Log.outError(LogFilter.Network, "WORLD: unknown spell id {0}", cast.Cast.SpellID); return; } if (spellInfo.IsPassive()) { return; } Unit caster = mover; if (caster.IsTypeId(TypeId.Unit) && !caster.ToCreature().HasSpell(spellInfo.Id)) { // If the vehicle creature does not have the spell but it allows the passenger to cast own spells // change caster to player and let him cast if (!GetPlayer().IsOnVehicle(caster) || spellInfo.CheckVehicle(GetPlayer()) != SpellCastResult.SpellCastOk) { return; } caster = GetPlayer(); } // check known spell or raid marker spell (which not requires player to know it) if (caster.IsTypeId(TypeId.Player) && !caster.ToPlayer().HasActiveSpell(spellInfo.Id) && !spellInfo.HasEffect(SpellEffectName.ChangeRaidMarker) && !spellInfo.HasAttribute(SpellAttr8.RaidMarker)) { return; } // Check possible spell cast overrides spellInfo = caster.GetCastSpellInfo(spellInfo); // Client is resending autoshot cast opcode when other spell is casted during shoot rotation // Skip it to prevent "interrupt" message if (spellInfo.IsAutoRepeatRangedSpell() && GetPlayer().GetCurrentSpell(CurrentSpellTypes.AutoRepeat) != null && GetPlayer().GetCurrentSpell(CurrentSpellTypes.AutoRepeat).m_spellInfo == spellInfo) { return; } // can't use our own spells when we're in possession of another unit, if (GetPlayer().isPossessing()) { return; } // client provided targets SpellCastTargets targets = new SpellCastTargets(caster, cast.Cast); // auto-selection buff level base at target level (in spellInfo) if (targets.GetUnitTarget() != null) { SpellInfo actualSpellInfo = spellInfo.GetAuraRankForLevel(targets.GetUnitTarget().GetLevelForTarget(caster)); // if rank not found then function return NULL but in explicit cast case original spell can be casted and later failed with appropriate error message if (actualSpellInfo != null) { spellInfo = actualSpellInfo; } } if (cast.Cast.MoveUpdate.HasValue) { HandleMovementOpcode(ClientOpcodes.MoveStop, cast.Cast.MoveUpdate.Value); } Spell spell = new Spell(caster, spellInfo, TriggerCastFlags.None, ObjectGuid.Empty, false); SpellPrepare spellPrepare = new SpellPrepare(); spellPrepare.ClientCastID = cast.Cast.CastID; spellPrepare.ServerCastID = spell.m_castId; SendPacket(spellPrepare); spell.m_fromClient = true; spell.m_misc.Data0 = cast.Cast.Misc[0]; spell.m_misc.Data1 = cast.Cast.Misc[1]; spell.prepare(targets); }
void HandleAcceptTrade(AcceptTrade acceptTrade) { TradeData my_trade = GetPlayer().GetTradeData(); if (my_trade == null) { return; } Player trader = my_trade.GetTrader(); TradeData his_trade = trader.GetTradeData(); if (his_trade == null) { return; } Item[] myItems = new Item[(int)TradeSlots.Count]; Item[] hisItems = new Item[(int)TradeSlots.Count]; // set before checks for propertly undo at problems (it already set in to client) my_trade.SetAccepted(true); TradeStatusPkt info = new TradeStatusPkt(); if (his_trade.GetServerStateIndex() != acceptTrade.StateIndex) { info.Status = TradeStatus.StateChanged; SendTradeStatus(info); my_trade.SetAccepted(false); return; } if (!GetPlayer().IsWithinDistInMap(trader, 11.11f, false)) { info.Status = TradeStatus.TooFarAway; SendTradeStatus(info); my_trade.SetAccepted(false); return; } // not accept case incorrect money amount if (!GetPlayer().HasEnoughMoney(my_trade.GetMoney())) { info.Status = TradeStatus.Failed; info.BagResult = InventoryResult.NotEnoughMoney; SendTradeStatus(info); my_trade.SetAccepted(false, true); return; } // not accept case incorrect money amount if (!trader.HasEnoughMoney(his_trade.GetMoney())) { info.Status = TradeStatus.Failed; info.BagResult = InventoryResult.NotEnoughMoney; trader.GetSession().SendTradeStatus(info); his_trade.SetAccepted(false, true); return; } if (GetPlayer().GetMoney() >= PlayerConst.MaxMoneyAmount - his_trade.GetMoney()) { info.Status = TradeStatus.Failed; info.BagResult = InventoryResult.TooMuchGold; SendTradeStatus(info); my_trade.SetAccepted(false, true); return; } if (trader.GetMoney() >= PlayerConst.MaxMoneyAmount - my_trade.GetMoney()) { info.Status = TradeStatus.Failed; info.BagResult = InventoryResult.TooMuchGold; trader.GetSession().SendTradeStatus(info); his_trade.SetAccepted(false, true); return; } // not accept if some items now can't be trade (cheating) for (byte i = 0; i < (byte)TradeSlots.Count; ++i) { Item item = my_trade.GetItem((TradeSlots)i); if (item) { if (!item.CanBeTraded(false, true)) { info.Status = TradeStatus.Cancelled; SendTradeStatus(info); return; } if (item.IsBindedNotWith(trader)) { info.Status = TradeStatus.Failed; info.BagResult = InventoryResult.TradeBoundItem; SendTradeStatus(info); return; } } item = his_trade.GetItem((TradeSlots)i); if (item) { if (!item.CanBeTraded(false, true)) { info.Status = TradeStatus.Cancelled; SendTradeStatus(info); return; } } } if (his_trade.IsAccepted()) { SetAcceptTradeMode(my_trade, his_trade, myItems, hisItems); Spell my_spell = null; SpellCastTargets my_targets = new SpellCastTargets(); Spell his_spell = null; SpellCastTargets his_targets = new SpellCastTargets(); // not accept if spell can't be casted now (cheating) uint my_spell_id = my_trade.GetSpell(); if (my_spell_id != 0) { SpellInfo spellEntry = Global.SpellMgr.GetSpellInfo(my_spell_id, _player.GetMap().GetDifficultyID()); Item castItem = my_trade.GetSpellCastItem(); if (spellEntry == null || !his_trade.GetItem(TradeSlots.NonTraded) || (my_trade.HasSpellCastItem() && !castItem)) { ClearAcceptTradeMode(my_trade, his_trade); ClearAcceptTradeMode(myItems, hisItems); my_trade.SetSpell(0); return; } my_spell = new Spell(GetPlayer(), spellEntry, TriggerCastFlags.FullMask); my_spell.m_CastItem = castItem; my_targets.SetTradeItemTarget(GetPlayer()); my_spell.m_targets = my_targets; SpellCastResult res = my_spell.CheckCast(true); if (res != SpellCastResult.SpellCastOk) { my_spell.SendCastResult(res); ClearAcceptTradeMode(my_trade, his_trade); ClearAcceptTradeMode(myItems, hisItems); my_spell.Dispose(); my_trade.SetSpell(0); return; } } // not accept if spell can't be casted now (cheating) uint his_spell_id = his_trade.GetSpell(); if (his_spell_id != 0) { SpellInfo spellEntry = Global.SpellMgr.GetSpellInfo(his_spell_id, trader.GetMap().GetDifficultyID()); Item castItem = his_trade.GetSpellCastItem(); if (spellEntry == null || !my_trade.GetItem(TradeSlots.NonTraded) || (his_trade.HasSpellCastItem() && !castItem)) { his_trade.SetSpell(0); ClearAcceptTradeMode(my_trade, his_trade); ClearAcceptTradeMode(myItems, hisItems); return; } his_spell = new Spell(trader, spellEntry, TriggerCastFlags.FullMask); his_spell.m_CastItem = castItem; his_targets.SetTradeItemTarget(trader); his_spell.m_targets = his_targets; SpellCastResult res = his_spell.CheckCast(true); if (res != SpellCastResult.SpellCastOk) { his_spell.SendCastResult(res); ClearAcceptTradeMode(my_trade, his_trade); ClearAcceptTradeMode(myItems, hisItems); my_spell.Dispose(); his_spell.Dispose(); his_trade.SetSpell(0); return; } } // inform partner client info.Status = TradeStatus.Accepted; trader.GetSession().SendTradeStatus(info); // test if item will fit in each inventory TradeStatusPkt myCanCompleteInfo = new TradeStatusPkt(); TradeStatusPkt hisCanCompleteInfo = new TradeStatusPkt(); hisCanCompleteInfo.BagResult = trader.CanStoreItems(myItems, (int)TradeSlots.TradedCount, ref hisCanCompleteInfo.ItemID); myCanCompleteInfo.BagResult = GetPlayer().CanStoreItems(hisItems, (int)TradeSlots.TradedCount, ref myCanCompleteInfo.ItemID); ClearAcceptTradeMode(myItems, hisItems); // in case of missing space report error if (myCanCompleteInfo.BagResult != InventoryResult.Ok) { ClearAcceptTradeMode(my_trade, his_trade); myCanCompleteInfo.Status = TradeStatus.Failed; trader.GetSession().SendTradeStatus(myCanCompleteInfo); myCanCompleteInfo.FailureForYou = true; SendTradeStatus(myCanCompleteInfo); my_trade.SetAccepted(false); his_trade.SetAccepted(false); return; } else if (hisCanCompleteInfo.BagResult != InventoryResult.Ok) { ClearAcceptTradeMode(my_trade, his_trade); hisCanCompleteInfo.Status = TradeStatus.Failed; SendTradeStatus(hisCanCompleteInfo); hisCanCompleteInfo.FailureForYou = true; trader.GetSession().SendTradeStatus(hisCanCompleteInfo); my_trade.SetAccepted(false); his_trade.SetAccepted(false); return; } // execute trade: 1. remove for (byte i = 0; i < (int)TradeSlots.TradedCount; ++i) { if (myItems[i]) { myItems[i].SetGiftCreator(GetPlayer().GetGUID()); GetPlayer().MoveItemFromInventory(myItems[i].GetBagSlot(), myItems[i].GetSlot(), true); } if (hisItems[i]) { hisItems[i].SetGiftCreator(trader.GetGUID()); trader.MoveItemFromInventory(hisItems[i].GetBagSlot(), hisItems[i].GetSlot(), true); } } // execute trade: 2. store MoveItems(myItems, hisItems); // logging money if (HasPermission(RBACPermissions.LogGmTrade)) { if (my_trade.GetMoney() > 0) { Log.outCommand(GetPlayer().GetSession().GetAccountId(), "GM {0} (Account: {1}) give money (Amount: {2}) to player: {3} (Account: {4})", GetPlayer().GetName(), GetPlayer().GetSession().GetAccountId(), my_trade.GetMoney(), trader.GetName(), trader.GetSession().GetAccountId()); } if (his_trade.GetMoney() > 0) { Log.outCommand(GetPlayer().GetSession().GetAccountId(), "GM {0} (Account: {1}) give money (Amount: {2}) to player: {3} (Account: {4})", trader.GetName(), trader.GetSession().GetAccountId(), his_trade.GetMoney(), GetPlayer().GetName(), GetPlayer().GetSession().GetAccountId()); } } // update money GetPlayer().ModifyMoney(-(long)my_trade.GetMoney()); GetPlayer().ModifyMoney((long)his_trade.GetMoney()); trader.ModifyMoney(-(long)his_trade.GetMoney()); trader.ModifyMoney((long)my_trade.GetMoney()); if (my_spell) { my_spell.Prepare(my_targets); } if (his_spell) { his_spell.Prepare(his_targets); } // cleanup ClearAcceptTradeMode(my_trade, his_trade); GetPlayer().SetTradeData(null); trader.SetTradeData(null); // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards) SQLTransaction trans = new SQLTransaction(); GetPlayer().SaveInventoryAndGoldToDB(trans); trader.SaveInventoryAndGoldToDB(trans); DB.Characters.CommitTransaction(trans); info.Status = TradeStatus.Complete; trader.GetSession().SendTradeStatus(info); SendTradeStatus(info); } else { info.Status = TradeStatus.Accepted; trader.GetSession().SendTradeStatus(info); } }
void HandlePetCastSpell(PetCastSpell petCastSpell) { SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(petCastSpell.Cast.SpellID); if (spellInfo == null) { Log.outError(LogFilter.Network, "WorldSession.HandlePetCastSpell: unknown spell id {0} tried to cast by {1}", petCastSpell.Cast.SpellID, petCastSpell.PetGUID.ToString()); return; } Unit caster = Global.ObjAccessor.GetUnit(GetPlayer(), petCastSpell.PetGUID); if (!caster) { Log.outError(LogFilter.Network, "WorldSession.HandlePetCastSpell: Caster {0} not found.", petCastSpell.PetGUID.ToString()); return; } // This opcode is also sent from charmed and possessed units (players and creatures) if (caster != GetPlayer().GetGuardianPet() && caster != GetPlayer().GetCharm()) { Log.outError(LogFilter.Network, "WorldSession.HandlePetCastSpell: {0} isn't pet of player {1} ({2}).", petCastSpell.PetGUID.ToString(), GetPlayer().GetName(), GetPlayer().GetGUID().ToString()); return; } // do not cast not learned spells if (!caster.HasSpell(spellInfo.Id) || spellInfo.IsPassive()) { return; } SpellCastTargets targets = new SpellCastTargets(caster, petCastSpell.Cast); caster.ClearUnitState(UnitState.Follow); Spell spell = new Spell(caster, spellInfo, TriggerCastFlags.None); spell.m_fromClient = true; spell.m_misc.Data0 = petCastSpell.Cast.Misc[0]; spell.m_misc.Data1 = petCastSpell.Cast.Misc[1]; spell.m_targets = targets; SpellCastResult result = spell.CheckPetCast(null); if (result == SpellCastResult.SpellCastOk) { Creature creature = caster.ToCreature(); if (creature) { Pet pet = creature.ToPet(); if (pet) { // 10% chance to play special pet attack talk, else growl // actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell if (pet.getPetType() == PetType.Summon && (RandomHelper.IRand(0, 100) < 10)) { pet.SendPetTalk(PetTalk.SpecialSpell); } else { pet.SendPetAIReaction(petCastSpell.PetGUID); } } } SpellPrepare spellPrepare = new SpellPrepare(); spellPrepare.ClientCastID = petCastSpell.Cast.CastID; spellPrepare.ServerCastID = spell.m_castId; SendPacket(spellPrepare); spell.prepare(targets); } else { spell.SendPetCastResult(result); if (!caster.GetSpellHistory().HasCooldown(spellInfo.Id)) { caster.GetSpellHistory().ResetCooldown(spellInfo.Id, true); } spell.finish(false); spell.Dispose(); } }