Index: configure.ac =================================================================== --- configure.ac (revision 4771) +++ configure.ac (working copy) @@ -48,9 +48,22 @@ pthread_create();, [], [AC_MSG_ERROR([Missing pthread])]) ]) + +# LibEvent Checking +# Default: /usr/local/bin +LEVENT_INCLUDES="-I/usr/local/include $LEVENT_INCLUDES" +LEVENT_LIB="-L/usr/local/lib -levent $LEVENT_LIB" +AC_ARG_WITH(libeventdir, +AC_HELP_STRING([--with_libeventdir=DIRECTORY],[base directory for libevent])) +if test "$with_libeventdir" != "no"; then +LEVENT_INCLUDES="-I$with_libeventdir/include $LEVENT_INCLUDES" +LEVENT_LIB="-L$with_libeventdir/lib -levent $LEVENT_LIB" +fi + AC_CHECK_LIB( z, compress, [ZLIB=-lz],[AC_MSG_ERROR([Missing zlib])] ) AC_CHECK_LIB( compat, ftime, [COMPATLIB=-lcompat] ) AC_CHECK_LIB( crypto, SHA1_Init, [SSLLIB=-lssl], [AC_MSG_ERROR([Missing openssl])]) +AC_CHECK_LIB( event, event_init, [], [AC_MSG_ERROR(libevent is missing, install it or use --with-libevent=DIR.)]) AC_ARG_WITH(postgresql, [ --with-postgresql Use PostgreSQL as a backend (default: no)], @@ -162,6 +175,7 @@ AC_CHECK_HEADERS([mysql.h mysql/mysql.h]) AC_CHECK_HEADERS([libpq-fe.h]) AC_CHECK_HEADERS([zlib.h]) +AC_CHECK_HEADERS([event.h]) ## Check for typedefs, structures, and compiler characteristics. AC_HEADER_STDBOOL @@ -186,6 +200,8 @@ AC_CHECK_FUNCS([atexit ftime gethostbyaddr gethostbyname gethostname gettimeofday memmove memset pow realpath select socket sqrt strchr strdup strerror strstr]) ## Export defined variables +AC_SUBST(LEVENT_LIB) +AC_SUBST(LEVENT_INCLUDES) AC_SUBST(ZLIB) AC_SUBST(COMPATLIB) AC_SUBST(SSLLIB) @@ -223,7 +239,9 @@ src/mangosd/Makefile src/mangosd/mangosd.conf src/bindings/Makefile - src/bindings/universal/Makefile + src/bindings/ScriptDev2/Makefile + src/bindings/ScriptDev2/sql/Makefile + src/bindings/ScriptDev2/sql/Updates/Makefile ]) ## Disabled Makefiles, until they are ready for a successful make and Index: src/bindings/Makefile.am =================================================================== --- src/bindings/Makefile.am (revision 4771) +++ src/bindings/Makefile.am (working copy) @@ -14,4 +14,4 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -SUBDIRS = universal +SUBDIRS = ScriptDev2 Index: src/game/AggressorAI.cpp =================================================================== --- src/game/AggressorAI.cpp (revision 4773) +++ src/game/AggressorAI.cpp (working copy) @@ -38,7 +38,7 @@ return PERMIT_BASE_NO; } -AggressorAI::AggressorAI(Creature &c) : i_creature(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK) +AggressorAI::AggressorAI(Creature &c) : i_creature(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK), pAISpellSystem(&c) { } @@ -111,8 +111,10 @@ } void -AggressorAI::UpdateAI(const uint32 /*diff*/) +AggressorAI::UpdateAI(const uint32 diff) { + pAISpellSystem.Update(diff); + // update i_victimGuid if i_creature.getVictim() !=0 and changed if(!i_creature.SelectHostilTarget() || !i_creature.getVictim()) return; Index: src/game/AggressorAI.h =================================================================== --- src/game/AggressorAI.h (revision 4773) +++ src/game/AggressorAI.h (working copy) @@ -21,6 +21,7 @@ #include "CreatureAI.h" #include "Timer.h" +#include "AISpellSystem.h" class Creature; @@ -45,6 +46,7 @@ static int Permissible(const Creature *); private: + AISpellSystem pAISpellSystem; Creature &i_creature; uint64 i_victimGuid; AggressorState i_state; Index: src/game/AISpellSystem.cpp =================================================================== --- src/game/AISpellSystem.cpp (revision 0) +++ src/game/AISpellSystem.cpp (revision 0) @@ -0,0 +1,518 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "AISpellSystem.h" +#include "../shared/WorldPacket.h" +#include "Unit.h" +#include "Creature.h" +#include "Player.h" +#include "Spell.h" + +//Global Cooldown +#define AI_GCD 1500 + +//0 - NONE, 1 - Self, 2 - Enemy, 3 - AE enemy, 4 - Friendly, 5 - Area Friendly +enum SpellTargetType +{ + SPELL_T_NULL = 0, + SPELL_T_SELF = 1, + SPELL_T_ENEMY = 2, + SPELL_T_AOEENEMY = 4, + SPELL_T_FRIEND = 8, + SPELL_T_AOEFRIEND = 16, +}; + +//0 - NONE, 1 - Damage, 2 - Heal, 3 - Damage over time, 4 - heal over time, 5 - Crowd Control, 6 - Snare, 7 - Buff, 8 - Debuff +enum SpellEffectType +{ + SPELL_E_NULL = 0, + SPELL_E_DAMAGE = 1, + SPELL_E_HEAL = 2, + SPELL_E_DOT = 4, + SPELL_E_HOT = 8, + SPELL_E_CC = 16, + SPELL_E_SNARE = 32, //not used right now + SPELL_E_BUFF = 64, + SPELL_E_DEBUFF = 128, +}; + +//Also could be called the priority type. Later types = higher priority +enum SpellType +{ + TYPE_NONE = 0, + TYPE_BUFF, + TYPE_AREA_DAMAGE, + TYPE_DIRECT_DAMAGE, + TYPE_CROWD_CONTROL, + TYPE_SNARE, + TYPE_DEBUFF, + TYPE_DAMAGE_OVER_TIME, + TYPE_HEAL_OVER_TIME, + TYPE_AREA_HEAL, + TYPE_HEAL, + TYPE_SELF_HEAL, + +}; + +struct SpellHolder +{ + // Constants + uint32 SpellId; + uint32 RangeMin; + uint32 RangeMax; + uint32 Radius; + uint32 Duration; + uint32 CastTime; + uint32 Cooldown; + uint32 PowerCost; + uint8 PowerType; + + uint8 Classication; + uint8 Targets; + + // Variables + uint32 CooldownRemaining; +}; + +bool Sort(SpellHolder* SH1, SpellHolder* SH2) +{ + return (SH1->Classication > SH2->Classication); +} + +AISpellSystem::AISpellSystem(Creature* pCreature) +{ + ASSERT(pCreature); + + m_Unit = pCreature; + + //We know size of spells is always 4 + ClassifySpells(pCreature->m_spells, 4); + + if (m_SpellList.size() > 1) + m_SpellList.sort(std::ptr_fun(Sort)); +} +AISpellSystem::AISpellSystem(Unit* pUnit, const uint32* pSpellArray, const uint32 Size) +{ + ASSERT(pUnit); + + m_Unit = pUnit; + ClassifySpells(pSpellArray, Size); + + if (m_SpellList.size() > 1) + m_SpellList.sort(std::ptr_fun(Sort)); +} + +AISpellSystem::~AISpellSystem() +{ + + if (!m_SpellList.empty()) + for (std::list::iterator i = m_SpellList.begin(); i != m_SpellList.end(); ++i) + { + SpellHolder* pSpell = (*i); + delete pSpell; + } + + m_SpellList.clear(); +} + +void AISpellSystem::ClassifySpells(const uint32* pSpellArray, const uint32 Size) +{ + if (!pSpellArray || !Size) + return; + + //Different Entries we need to get cross referenced information from + SpellEntry const* TempSpell; + SpellRangeEntry const* TempRange; + SpellRadiusEntry const* TempRadius; + SpellDurationEntry const* TempDuration; + SpellCastTimesEntry const* TempCastTime; + + uint32 TargetType = SPELL_T_NULL; + uint32 EffectType = SPELL_E_NULL; + + //Check if each spell is viable(set it to null if not) + for (uint32 i = 0; i < Size; i++) + { + + TempSpell = sSpellStore.LookupEntry(pSpellArray[i]); + + //This spell doesn't exist + if (!TempSpell) + continue; + + //Gather spell Target information and Effect information + for (int j=0; j<3; j++) + { + //***TARGETS*** + + //self + if ( TempSpell->EffectImplicitTargetA[j] == TARGET_SELF || + TempSpell->EffectImplicitTargetB[j] == TARGET_SELF) + TargetType |= SPELL_T_SELF; + else + + //single enemy + if ( TempSpell->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE || + TempSpell->EffectImplicitTargetA[j] == TARGET_CURRENT_SELECTED_ENEMY || + TempSpell->EffectImplicitTargetB[j] == TARGET_CHAIN_DAMAGE || + TempSpell->EffectImplicitTargetB[j] == TARGET_CURRENT_SELECTED_ENEMY) + TargetType |= SPELL_T_ENEMY; + else + + //AoE at enemy + if ( TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA || + TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || + TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED || + TempSpell->EffectImplicitTargetB[j] == TARGET_ALL_ENEMY_IN_AREA || + TempSpell->EffectImplicitTargetB[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || + TempSpell->EffectImplicitTargetB[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) + TargetType |= SPELL_T_AOEENEMY; + else + + //single friend + if ( TempSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_FRIEND || + TempSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_PARTY || + TempSpell->EffectImplicitTargetB[j] == TARGET_SINGLE_FRIEND || + TempSpell->EffectImplicitTargetB[j] == TARGET_SINGLE_PARTY) + TargetType |= SPELL_T_FRIEND; + else + + //aoe friends + if ( TempSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER || + TempSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY || + TempSpell->EffectImplicitTargetB[j] == TARGET_ALL_PARTY_AROUND_CASTER || + TempSpell->EffectImplicitTargetB[j] == TARGET_AREAEFFECT_PARTY) + TargetType |= SPELL_T_AOEFRIEND; + + //***EFFECTS*** + + //damage effect + if ( TempSpell->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE || + TempSpell->Effect[j] == SPELL_EFFECT_INSTAKILL || + TempSpell->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE || + TempSpell->Effect[j] == SPELL_EFFECT_HEALTH_LEECH) + EffectType |= SPELL_E_DAMAGE; + else + + //healing effect + if ( TempSpell->Effect[j] == SPELL_EFFECT_HEAL || + TempSpell->Effect[j] == SPELL_EFFECT_HEAL_MAX_HEALTH || + TempSpell->Effect[j] == SPELL_EFFECT_HEAL_MECHANICAL ) + EffectType |= SPELL_E_HEAL; + else + + //Aura based effects + if ( TempSpell->Effect[j] == SPELL_EFFECT_APPLY_AURA || + TempSpell->Effect[j] == SPELL_EFFECT_PERSISTENT_AREA_AURA || + TempSpell->Effect[j] == SPELL_EFFECT_APPLY_AREA_AURA) + { + + //damage over time + if ( TempSpell->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_DAMAGE || + TempSpell->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_LEECH) + EffectType |= SPELL_E_DOT; + else + + //heal over time + if ( TempSpell->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_HEAL ) + EffectType |= SPELL_E_HOT; + else + + //CC + if ( TempSpell->EffectApplyAuraName[j] == SPELL_AURA_MOD_CONFUSE || + TempSpell->EffectApplyAuraName[j] == SPELL_AURA_MOD_FEAR || + TempSpell->EffectApplyAuraName[j] == SPELL_AURA_MOD_STUN || + TempSpell->EffectApplyAuraName[j] == SPELL_AURA_MOD_ROOT || + TempSpell->EffectApplyAuraName[j] == SPELL_AURA_MOD_SILENCE || + TempSpell->EffectApplyAuraName[j] == SPELL_AURA_MOD_PACIFY_SILENCE || + TempSpell->EffectApplyAuraName[j] == SPELL_AURA_PERIODIC_DAMAGE) + EffectType |= SPELL_E_CC; + else + + //Buff + if ( IsPositiveEffect(pSpellArray[i], j)) + EffectType |= SPELL_E_BUFF; + else + + //Debuff + if ( !IsPositiveEffect(pSpellArray[i], j)) + EffectType |= SPELL_E_DEBUFF; + + //Mind Control + //SPELL_AURA_MOD_POSSESS + //SPELL_AURA_MOD_CHARM + //SPELL_AURA_PERSUADED + //SPELL_AURA_AOE_CHARM + + } + } + + //*** Final Catagorization *** + + //Create a new holder for these spells + SpellHolder* pSpellHolder = new SpellHolder; + + ASSERT(pSpellHolder); + + pSpellHolder->Classication = TYPE_NONE; + + //Reverse priority + //This classifies each spell by its most powerful effect + if (EffectType & SPELL_E_BUFF) + pSpellHolder->Classication = TYPE_BUFF; + + if (EffectType & SPELL_E_DAMAGE) + if (TargetType & SPELL_T_ENEMY) + pSpellHolder->Classication = TYPE_DIRECT_DAMAGE; + else pSpellHolder->Classication = TYPE_AREA_DAMAGE; + + if (EffectType & SPELL_E_CC) + pSpellHolder->Classication = TYPE_CROWD_CONTROL; + + if (EffectType & SPELL_E_SNARE) + pSpellHolder->Classication = TYPE_SNARE; + + if (EffectType & SPELL_E_DEBUFF) + pSpellHolder->Classication = TYPE_DEBUFF; + + if (EffectType & SPELL_E_DOT) + pSpellHolder->Classication = TYPE_DAMAGE_OVER_TIME; + + if (EffectType & SPELL_E_HOT) + pSpellHolder->Classication = TYPE_HEAL_OVER_TIME; + + if (EffectType & SPELL_E_HEAL) + if (TargetType & SPELL_T_SELF) + pSpellHolder->Classication = TYPE_SELF_HEAL; + else if (TargetType & SPELL_T_FRIEND) + pSpellHolder->Classication = TYPE_HEAL; + else if (TargetType & SPELL_T_AOEFRIEND) + pSpellHolder->Classication = TYPE_AREA_HEAL; + + TempRange = sSpellRangeStore.LookupEntry(TempSpell->rangeIndex); + TempRadius = sSpellRadiusStore.LookupEntry(TempSpell->EffectRadiusIndex[0]); + TempDuration = sSpellDurationStore.LookupEntry(TempSpell->DurationIndex); + TempCastTime = sCastTimesStore.LookupEntry(TempSpell->CastingTimeIndex); + + // Constants + pSpellHolder->CastTime = TempCastTime ? TempCastTime->CastTime : 0; + pSpellHolder->Cooldown = TempSpell->RecoveryTime; + pSpellHolder->Duration = TempDuration ? TempDuration->Duration[0] : 0; + pSpellHolder->Radius = TempRadius ? TempRadius->Radius : 0; + pSpellHolder->PowerCost = TempSpell->manaCost; + pSpellHolder->PowerType = TempSpell->powerType; + pSpellHolder->RangeMax = TempRange ? TempRange->maxRange : 0; + pSpellHolder->RangeMin = TempRange ? TempRange->minRange : 0; + pSpellHolder->SpellId = pSpellArray[i]; + pSpellHolder->Targets = TargetType; + + // Reset Variables + pSpellHolder->CooldownRemaining = 0; + + m_SpellList.push_back(pSpellHolder); + } +} + +bool AISpellSystem::Cast(SpellHolder* pSpell) +{ + //Silenced so we can't cast + if (m_Unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + return false; + + //Check for power + if (m_Unit->GetPower((Powers)(pSpell->PowerType)) < pSpell->PowerCost) + return false; + + //Still on cooldown + if (pSpell->CooldownRemaining) + return false; + + //Holder for Target. If target remains null spell will not be casted. + Unit* pTarget = NULL; + + //Individual checks based on classification + switch (pSpell->Classication) + { + //Replace with checks for friendly units hp when possible + case TYPE_SELF_HEAL: + case TYPE_HEAL: + case TYPE_AREA_HEAL: + case TYPE_HEAL_OVER_TIME: + + ASSERT(m_Unit->GetMaxHealth()); + + if (m_Unit->GetHealth()*3/m_Unit->GetMaxHealth() < 2 && + !m_Unit->HasAura(pSpell->SpellId, 0) && !m_Unit->HasAura(pSpell->SpellId, 1) && !m_Unit->HasAura(pSpell->SpellId, 2)) + pTarget = m_Unit; + break; + + case TYPE_DEBUFF: + case TYPE_DAMAGE_OVER_TIME: + { + Unit* pTemp; + + if (m_Unit->getThreatManager().getThreatList().empty()) + break; + + std::list::iterator i = m_Unit->getThreatManager().getThreatList().begin(); + for (; i!=m_Unit->getThreatManager().getThreatList().end(); ++i) + { + pTemp = Unit::GetUnit((*m_Unit),(*i)->getUnitGuid()); + + if (pTemp && pTemp->isAlive() && + !pTemp->HasAura(pSpell->SpellId, 0) && !pTemp->HasAura(pSpell->SpellId, 1) && !pTemp->HasAura(pSpell->SpellId, 2) && + (m_Unit->GetDistanceSq(pTemp) < pSpell->RangeMax*pSpell->RangeMax && m_Unit->GetDistanceSq(pTemp) > pSpell->RangeMin*pSpell->RangeMin)) + { + pTarget = pTemp; + + //Set cooldown = to duration of spell to prevent spamming of dots and debuffs + //pSpell->CooldownRemaining = pSpell->Duration; + break; + } + } + } + break; + + case TYPE_CROWD_CONTROL: + case TYPE_SNARE: + { + if (m_Unit->getThreatManager().getThreatList().size() < 2) + break; + + Unit* pTemp; + + std::list::iterator i = m_Unit->getThreatManager().getThreatList().begin(); + for (; i!=m_Unit->getThreatManager().getThreatList().end(); ++i) + { + pTemp = Unit::GetUnit((*m_Unit),(*i)->getUnitGuid()); + + if (pTemp && pTemp->isAlive() && m_Unit->getVictim() != pTemp && + !pTemp->HasAura(pSpell->SpellId, 0) && !pTemp->HasAura(pSpell->SpellId, 1) && !pTemp->HasAura(pSpell->SpellId, 2) && + (m_Unit->GetDistanceSq(pTemp) < pSpell->RangeMax*pSpell->RangeMax && m_Unit->GetDistanceSq(pTemp) > pSpell->RangeMin*pSpell->RangeMin)) + { + pTarget = pTemp; + break; + } + } + } + break; + + case TYPE_AREA_DAMAGE: + { + //Check that at least 2 players are within range + Unit* pTemp; + uint32 PlayersInRange = 0; + + if (m_Unit->getThreatManager().getThreatList().size() < 2) + break; + + std::list::iterator i = m_Unit->getThreatManager().getThreatList().begin(); + for (; i!=m_Unit->getThreatManager().getThreatList().end(); ++i) + { + pTemp = Unit::GetUnit((*m_Unit),(*i)->getUnitGuid()); + if (pTemp && pTemp->isAlive() && + (m_Unit->GetDistanceSq(pTemp) < pSpell->Radius)) + { + PlayersInRange++; + } + } + + //More than 1 player in range + if (PlayersInRange > 1) + pTarget = m_Unit; + } + break; + + case TYPE_DIRECT_DAMAGE: + { + //This causes the creature to cast on the first player that this target is allowed to hit (range issues etc) + //Threat list is always in order from Highest threat to lowest + Unit* pTemp; + + if (m_Unit->getThreatManager().getThreatList().empty()) + break; + + std::list::iterator i = m_Unit->getThreatManager().getThreatList().begin(); + for (; i!=m_Unit->getThreatManager().getThreatList().end(); ++i) + { + pTemp = Unit::GetUnit((*m_Unit),(*i)->getUnitGuid()); + if (pTemp && pTemp->isAlive() && + (m_Unit->GetDistanceSq(pTemp) < pSpell->RangeMax*pSpell->RangeMax && m_Unit->GetDistanceSq(pTemp) > pSpell->RangeMin*pSpell->RangeMin)) + { + pTarget = pTemp; + break; + } + } + } + break; + + case TYPE_BUFF: + //Later replace with checks for other creatures in area + if (!m_Unit->HasAura(pSpell->SpellId, 0) && !m_Unit->HasAura(pSpell->SpellId, 1) && !m_Unit->HasAura(pSpell->SpellId, 2) && + (!m_Unit->isInCombat() || m_Unit->GetPower((Powers)pSpell->PowerType) > m_Unit->GetMaxPower((Powers)pSpell->PowerType)-pSpell->PowerCost)) + pTarget = m_Unit; + break; + + case TYPE_NONE: + //Simply attempt to cast on current target since we have no idea what this spell does + pTarget = m_Unit->getVictim(); + break; + }; + + if (!pTarget) + return false; + + //Cast spell + m_Unit->CastSpell(pTarget, pSpell->SpellId, false); + pSpell->CooldownRemaining += pSpell->Cooldown + AI_GCD; + + return true; +} + +bool AISpellSystem::Update(const uint32 diff) +{ + if (!m_Unit || m_SpellList.empty()) + return false; + + //Decrease cooldowns + for (std::list::iterator i = m_SpellList.begin(); i != m_SpellList.end(); ++i) + { + if ((*i)->CooldownRemaining) + if ((*i)->CooldownRemaining > diff) + (*i)->CooldownRemaining -= diff; + else (*i)->CooldownRemaining = 0; + } + + //Prevent Movement while casting + if (m_Unit->IsNonMeleeSpellCasted(false)) + { + m_Unit->StopMoving(); + + return true; + } + + //Cast first valid spell + for (std::list::iterator i = m_SpellList.begin(); i != m_SpellList.end(); ++i) + { + //Cast returns true if the spell was properly cast + if (Cast((*i))) + break; + } + + return true; +} Index: src/game/AISpellSystem.h =================================================================== --- src/game/AISpellSystem.h (revision 0) +++ src/game/AISpellSystem.h (revision 0) @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Unit.h" + +class Creature; +struct SpellHolder; + +class AISpellSystem +{ +public: + AISpellSystem(Creature*); + AISpellSystem(Unit*, const uint32* pSpellArray, const uint32 Size); + ~AISpellSystem(); + + bool Update(const uint32); + +private: + Unit* m_Unit; + std::list m_SpellList; + + void ClassifySpells(const uint32* pSpellArray, const uint32 Size); + bool Cast(SpellHolder*); +}; Index: src/game/BattleGroundMgr.cpp =================================================================== --- src/game/BattleGroundMgr.cpp (revision 4773) +++ src/game/BattleGroundMgr.cpp (working copy) @@ -79,10 +79,34 @@ else m_QueuedPlayers[queue_id].Horde++; - /* temporary BG queue status message - std::ostringstream str; - str << "Battleground queue invited status: horde: " << m_QueuedPlayers[queue_id].Horde << ", aliance: " << m_QueuedPlayers[queue_id].Alliance; - plr->Say(str.str(), LANG_UNIVERSAL);*/ + /* temporary BG queue status message */ + //#TODO: add proper check for the number of players in queue + if ( m_QueuedPlayers[queue_id].Alliance == 5 || m_QueuedPlayers[queue_id].Alliance == 7 || m_QueuedPlayers[queue_id].Alliance == 10 || + m_QueuedPlayers[queue_id].Horde == 5 || m_QueuedPlayers[queue_id].Horde == 7 || m_QueuedPlayers[queue_id].Horde == 10 ) + { + std::ostringstream str; + str << "Battleground queue invited status for "; + switch (bgTypeId) + { + case 1: + str << "Alterac Valley"; break; + case 2: + str << "Warsong Gulch"; break; + case 3: + str << "Arathi Basin"; break; + //#TODO: add other cases + default: + str << "other bg / arena"; break; + } + str << ", for level "; + if(plr->getLevel()!=sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + str << (int)(plr->getLevel()/10)*10 << " - " << (int)(plr->getLevel()/10)*10+9; + else + str << plr->getLevel(); + str << ":\n Horde: " << m_QueuedPlayers[queue_id].Horde << ", Alliance: " << m_QueuedPlayers[queue_id].Alliance; + sWorld.SendWorldText(str.str().c_str(), NULL); + } + /* plr->Say(str.str(), LANG_UNIVERSAL);*/ this->Update(bgTypeId, queue_id); } Index: src/game/Chat.cpp =================================================================== --- src/game/Chat.cpp (revision 4773) +++ src/game/Chat.cpp (working copy) @@ -265,15 +265,18 @@ { "die", SEC_ADMINISTRATOR, &ChatHandler::HandleDieCommand, "", NULL }, { "revive", SEC_ADMINISTRATOR, &ChatHandler::HandleReviveCommand, "", NULL }, { "dismount", SEC_PLAYER, &ChatHandler::HandleDismountCommand, "", NULL }, + { "activequests", SEC_PLAYER, &ChatHandler::HandleActiveQuestsCommand, "", NULL }, { "setmodel", SEC_GAMEMASTER, &ChatHandler::HandleSetModelCommand, "", NULL }, { "factionid", SEC_GAMEMASTER, &ChatHandler::HandleFactionIdCommand, "", NULL }, { "listgm", SEC_PLAYER, &ChatHandler::HandleGMListCommand, "", NULL }, + { "bug", SEC_PLAYER, &ChatHandler::HandleBugCommand, "", NULL }, { "gm", SEC_MODERATOR, &ChatHandler::HandleGMmodeCommand, "", NULL }, { "gps", SEC_MODERATOR, &ChatHandler::HandleGPSCommand, "", NULL }, { "guid", SEC_GAMEMASTER, &ChatHandler::HandleGUIDCommand, "", NULL }, { "help", SEC_PLAYER, &ChatHandler::HandleHelpCommand, "", NULL }, { "info", SEC_PLAYER, &ChatHandler::HandleInfoCommand, "", NULL }, { "npcinfo", SEC_ADMINISTRATOR, &ChatHandler::HandleNpcInfoCommand, "", NULL }, + { "creatureinfo", SEC_PLAYER, &ChatHandler::HandleCreatureInfoCommand, "", NULL }, { "npcinfoset", SEC_ADMINISTRATOR, &ChatHandler::HandleNpcInfoSetCommand, "", NULL }, { "addvendoritem",SEC_GAMEMASTER, &ChatHandler::HandleAddVendorItemCommand, "", NULL }, { "delvendoritem",SEC_GAMEMASTER, &ChatHandler::HandleDelVendorItemCommand, "", NULL }, @@ -840,4 +843,4 @@ } return obj; -} \ No newline at end of file +} Index: src/game/Chat.h =================================================================== --- src/game/Chat.h (revision 4773) +++ src/game/Chat.h (working copy) @@ -91,6 +91,7 @@ bool HandleDismountCommand(const char* args); bool HandleSaveCommand(const char* args); bool HandleGMListCommand(const char* args); + bool HandleBugCommand(const char* args); bool HandleNamegoCommand(const char* args); bool HandleGonameCommand(const char* args); @@ -254,6 +255,7 @@ bool HandleExploreCheatCommand(const char* args); bool HandleTextEmoteCommand(const char* args); bool HandleNpcInfoCommand(const char* args); + bool HandleCreatureInfoCommand(const char* args); bool HandleNpcInfoSetCommand(const char* args); bool HandleHoverCommand(const char* args); bool HandleLevelUpCommand(const char* args); @@ -292,6 +294,7 @@ bool HandleLookupSkillCommand(const char* args); bool HandleLookupSpellCommand(const char* args); bool HandleLookupQuestCommand(const char* args); + bool HandleActiveQuestsCommand(const char* args); bool HandleLookupCreatureCommand(const char* args); bool HandleLookupObjectCommand(const char* args); bool HandlePasswordCommand(const char* args); Index: src/game/Group.cpp =================================================================== --- src/game/Group.cpp (revision 4773) +++ src/game/Group.cpp (working copy) @@ -575,6 +575,7 @@ { uint16 dest; LootItem *item = &(roll->loot->items[roll->itemSlot]); + if(!item){roll->loot->NotifyItemRemoved(roll->itemSlot); roll->loot->unlootedCount--; RollId.erase(roll); return;} uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count, false ); if ( msg == EQUIP_ERR_OK ) { Index: src/game/Language.h =================================================================== --- src/game/Language.h (revision 4773) +++ src/game/Language.h (working copy) @@ -375,6 +375,7 @@ #define LANG_COMMAND_GRAVEYARDERROR "No faction in Graveyard with id= #%u , fix your DB" #define LANG_COMMAND_GRAVEYARD_NOTEAM "invalid team, please fix database" #define LANG_COMMAND_GRAVEYARD_ANY "any" +#define LANG_COMMAND_BUGREPORTED "Aciu %s, Atsimink kad sita komanda galima naudoti tik norint pranesti zaidimo klaidas." #define LANG_COMMAND_GRAVEYARD_ALLIANCE "alliance" #define LANG_COMMAND_GRAVEYARD_HORDE "horde" #define LANG_COMMAND_GRAVEYARDNEAREST "Graveyard #%u (faction: %s) is nearest from linked to zone #%u." @@ -478,6 +479,7 @@ #define LANG_MOVEGENS_UNKNOWN "Unknown movement generator (%u)" #define LANG_NPCINFO_CHAR "Player selected NPC\nGUID: %u.\nFaction: %u.\nnpcFlags: %u.\nEntry: %u.\nDisplayID: %u (Native: %u)." +#define LANG_CREATUREINFO_CHAR "Player selected Creature\nGUID: %u.\nEntry: %u." #define LANG_NPCINFO_LEVEL "Level: %u." #define LANG_NPCINFO_HEALTH "Health (base): %u. (max): %u. (current): %u." #define LANG_NPCINFO_FLAGS "Field Flags: %u.\nDynamic Flags: %u.\nFaction Template: %u." Index: src/game/Level0.cpp =================================================================== --- src/game/Level0.cpp (revision 4773) +++ src/game/Level0.cpp (working copy) @@ -297,3 +297,52 @@ SendSysMessage(LANG_BAD_VALUE); return true; } + +bool ChatHandler::HandleCreatureInfoCommand(const char* args) +{ + Creature* target = getSelectedCreature(); + + if(!target) + { + SendSysMessage(LANG_SELECT_CREATURE); + return true; + } + + uint32 Entry = target->GetUInt32Value(OBJECT_FIELD_ENTRY); + CreatureInfo const* cInfo = target->GetCreatureInfo(); + + PSendSysMessage(LANG_CREATUREINFO_CHAR, target->GetDBTableGUIDLow(), Entry); + + return true; +} + +bool ChatHandler::HandleBugCommand(const char* args) +{ + if (!*args) + return false; + + std::string bug=args; + WorldDatabase.escape_string(bug); + PSendSysMessage(LANG_COMMAND_BUGREPORTED, m_session->GetPlayerName()); + WorldDatabase.PExecute("INSERT INTO `bugs` (`name`, `bug`) VALUES ('%s', '%s')", m_session->GetPlayerName(), bug.c_str()); + return true; +} + +bool ChatHandler::HandleActiveQuestsCommand(const char* args) +{ + QuestStatusMap* qmap = &(m_session->GetPlayer()->getQuestStatusMap()); + for(QuestStatusMap::iterator itr=qmap->begin(); itr!=qmap->end(); ++itr) + { + if(itr->second.m_status == QUEST_STATUS_INCOMPLETE) + { + std::ostringstream str; + str << "Quest id #" << itr->first; + QueryResult *result=WorldDatabase.PQuery("SELECT `Title` FROM `quest_template` WHERE `entry`='%u'", itr->first); + if(result) + { str << " - " << result->Fetch()[0].GetCppString(); delete result; } + str << " is active."; + PSendSysMessage(str.str().c_str()); + } + } + return true; +} \ No newline at end of file Index: src/game/Spell.cpp =================================================================== --- src/game/Spell.cpp (revision 4773) +++ src/game/Spell.cpp (working copy) @@ -2554,6 +2554,8 @@ else if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MECHANIC_IMMUNITY) mechanic_immune |= 1 << m_spellInfo->EffectMiscValue[i]; } + //immune movement impairement and loss of control + if(m_spellInfo->Id==(uint32)42292)mechanic_immune=0x9967da6; //Check whether the cast should be prevented by any state you might have. uint8 prevented_reason = 0; @@ -3417,7 +3419,7 @@ case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: { if(m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_TARGET_NOT_PLAYER; - if(m_spellInfo->rangeIndex == 1 || m_spellInfo->rangeIndex == 2 || m_spellInfo->rangeIndex == 7) + if(m_spellInfo->rangeIndex == 1 || m_spellInfo->rangeIndex == 2 || m_spellInfo->rangeIndex == 6 || m_spellInfo->rangeIndex == 7) break; Item *pItem = ((Player*)m_caster)->GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED ); if(!pItem || pItem->IsBroken() || pItem->GetProto()->Class != ITEM_CLASS_WEAPON ) Index: src/game/SpellAuras.cpp =================================================================== --- src/game/SpellAuras.cpp (revision 4773) +++ src/game/SpellAuras.cpp (working copy) @@ -2645,8 +2645,9 @@ void Aura::HandleModMechanicImmunity(bool apply, bool Real) { - uint32 mechanic = m_modifier.m_miscvalue; - + uint32 mechanic = 1 << m_modifier.m_miscvalue; + //immune movement impairement and loss of control + if(GetId()==(uint32)42292)mechanic=0x9967da6; if(apply) { Unit::AuraMap& Auras = m_target->GetAuras(); @@ -2655,7 +2656,10 @@ next = iter; next++; SpellEntry const *spell = iter->second->GetSpellProto(); - if(spell->Mechanic == mechanic && !iter->second->IsPositive() && spell->Id != GetId()) + if( (1<Mechanic) & mechanic //check for mechanic mask + && !( spell->Attributes & 0x20000000) //spells unaffected by invulnerability + && !iter->second->IsPositive() //only remove negative spells + && spell->Id != GetId()) { m_target->RemoveAurasDueToSpell(spell->Id); if(Auras.empty()) Index: src/game/SpellEffects.cpp =================================================================== --- src/game/SpellEffects.cpp (revision 4773) +++ src/game/SpellEffects.cpp (working copy) @@ -2887,7 +2887,7 @@ bool criticalhit = false; WeaponAttackType attType = BASE_ATTACK; - if(m_spellInfo->rangeIndex != 1 && m_spellInfo->rangeIndex != 2 && m_spellInfo->rangeIndex != 7) + if(m_spellInfo->rangeIndex != 1 && m_spellInfo->rangeIndex != 2 && m_spellInfo->rangeIndex != 6 && m_spellInfo->rangeIndex != 7) { attType = RANGED_ATTACK; @@ -2952,7 +2952,7 @@ // take ammo if(m_caster->GetTypeId() == TYPEID_PLAYER) { - if(m_spellInfo->rangeIndex != 1 && m_spellInfo->rangeIndex != 2 && m_spellInfo->rangeIndex != 7) + if(m_spellInfo->rangeIndex != 1 && m_spellInfo->rangeIndex != 2 && m_spellInfo->rangeIndex != 6 && m_spellInfo->rangeIndex != 7) { Item *pItem = ((Player*)m_caster)->GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED ); if(!pItem || pItem->IsBroken()) Index: src/game/Unit.cpp =================================================================== --- src/game/Unit.cpp (revision 4773) +++ src/game/Unit.cpp (working copy) @@ -3998,7 +3998,40 @@ CastSpell(pVictim,37378,true,castItem,triggredByAura); return; } + // Windfury weapon + case 33757: + case 33756: + case 33755: + case 33754: + case 33727: + { + // never proc from another windfury attack or the AP bonus will stack and we will get really huge criticals + if ( !procSpell || ( procSpell->Id != 25504 && procSpell->Id != 33750 ) ){ + // give us temporary AP bonus + if(GetTypeId() == TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(25504) ) + return; + + HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(dummySpell->EffectBasePoints[0]+1), true); + + int32 EffectBasePoints0 = CalculateDamage(BASE_ATTACK); + + CastCustomSpell(pVictim,25504,&EffectBasePoints0,NULL,NULL,true,NULL,triggredByAura); + // cooldown + ((Player*)this)->AddSpellCooldown(25504,0,time(NULL) + 3); + + if (haveOffhandWeapon()) + { + int32 EffectBasePoints0 = CalculateDamage(OFF_ATTACK); + CastCustomSpell(pVictim,33750,&EffectBasePoints0,NULL,NULL,true,NULL,triggredByAura); + // cooldown + ((Player*)this)->AddSpellCooldown(33750,0,time(NULL) + 3); + } + + // remove it + HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(dummySpell->EffectBasePoints[0]+1), false); + } + } default: break; } Index: src/mangosd/Makefile.am =================================================================== --- src/mangosd/Makefile.am (revision 4771) +++ src/mangosd/Makefile.am (working copy) @@ -33,8 +33,8 @@ WorldRunnable.h ## Link world daemon against the shared library -mangos_worldd_LDADD = ../bindings/universal/libmangosscript.la ../game/libmangosgame.a ../shared/libmangosdatabase.a ../shared/libmangosconfig.a ../shared/libmangosauth.a ../shared/libmangosshared.a ../shared/libmangosvmaps.a ../shared/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a -mangos_worldd_LDFLAGS = -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/universal/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic +mangos_worldd_LDADD = ../bindings/ScriptDev2/libmangosscript.la ../game/libmangosgame.a ../shared/libmangosdatabase.a ../shared/libmangosconfig.a ../shared/libmangosauth.a ../shared/libmangosshared.a ../shared/libmangosvmaps.a ../shared/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a +mangos_worldd_LDFLAGS = -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/ScriptDev2/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic ## Additional files to include when running 'make dist' # Include world daemon configuration Index: src/shared/Network/Socket.cpp =================================================================== --- src/shared/Network/Socket.cpp (revision 4771) +++ src/shared/Network/Socket.cpp (working copy) @@ -345,6 +345,15 @@ SetCloseAndDelete(); return INVALID_SOCKET; } + + optval = 1; + if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&optval, sizeof(optval)) == -1) + { + Handler().LogError(this, "setsockopt(IPPROTO_TCP, TCP_NODELAY)", Errno, StrError(Errno), LOG_LEVEL_FATAL); + closesocket(s); + SetCloseAndDelete(); + return INVALID_SOCKET; + } } return s; } @@ -1254,3 +1263,6 @@ + + +