Index: src/framework/Platform/Define.h =================================================================== --- src/framework/Platform/Define.h (revision 4763) +++ src/framework/Platform/Define.h (working copy) @@ -31,6 +31,7 @@ #define MANGOS_IMPORT __cdecl #define MANGOS_SCRIPT_EXT ".dll" #define MANGOS_SCRIPT_NAME "MaNGOSScript" +#define MANGOS_TIER_SCRIPT_NAME "Script" #define SIGQUIT 3 #else #define MANGOS_LIBRARY_HANDLE void* Index: src/game/AggressorAI.cpp =================================================================== --- src/game/AggressorAI.cpp (revision 4763) +++ src/game/AggressorAI.cpp (working copy) @@ -28,129 +28,197 @@ #include -int -AggressorAI::Permissible(const Creature *creature) +int AggressorAI::Permissible(const Creature *creature) { // have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight - if( !creature->isCivilian() && !creature->IsNeutralToAll() ) + if( !creature->isCivilian() && !creature->IsNeutralToAll() && !creature->m_creatureSpellBook ) return PERMIT_BASE_PROACTIVE; 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_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK) { } -void -AggressorAI::MoveInLineOfSight(Unit *u) +void AggressorAI::MoveInLineOfSight(Unit *who) { - if( !i_creature.getVictim() && !i_creature.hasUnitState(UNIT_STAT_STUNDED) && u->isTargetableForAttack() && - ( i_creature.IsHostileTo( u ) /*|| u->getVictim() && i_creature.IsFriendlyTo( u->getVictim() )*/ ) && - u->isInAccessablePlaceFor(&i_creature) ) + //Planning something to get Rid of all Z check like that will be great since it wrong, GS + if( !i_creature.HasPathTo(who) ) + return; + + if( !i_creature.GetUInt64Value(UNIT_FIELD_TARGET) ) { - float attackRadius = i_creature.GetAttackDistance(u); - if(i_creature.IsWithinDistInMap(u, attackRadius) && i_creature.GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE) + //MoveInLineOfSight Normal + if( i_creature.IsHostileTo(who) && who->isInAccessablePlaceFor(&i_creature) ) { - if(!i_creature.IsWithinLOSInMap(u)) return; - AttackStart(u); - u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + float _attackRadius = i_creature.GetAttackDistance(who); + // Without AggroRate will be much Better and I Sugest we remove + if( i_creature.IsWithinDistInMap(who, _attackRadius) ) + { + if(!i_creature.IsWithinLOSInMap(who)) + return; + + if( who->HasStealthAura() ) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + selectedAttackRadius = _attackRadius; + AttackStart(who); + AIMotionTarget(who,FOLLOW_RANGE_MELEE); + } + //RunAway In Fear, TODO: Check for Attacker List if not empty and we are not in, Stop That and Attack + //_attackRadius + RUNAWAY_AGGRO_RANGE, More you put more creature has chance to runaway and from far + //_attackRadius <= irand(5,10) , from witch player they runaway... i use same system as aggrorange + else if( _attackRadius <= irand(0,10) + && who->GetTypeId() == TYPEID_PLAYER + && i_creature.IsWithinDistInMap(who, _attackRadius + RUNAWAY_AGGRO_RANGE) + && !i_creature.HasAura(CREATURE_SPELL_RUNAWAY,0) ) + { + float X,Y,Z; + i_creature.GetRespawnCoord(X,Y,Z); + selectedAttackRadius = _attackRadius; + if(who->GetDistance2d(X,Y) < RANGE_MAX_AUTO_FEAR) + i_creature.CastSpell( (Unit*)&i_creature, CREATURE_SPELL_RUNAWAY, false ,NULL,NULL, who->GetGUID()); + } + return; } + //MoveInLineOfSight Friendly + //TODO: hasHealer must be gone away and m_assistance, redesing, GS + if( i_creature.IsWithinDistInMap(who,RANGE_AUTO_ASSIST ) && i_creature.IsFriendlyTo(who) && !who->m_hasHealer ) //!irand(0,20) , can be used for Strongnest Adjustement, less Chance more easy + { + if(!i_creature.IsWithinLOSInMap(who)) + return; + + //TODO: make it with IsInCombat() + Take target from AttackerList. + Unit *victimTarget = who->getVictim(); + if( who->getVictim() ) + { + if( i_creature.IsHostileTo(victimTarget) ) + { + AttackStart( victimTarget ); + AIMotionTarget(victimTarget,FOLLOW_RANGE_MELEE); + //That is Ugly, i think i can obtain my status into another check with a Operator Overload + //So temporary i will let it there. + if( who->GetTypeId() == TYPEID_UNIT && ((Creature*)who)->m_AssistenceStatus->requested ) + ((Creature*)who)->m_AssistenceStatus->obtained = true; + } + } + } } } void AggressorAI::EnterEvadeMode() { - if( !i_creature.isAlive() ) - { - DEBUG_LOG("Creature stopped attacking cuz his dead [guid=%u]", i_creature.GetGUIDLow()); - i_victimGuid = 0; - i_creature.CombatStop(true); - i_creature.DeleteThreatList(); + lastAction = getMSTime()+30000; + i_creature.m_AssistenceStatus->reset(); //TODO: fix that scruct, at least make it proctected + if( !i_creature.isCharmed() ) + i_creature.RemoveAllAuras(); + i_creature.DeleteThreatList(); + i_creature.CombatStop(true); +} + +void AggressorAI::UpdateAI(const uint32 /*diff*/) +{ + if( i_creature.isDead() || i_creature.hasUnitState( UNIT_STAT_FLEEING ) ) return; - } - Unit* victim = ObjectAccessor::Instance().GetUnit(i_creature, i_victimGuid ); - - if( !victim ) - { - DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_creature.GetGUIDLow()); + //RunAway At friend + //Unit::getHpPercent(&i_creature)+irand(10,20), can really change the Strongest. + if( !i_creature.hasUnitState( UNIT_STAT_ROOT ) && selectedAttackRadius <= irand(0,40) && i_creature.getVictim() && Unit::getHpPercent(&i_creature)+irand(10,20) < Unit::getHpPercent(i_creature.getVictim()) ) + { + //i_creature.getVictim()->getAttackers().size() < 3 , can radicaly change the Strongest + if( !i_creature.m_AssistenceStatus->requested && i_creature.getVictim()->getAttackers().size() < 2) + { + i_creature.CastSpell( (Unit*)&i_creature, CREATURE_SPELL_TAP_FRIEND, false ); + i_creature.m_AssistenceStatus->requested = true; + return; + } + //Check spell http://www.wowhead.com/?spell=32126, who is Flee Combat Much More Better, and keep fear when no combat + else if(i_creature.m_AssistenceStatus->obtained = false && Unit::getHpPercent(&i_creature) < irand(7,14) ) + i_creature.CastSpell( (Unit*)&i_creature, CREATURE_SPELL_RUNAWAY, false ,NULL,NULL, i_creature.GetUInt64Value(UNIT_FIELD_TARGET)); } - else if( !victim->isAlive() ) + + if( i_creature.SelectHostilTarget() && i_creature.getVictim() && i_creature.isAttackReady() && i_creature.IsWithinDistInMap(i_creature.getVictim(), i_creature.GetFloatValue(UNIT_FIELD_COMBATREACH)) ) { - DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", i_creature.GetGUIDLow()); + //TODO:Add a Chance To win and Go to Friend. + i_creature.AttackerStateUpdate(i_creature.getVictim()); + i_creature.resetAttackTimer(); + lastAction = getMSTime(); } - else if( victim->HasStealthAura() ) - { - DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_creature.GetGUIDLow()); - } - else if( victim->isInFlight() ) - { - DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_creature.GetGUIDLow()); - } - else - { - DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", i_creature.GetGUIDLow()); - //i_state = STATE_LOOK_AT_VICTIM; - //i_tracker.Reset(TIME_INTERVAL_LOOK); - } - if(!i_creature.isCharmed()) + //Wait Time Reset Action Selected, TODO: Change that to put more dynamic action at different timer. + //A text Chat can be done there, will be cool. + //Do search on wowhead for Gossip before any chat text system. + if(lastAction+irand(5000*CREATURE_WAIT_RATE,7000*CREATURE_WAIT_RATE) < getMSTime() ) { - i_creature.RemoveAllAuras(); + i_creature.m_AssistenceStatus->reset(); - // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead - if( i_creature.GetMotionMaster()->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE ) - i_creature.GetMotionMaster()->TargetedHome(); + i_creature.SelectHostilTarget(); + + lastAction = getMSTime(); } +} - i_creature.DeleteThreatList(); - i_victimGuid = 0; - i_creature.CombatStop(true); - +bool AggressorAI::IsVisible(Unit *pl) const +{ + return i_creature.GetDistanceSq(pl) < sWorld.getConfig(CONFIG_SIGHT_MONSTER) + && pl->isVisibleForOrDetect(&i_creature,true); } -void -AggressorAI::UpdateAI(const uint32 /*diff*/) +void AggressorAI::AttackStart(Unit *unit) { - // update i_victimGuid if i_creature.getVictim() !=0 and changed - if(!i_creature.SelectHostilTarget() || !i_creature.getVictim()) + if( !unit ) return; - i_victimGuid = i_creature.getVictim()->GetGUID(); - - if( i_creature.IsWithinDistInMap(i_creature.getVictim(), ATTACK_DISTANCE)) + if( unit->isTargetableForAttack() /*&& attacker!= m_creature, That should not happen*/ ) { - if( i_creature.isAttackReady() ) + if ( i_creature.Attack(unit) ) { - i_creature.AttackerStateUpdate(i_creature.getVictim()); + i_creature.AddThreat(unit, 0.0f); i_creature.resetAttackTimer(); + + if (unit->GetTypeId() == TYPEID_PLAYER) + i_creature.SetLootRecipient((Player*)unit); + selectedAttackRadius = i_creature.GetAttackDistance(unit); + AIMotionTarget(unit,FOLLOW_RANGE_MELEE); + + return; } } } -bool -AggressorAI::IsVisible(Unit *pl) const +void AggressorAI::AIMotionTarget(Unit* target, float offSet) { - return i_creature.GetDistanceSq(pl) < sWorld.getConfig(CONFIG_SIGHT_MONSTER) - && pl->isVisibleForOrDetect(&i_creature,true); -} + bool exist = false; + MotionMaster* creatureMM = i_creature.GetMotionMaster(); -void -AggressorAI::AttackStart(Unit *u) -{ - if( !u ) - return; + if(!target /*&& creatureMM->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE*/) + { + //creatureMM->MovementExpired(); + return; + } + //Here is wrong... TODO fix angle... but in fact i wan MV for Follower and One for Attacker. + float angle = NULL; + if( !i_creature.IsHostileTo(target) ) + { + angle = target->GetOrientation()*2.0f; + } - if(i_creature.Attack(u)) + if(creatureMM->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE) { - i_creature.AddThreat(u, 0.0f); - // DEBUG_LOG("Creature %s tagged a victim to kill [guid=%u]", i_creature.GetName(), u->GetGUIDLow()); - i_victimGuid = u->GetGUID(); - - i_creature.resetAttackTimer(); - i_creature.GetMotionMaster()->Mutate(new TargetedMovementGenerator(*u)); - if (u->GetTypeId() == TYPEID_PLAYER) - i_creature.SetLootRecipient((Player*)u); + TargetedMovementGenerator* targetMV = static_cast*> (creatureMM->top()); + if( targetMV && targetMV->GetTarget() ) + { + if( targetMV->GetTarget()->GetGUID() != target->GetGUID() ) + { + creatureMM->MovementExpired(false); + creatureMM->Mutate(new TargetedMovementGenerator(*target,offSet,angle)); + } + else if(targetMV->GetOffSet() != offSet) + targetMV->SetOffSet(offSet); + } } + else + { + i_creature.GetMotionMaster()->Mutate(new TargetedMovementGenerator(*target,offSet,angle)); + } } Index: src/game/AggressorAI.h =================================================================== --- src/game/AggressorAI.h (revision 4763) +++ src/game/AggressorAI.h (working copy) @@ -45,8 +45,10 @@ static int Permissible(const Creature *); private: + void AIMotionTarget(Unit* target, float offSet); Creature &i_creature; - uint64 i_victimGuid; + uint32 lastAction; + float selectedAttackRadius; AggressorState i_state; TimeTracker i_tracker; }; Index: src/game/AggressorCasterAI.cpp =================================================================== --- src/game/AggressorCasterAI.cpp (revision 0) +++ src/game/AggressorCasterAI.cpp (revision 0) @@ -0,0 +1,792 @@ +/* + * 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 "AggressorCasterAI.h" +#include "Errors.h" +#include "Creature.h" +#include "Player.h" +#include "TargetedMovementGenerator.h" +#include "ConfusedMovementGenerator.h" +#include "RandomMovementGenerator.h" +#include "HomeMovementGenerator.h" +#include "Database/DBCStores.h" +#include "ObjectAccessor.h" +#include "VMapFactory.h" +#include "World.h" + +#include + +int AggressorCasterAI::Permissible(const Creature *creature) +{ + // have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight + if( !creature->isCivilian() && !creature->IsNeutralToAll() && creature->m_creatureSpellBook ) + return PERMIT_BASE_PROACTIVE; + + return PERMIT_BASE_NO; +} + +AggressorCasterAI::AggressorCasterAI(Creature &c) : spellCasting(new CreatureSpellCasting(c.m_creatureSpellBook)), +i_creature(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK), +selectedAttackRadius(100.0f),lastAction(0) {} + +void AggressorCasterAI::MoveInLineOfSight(Unit *who) +{ + //Planning something to get Rid of all Z check like that will be great since it wrong, GS + if( !i_creature.HasPathTo(who) ) + return; + + if( !i_creature.GetUInt64Value(UNIT_FIELD_TARGET) ) + { + //MoveInLineOfSight Normal + if( i_creature.IsHostileTo(who) && who->isInAccessablePlaceFor(&i_creature) ) + { + float _attackRadius = i_creature.GetAttackDistance(who); + // Without AggroRate will be much Better and I Sugest we remove + if( i_creature.IsWithinDistInMap(who, _attackRadius) ) + { + if(!i_creature.IsWithinLOSInMap(who)) + return; + + if( who->HasStealthAura() ) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + AttackStart(who); + selectedAttackRadius = _attackRadius; + if(i_creature.m_creatureSpellBook) + AIMotionTarget(who,FOLLOW_RANGE_CASTER); + else + AIMotionTarget(who,FOLLOW_RANGE_MELEE); + } + //RunAway In Fear, TODO: Check for Attacker List if not empty and we are not in, Stop That and Attack + else if( _attackRadius <= irand(0,10) + && who->GetTypeId() == TYPEID_PLAYER + && i_creature.IsWithinDistInMap(who, _attackRadius + RUNAWAY_AGGRO_RANGE) + && !i_creature.HasAura(CREATURE_SPELL_RUNAWAY,0) ) + { + + float X,Y,Z; + i_creature.GetRespawnCoord(X,Y,Z); + selectedAttackRadius = _attackRadius; + if(who->GetDistance2d(X,Y) < RANGE_MAX_AUTO_FEAR) + i_creature.CastSpell( (Unit*)&i_creature, CREATURE_SPELL_RUNAWAY, false ,NULL,NULL, who->GetGUID()); + } + return; //Maybe put dynamic Cast on enemy here. + } + //MoveInLineOfSight Friendly + if( i_creature.IsWithinDistInMap(who,RANGE_AUTO_ASSIST ) && i_creature.IsFriendlyTo(who) && ( !who->m_hasHealer || !irand(0,20) ) ) //!irand(0,20) , can be used for Strongnest Adjustement, less Chance more easy + { + if(!i_creature.IsWithinLOSInMap(who)) + return; + + //TODO: make it with IsInCombat() + Take target from AttackerList. + Unit *victimTarget = who->getVictim(); + if( who->getVictim() ) + { + if( i_creature.IsHostileTo(victimTarget) ) + { + if( spellCasting->spellBook && spellCasting->spellBook->hasHealing() && Unit::getMpPercent(&i_creature) > 10 ) + { + i_creature.SetUInt64Value( UNIT_FIELD_TARGET, who->GetGUID() ); + who->m_hasHealer = true; + if(i_creature.m_creatureSpellBook) + AIMotionTarget(who,FOLLOW_RANGE_CASTER); + else + AIMotionTarget(who,FOLLOW_RANGE_MELEE); + AISelectSpell( spellCasting ); + } + else + { + AttackStart( victimTarget ); + if(i_creature.m_creatureSpellBook) + AIMotionTarget(victimTarget,FOLLOW_RANGE_CASTER); + else + AIMotionTarget(victimTarget,FOLLOW_RANGE_MELEE); + } + //That is Ugly, i think i can obtain my status into another check with a Operator Overload + //So temporary i will let it there. + if( who->GetTypeId() == TYPEID_UNIT && ((Creature*)who)->m_AssistenceStatus->requested ) + ((Creature*)who)->m_AssistenceStatus->obtained = true; + } + } + else if(spellCasting->spellBook && Unit::getHpPercent(who) < 100 ) //Target is IDLE Around we Heal Of Buff if we can + AISelectSpell( spellCasting, who ); + } + } + + //That has fucking too much check, must found the fastes way to dynamic heal... + //We are probaly doing something, event if, we try to do something on a dynamic target + if( i_creature.IsWithinDistInMap(who, RANGE_DYNAMIC_ASSIST ) && i_creature.IsFriendlyTo(who) ) + { + if(!i_creature.IsWithinLOSInMap(who)) + return; + + if( who->getVictim() && i_creature.IsFriendlyTo(who->getVictim())) + return; + else if( Unit::getHpPercent(who) < 100 && spellCasting->spellBook + && spellCasting->spellBook->hasHealing() + && Unit::getMpPercent(&i_creature) > 10 && !irand(0,10)) + { + if( spellCasting->spellIndex > -1 ) + { + if( !spellCasting->reached ) //We cancel Current spell Only if not allready into casting Timer + spellCasting->reset(); + else + return; + } + AISelectSpell( spellCasting, who ); + return; + } + } + //TODO:Dynamic cast on Enemy should not be needed ... but will test to see result. +} +void AggressorCasterAI::EnterEvadeMode() +{ + spellCasting->reset(true); + lastAction = getMSTime()+30000; + i_creature.m_AssistenceStatus->reset(); //TODO: fix that scruct, at least make it proctected + if( !i_creature.isCharmed() ) + i_creature.RemoveAllAuras(); + i_creature.DeleteThreatList(); + i_victimGuid = 0; + i_creature.CombatStop(true); +} +void AggressorCasterAI::UpdateAI(const uint32 diff) +{ + if( i_creature.isDead() || i_creature.hasUnitState( UNIT_STAT_FLEEING ) ) + return; + + //RunAway At friend + if( !i_creature.hasUnitState( UNIT_STAT_ROOT ) && selectedAttackRadius <= irand(0,40) && i_creature.getVictim() && Unit::getHpPercent(&i_creature)+irand(1,20) < Unit::getHpPercent(i_creature.getVictim()) ) + { + if( !i_creature.m_AssistenceStatus->requested ) + { + i_creature.CastSpell( (Unit*)&i_creature, CREATURE_SPELL_TAP_FRIEND, false ); + i_creature.m_AssistenceStatus->requested = true; + return; + } + //Check spell http://www.wowhead.com/?spell=32126, who is Flee Combat Much More Better, and keep fear when no combat + else if(i_creature.m_AssistenceStatus->obtained = false && Unit::getHpPercent(&i_creature) < irand(7,14) ) + i_creature.CastSpell( (Unit*)&i_creature, CREATURE_SPELL_RUNAWAY, false ,NULL,NULL, i_creature.GetUInt64Value(UNIT_FIELD_TARGET)); + } + + //Spell Casting + //Check spell: http://www.wowhead.com/?spell=39258 + //root spell should be putted by AICast() and Aura Time Controled with the Spell Timer we gonna cast + // so 1 check in less into MV for Spellcasting for Monster, Much more better + //All this will go Into Spellcasting Scructure or will become a class with self update at the end. + //remember + if(spellCasting->spellIndex > -1) + { + //Dynamic Target Check, In Fact is too be sure we don't change target or move during action on Dynamic target + //Procedure where made fast and will be need to be symplifie for better reading. + Unit *target = i_creature.GetUnit( i_creature,i_creature.GetUInt64Value(UNIT_FIELD_TARGET) ); + Unit *spellTarget = NULL; + + if( spellCasting->targetGUID != i_creature.GetGUID() ) + spellTarget = i_creature.GetUnit(i_creature,spellCasting->targetGUID ); + else + spellTarget = ((Unit*)&i_creature); + + bool dynamicTarget = false; + if(spellCasting->targetGUID != i_creature.GetGUID() && spellCasting->targetGUID != i_creature.GetUInt64Value(UNIT_FIELD_TARGET)) + dynamicTarget = true; + + if(spellTarget) + { + if( !spellCasting->reached ) + { + if(dynamicTarget) + { + if( i_creature.IsWithinDistInMap(spellTarget, spellCasting->range) ) + spellCasting->reached = true; + else + { + spellCasting->reset(); + return; + } + } + else + { + if( spellTarget->GetGUID() == i_creature.GetGUID() || i_creature.IsWithinDistInMap(spellTarget, spellCasting->range) ) + spellCasting->reached = true; + else + { + spellCasting->runningTimer += diff; + if(spellCasting->runningTimer > irand(500*CREATURE_WAIT_REACH_RATE,1000*CREATURE_WAIT_REACH_RATE) ) + { + //need Speed To reach target, so we cancel Current Spell and try Speed Up. + //DoDoSay("Too Long To Reach Target, IsStopped:%u ",i_creature.IsStopped()); + spellCasting->runningTimer = 0; + spellCasting->reset(); + + //Not check spellBook since we are here mean she go one for sure... But Maybe i forget something + if( i_creature.m_creatureSpellBook->hasSpeed() && !i_creature.hasUnitState( UNIT_STAT_ROOT ) ) + { + spellCasting->reset(); + AISelectSpell(spellCasting, spellTarget, SELECTSPELL_SPEED); + return; + } + //TODO: + //Else If CreatureSpeed < TargetSpeed , Chance To Cast Combat OUT Spell. + } + //return; + } + } + } + if( spellCasting->reached ) + { + if(!spellCasting->started) + { + //Aura Control for Root or we can get conflit with spellRoot and unroot before time after casting + if(!i_creature.hasUnitState(UNIT_STAT_ROOT) && spellCasting->spellBook->info[spellCasting->spellIndex].hasCastTime) + { + //i_creature.SendMonsterMove(i_creature.GetPositionX(),i_creature.GetPositionY(),i_creature.GetPositionZ(),0,true,0); + //i_creature.Relocate(i_creature.GetPositionX(),i_creature.GetPositionY(),i_creature.GetPositionZ()); + i_creature.addUnitState(UNIT_STAT_ROOT); + i_creature.StopMoving(); + } + AICast(spellCasting); + lastAction = getMSTime(); + return; + } + //Spell Finish + else if( i_creature.m_currentSpells[CURRENT_GENERIC_SPELL] ) + { + if( !i_creature.HasSpellChannelActiveTimer() ) + { + //TODO: Change Offset after a spell so we look more alive. + //if(!dynamicTarget && target && target->isAlive() && i_creature.hasUnitState(UNIT_STAT_FOLLOW)) + //AIMotionTarget(target, rand_norm()*(spellCasting->range)); + if(!i_creature.HasAuraType(SPELL_AURA_MOD_ROOT)) + i_creature.clearUnitState(UNIT_STAT_ROOT); + spellCasting->reset(); + } + return; + } + else + { + i_creature.clearUnitState(UNIT_STAT_ROOT); + spellCasting->reset(); + //if(!dynamicTarget && target && target->isAlive() && i_creature.hasUnitState(UNIT_STAT_FOLLOW)) + //AIMotionTarget(target); + return; + } + } + } + else if(dynamicTarget) //Bad dynamicTarget Target + { + if(!spellCasting->reached) + spellCasting->reset(); + } + else //Bad Target + { + if(!spellCasting->reached) + spellCasting->reset(true); + } + //return; + } + + //Melee Attack + //AICheckFriendTarget(), TODO:remove that from here.... My probleme is the Threath List will remove friend target + if( /*!AICheckFriendTarget()*/i_creature.SelectHostilTarget() && i_creature.getVictim() /*&& AICheckVictimTarget()*/ && i_creature.isAttackReady() && i_creature.IsWithinDistInMap(i_creature.getVictim(), i_creature.GetFloatValue(UNIT_FIELD_COMBATREACH)) ) + { + if(i_creature.getVictim()->HasSpellChannelActiveTimer() && i_creature.m_creatureSpellBook && i_creature.m_creatureSpellBook->hasInterrupt()) + { + spellCasting->reset(); //TODO:Check we are casting or not before reset. + AISelectSpell( spellCasting, i_creature.getVictim(), SELECTSPELL_INTERRUPT); + return; + } + //Add a Chance To win and Go to Friend. + + i_creature.AttackerStateUpdate(i_creature.getVictim()); + i_creature.resetAttackTimer(); + lastAction = getMSTime(); + + } + + //Choses a Spell To Cast + if( spellCasting->spellIndex < 0 && i_creature.GetUInt64Value(UNIT_FIELD_TARGET) ) + AISelectSpell( spellCasting ); + + //Wait Time Reset Action Selected, TODO: Change that to put more dynamic action at different timer. + //A text Chant can be done there, will be cool. + if(lastAction+irand(5000*CREATURE_WAIT_RATE,7000*CREATURE_WAIT_RATE) < getMSTime() ) + { + i_creature.m_AssistenceStatus->reset(); + if( spellCasting->spellIndex > -1 ) + { + if( !spellCasting->reached ) + spellCasting->reset(); + else + return; + } + + if( AICheckFriendTarget() ) + { + Unit* friendTarget = i_creature.GetUnit(i_creature, i_creature.GetUInt64Value(UNIT_FIELD_TARGET)); + if( friendTarget && friendTarget->getVictim() ) + { + AISelectSpell(spellCasting,friendTarget->getVictim()); + } + else if(friendTarget && Unit::getHpPercent(friendTarget) > irand(90,99)) + { + friendTarget->m_hasHealer = false; + i_creature.SelectHostilTarget(); + } + } + else + i_creature.SelectHostilTarget(); + + lastAction = getMSTime(); + } +} +void AggressorCasterAI::DamageTaken(Unit *done_by, uint32 &damage) +{ + // Should be Replaced with better Idea...., GS + // Make a helper Attack the Persone is attacking him, so next tick he will be a attacker. + if( !i_creature.getVictim() && Unit::getHpPercent(&i_creature) < irand(80,99) ) + { + i_creature.SetUInt64Value(UNIT_FIELD_TARGET,0); + MoveInLineOfSight(done_by); + } +} +void AggressorCasterAI::JustDie() +{ + // a healer should never ne target by a healer so, else case should be good + // More work needed on Healer selection, must be simple and not flood same target for healing purpose, GS + if( AICheckFriendTarget() ) + { + Unit* friendTarget = i_creature.GetUnit(i_creature, i_creature.GetUInt64Value(UNIT_FIELD_TARGET)); + friendTarget->m_hasHealer = false; + } + +} +bool AggressorCasterAI::IsVisible(Unit *pl) const +{ + return i_creature.GetDistanceSq(pl) < sWorld.getConfig(CONFIG_SIGHT_MONSTER) + && pl->isVisibleForOrDetect(&i_creature,true); +} + +void AggressorCasterAI::AttackStart(Unit *unit) +{ + if( !unit ) + return; + + // AICheckFriendTarget() should not appear here, Must be Checked to be sure she never needed here. + if( AICheckFriendTarget() && Unit::getHpPercent(unit) > Unit::getHpPercent(&i_creature)+20 ) + { + i_creature.AddThreat(unit, 0.0f); //target can come from threat, should put at better place + return; + } + + if( unit->isTargetableForAttack() /*&& attacker!= m_creature, That should not happen*/ ) + { + if ( i_creature.Attack(unit) ) + { + i_creature.AddThreat(unit, 0.0f); + i_creature.resetAttackTimer(); + if (unit->GetTypeId() == TYPEID_PLAYER) + i_creature.SetLootRecipient((Player*)unit); + selectedAttackRadius = i_creature.GetAttackDistance(unit); + if(i_creature.m_creatureSpellBook) + AIMotionTarget(unit,FOLLOW_RANGE_CASTER); + else + AIMotionTarget(unit,FOLLOW_RANGE_MELEE); + return; + } + } +} +void AggressorCasterAI::AISelectSpell(CreatureSpellCasting *spellCasting, Unit *dynamicTarget, uint8 specialCase) +{ + uint16 chosenSpellId; + std::list spellIndexFound; + + spellIndexFound.clear(); + + if(spellCasting->spellIndex > -1 || !spellCasting->spellBook || i_creature.HasSpellCooldown() ) + return; + + Unit *enemyTarget = NULL; + Unit *friendTarget = NULL; + if(dynamicTarget) + { + if(i_creature.IsHostileTo(dynamicTarget)) + enemyTarget = dynamicTarget; + else + friendTarget = dynamicTarget; + } + else if(AICheckFriendTarget()) + friendTarget = i_creature.GetUnit(*&i_creature, i_creature.GetUInt64Value(UNIT_FIELD_TARGET)); + else if(AICheckVictimTarget()) + enemyTarget = i_creature.getVictim(); + + switch( specialCase ) + { + case SELECTSPELL_NONE: + break; + case SELECTSPELL_SPEED: + { + spellIndexFound.clear(); + for(uint8 i = CREATURE_SPELL_SPEED_1; i < CREATURE_SPELL_SPEED_MAX; i++) + { + CreatureSpellBookInfo *spellInfo = &spellCasting->spellBook->info[i]; + if( spellInfo->id && !i_creature.HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) + spellIndexFound.push_front(i); + } + if( spellIndexFound.size() > 0 ) + { + chosenSpellId = 0; + std::list::iterator itr = spellIndexFound.begin(); + for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; + spellCasting->spellIndex = (*itr); + chosenSpellId = spellCasting->spellBook->info[(*itr)].id; + + //DoDoSay("Speed: %u",chosenSpellId); + SetSpellMaxRange(spellCasting); + if( !(spellCasting->spellBook->info[(*itr)].targets & (1 << (SELECT_TARGET_ANY_FRIEND-1))) ) + spellCasting->targetGUID = i_creature.GetGUID(); + else + spellCasting->targetGUID = enemyTarget->GetGUID(); + } + }return; + case SELECTSPELL_INTERRUPT: + { + CreatureSpellBookInfo *spellInfo = &spellCasting->spellBook->info[CREATURE_SPELL_INTERRUPT_1]; + if( spellInfo->id && !i_creature.HasSpellCooldown(spellInfo->id) && AICheckTakePower(CREATURE_SPELL_INTERRUPT_1) ) + { + spellCasting->spellIndex = CREATURE_SPELL_INTERRUPT_1; + chosenSpellId = spellInfo->id; + SetSpellMaxRange(spellCasting); + spellCasting->targetGUID = enemyTarget->GetGUID(); + } + }return; + } + if( friendTarget ) + { + if( friendTarget->isAlive() ) + { + //heal Friend + spellIndexFound.clear(); + for(uint8 i = CREATURE_SPELL_HEALING_1; i < CREATURE_SPELL_HEALING_MAX; i++) + { + CreatureSpellBookInfo *spellInfo = &spellCasting->spellBook->info[i]; + if( spellInfo->id && !i_creature.HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) + spellIndexFound.push_front(i); + } + if( spellIndexFound.size() > 0 && Unit::getHpPercent(friendTarget) < 70) + { + chosenSpellId = 0; + std::list::iterator itr = spellIndexFound.begin(); + for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; + spellCasting->spellIndex = (*itr); + chosenSpellId = spellCasting->spellBook->info[(*itr)].id; + //DoDoSay("Healing(Friend): %u",chosenSpellId); + spellCasting->targetGUID = friendTarget->GetGUID(); + SetSpellMaxRange(spellCasting); + if(!dynamicTarget) + AIMotionTarget(friendTarget,(rand_norm()*spellCasting->range)+1.0f); + return; + + } + + //Buff Friend + spellIndexFound.clear(); + for(uint8 i = CREATURE_SPELL_BUFF_1; i < CREATURE_SPELL_BUFF_MAX; i++) + { + CreatureSpellBookInfo *spellInfo = &spellCasting->spellBook->info[i]; + if( spellInfo->id && !i_creature.HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) + spellIndexFound.push_front(i); + } + if( spellIndexFound.size() > 0 ) + { + srand(getMSTime()); + chosenSpellId = 0; + std::list::iterator itr = spellIndexFound.begin(); + for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; + spellCasting->spellIndex = (*itr); + chosenSpellId = spellCasting->spellBook->info[(*itr)].id; + + //DoDoSay("Buff Friend: %u",chosenSpellId); + spellCasting->targetGUID = friendTarget->GetGUID(); + SetSpellMaxRange(spellCasting); + if(!dynamicTarget) + AIMotionTarget(friendTarget,(rand_norm()*spellCasting->range)+1.0f); + return; + } + } + } + else if(enemyTarget) + { + if(!dynamicTarget) + { + float distance = sqrt(i_creature.GetDistance2dSq(enemyTarget)); + + if( !spellCasting->oldTargetRange ) + spellCasting->oldTargetRange = distance; + + // Target Is Casting a Channeled Spell + if( enemyTarget->HasSpellChannelActiveTimer() ) + { + //Cast Intterupted Spell if we have available + } + // Target Is Running Away, try speed-up to get to her. + // Far from what i wan... Need to correct that situation + + + //Potentiel Charge, but for sure Target is comming to Us + // Far from what i wan... Need to correct that situation + if( distance < spellCasting->oldTargetRange /*&& !enemyTarget->IsStopped()*/) + { + spellCasting->oldTargetRange = distance; + + spellIndexFound.clear(); + for(uint8 i = CREATURE_SPELL_STUNT_1; i < CREATURE_SPELL_STUNT_MAX; i++) + { + CreatureSpellBookInfo *spellInfo = &spellCasting->spellBook->info[i]; + if( spellInfo->id && !i_creature.HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) + spellIndexFound.push_front(i); + } + if( spellIndexFound.size() > 0 ) + { + srand(getMSTime()); + chosenSpellId = 0; + std::list::iterator itr = spellIndexFound.begin(); + for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; + spellCasting->spellIndex = (*itr); + chosenSpellId = spellCasting->spellBook->info[(*itr)].id; + + //DoDoSay("Stun: %u",chosenSpellId); + SetSpellMaxRange(spellCasting); + if( !(spellCasting->spellBook->info[chosenSpellId].targets & (1 << (SELECT_TARGET_SELF-1))) ) + spellCasting->targetGUID = i_creature.GetGUID(); + else + { + AIMotionTarget(enemyTarget,(rand_norm()*spellCasting->range)+1.0f); + spellCasting->targetGUID = enemyTarget->GetGUID(); + } + //spellCasting->spellBook->castingTimer = spellCasting->spellBook->info[(*itr)].castingTime; + spellCasting->targetGUID = enemyTarget->GetGUID(); + return; + } + } + } + //Healing + if( Unit::getHpPercent(&i_creature) < irand(40,90) ) + { + spellIndexFound.clear(); + for(uint8 i = CREATURE_SPELL_HEALING_1; i < CREATURE_SPELL_HEALING_MAX; i++) + { + CreatureSpellBookInfo *spellInfo = &spellCasting->spellBook->info[i]; + if( spellInfo->id && !i_creature.HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) + spellIndexFound.push_front(i); + } + if( spellIndexFound.size() > 0 ) + { + srand(getMSTime()); + chosenSpellId = 0; + std::list::iterator itr = spellIndexFound.begin(); + for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; + spellCasting->spellIndex = (*itr); + chosenSpellId = spellCasting->spellBook->info[(*itr)].id; + //DoDoSay("Healing: %u",chosenSpellId); + //spellCasting->spellBook->castingTimer = spellCasting->spellBook->info[(*itr)].castingTime; + spellCasting->targetGUID = i_creature.GetGUID(); + SetSpellMaxRange(spellCasting); + return; + } + + } + else //Buff + { + spellIndexFound.clear(); + for(uint8 i = CREATURE_SPELL_BUFF_1; i < CREATURE_SPELL_BUFF_MAX; i++) + { + CreatureSpellBookInfo *spellInfo = &spellCasting->spellBook->info[i]; + if( spellInfo->id && !i_creature.HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) + spellIndexFound.push_front(i); + } + if( spellIndexFound.size() > 0 ) + { + srand(getMSTime()); + chosenSpellId = 0; + std::list::iterator itr = spellIndexFound.begin(); + for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; + spellCasting->spellIndex = (*itr); + chosenSpellId = spellCasting->spellBook->info[(*itr)].id; + //DoDoSay("Buff: %u",chosenSpellId); + //spellCasting->spellBook->castingTimer = spellCasting->spellBook->info[(*itr)].castingTime; + spellCasting->targetGUID = i_creature.GetGUID(); + SetSpellMaxRange(spellCasting); + return; + } + } + + //Attack Spell + spellIndexFound.clear(); + for(uint8 i = CREATURE_SPELL_ATTACK_1; i < CREATURE_SPELL_ATTACK_MAX; i++) + { + CreatureSpellBookInfo *spellInfo = &spellCasting->spellBook->info[i]; + if( spellInfo->id && !i_creature.HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) + spellIndexFound.push_front(i); + } + if( spellIndexFound.size() ) + { + srand(getMSTime()); + chosenSpellId = 0; + std::list::iterator itr = spellIndexFound.begin(); + for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; + spellCasting->spellIndex = (*itr); + chosenSpellId = spellCasting->spellBook->info[(*itr)].id; + + //DoDoSay("Attack: %u",chosenSpellId); + spellCasting->targetGUID = enemyTarget->GetGUID(); + SetSpellMaxRange(spellCasting); + if(!dynamicTarget) + AIMotionTarget(enemyTarget,spellCasting->range/1.1f); + return; + } + + if(!dynamicTarget && i_creature.IsStopped()) //Debug, got to be removed... + AIMotionTarget(enemyTarget,1.5f); + } +} + +void AggressorCasterAI::AICast(CreatureSpellCasting *spellCasting) +{ + Unit *target = i_creature.GetUnit((i_creature),spellCasting->targetGUID); + + if( !target || !AICheckTakePower(spellCasting->spellIndex, true) ) + { + spellCasting->reset(); + return; + } + i_creature.CastSpell(target, spellCasting->spellBook->info[spellCasting->spellIndex].id, false); + spellCasting->started = true; +} +void AggressorCasterAI::AIMotionTarget(Unit* target, float offSet) +{ + bool exist = false; + MotionMaster* creatureMM = i_creature.GetMotionMaster(); + + if(!target /*&& creatureMM->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE*/) + { + //creatureMM->MovementExpired(); + return; + } + + float angle = NULL; + if( !i_creature.IsHostileTo(target) ) + { + angle = target->GetOrientation()*2.0f; + } + + if(creatureMM->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE) + { + TargetedMovementGenerator* targetMV = static_cast*> (creatureMM->top()); + if( targetMV && targetMV->GetTarget() ) + { + if( targetMV->GetTarget()->GetGUID() != target->GetGUID() ) + { + creatureMM->MovementExpired(false); + creatureMM->Mutate(new TargetedMovementGenerator(*target,offSet,angle)); + } + else if(targetMV->GetOffSet() != offSet) + targetMV->SetOffSet(offSet); + } + } + else + { + i_creature.GetMotionMaster()->Mutate(new TargetedMovementGenerator(*target,offSet,angle)); + } +} + +bool AggressorCasterAI::AICheckFriendTarget() +{ + //Probaly make that function Usable only into Update(), save The status, cause into MoveInLineOfSight(), will be check too much time for nothing. + //friend target not found so we reset Status of Friend Follow. + if( !i_creature.getVictim() && i_creature.GetUInt64Value(UNIT_FIELD_TARGET) ) + { + Unit* friendTarget = i_creature.GetUnit( i_creature, i_creature.GetUInt64Value(UNIT_FIELD_TARGET) ); + if( friendTarget && friendTarget->isAlive() && friendTarget->isInCombat() && !i_creature.IsHostileTo(friendTarget) && i_creature.IsWithinDistInMap(friendTarget,35.0f)) + return true; + else + { + if(friendTarget) + friendTarget->m_hasHealer = false; + i_creature.SetUInt64Value(UNIT_FIELD_TARGET,0); + AIMotionTarget(NULL,NULL); + } + } + return false; +} +bool AggressorCasterAI::AICheckVictimTarget() +{ + //Check if we have a current target + if( i_creature.getVictim() ) + { + //debug Line to help find why sometime MV target is not Creature Target + if( i_creature.GetUInt64Value(UNIT_FIELD_TARGET) != i_creature.getVictim()->GetGUID() ) + i_creature.SetUInt64Value(UNIT_FIELD_TARGET, i_creature.getVictim()->GetGUID()); + + if( i_creature.getVictim()->isTargetableForAttack() && i_creature.isAlive()) + return true; + } + return false; +} +bool AggressorCasterAI::AICheckTakePower(uint8 spellIndex, bool take) +{ + uint32 currentPowerValue; + // Don't take SpellAddon PowerType, Because can be missing, so is up to us + // to not put spell for mage on a warrior. + Powers creaturePowerType = i_creature.getPowerType(); + switch( creaturePowerType ) + { + case POWER_RAGE: currentPowerValue = i_creature.GetPower(POWER_RAGE)/10; break; + case POWER_MANA: currentPowerValue = i_creature.GetPower(POWER_MANA); break; + case POWER_FOCUS: currentPowerValue = i_creature.GetPower(POWER_FOCUS); break; + case POWER_ENERGY: currentPowerValue = i_creature.GetPower(POWER_ENERGY); break; + case POWER_HAPPINESS: currentPowerValue = i_creature.GetPower(POWER_HAPPINESS); break; + } + if( i_creature.m_creatureSpellBook->info[spellIndex].cost > currentPowerValue ) + return false; + + if( take && !i_creature.m_creatureSpellBook->info[spellIndex].usingDBC ) + switch(POWER_MANA) //We Only use this one for The Moment + { + case POWER_RAGE: + i_creature.SetPower(creaturePowerType, (currentPowerValue - i_creature.m_creatureSpellBook->info[spellIndex].cost)*10 );break; + case POWER_MANA: + i_creature.SetPower(creaturePowerType, (currentPowerValue - i_creature.m_creatureSpellBook->info[spellIndex].cost) );break; + case POWER_FOCUS: + i_creature.SetPower(creaturePowerType, (currentPowerValue - i_creature.m_creatureSpellBook->info[spellIndex].cost) );break; + case POWER_ENERGY: + i_creature.SetPower(creaturePowerType, (currentPowerValue - i_creature.m_creatureSpellBook->info[spellIndex].cost) );break; + case POWER_HAPPINESS: + i_creature.SetPower(creaturePowerType, (currentPowerValue - i_creature.m_creatureSpellBook->info[spellIndex].cost) );break; + } + + return true; +} + + +float AggressorCasterAI::SetSpellMaxRange(CreatureSpellCasting *spellCasting) +{ + // Not OK ... + float _rangeMax = spellCasting->spellBook->info[spellCasting->spellIndex].range[1] - 2.0f; + spellCasting->range = _rangeMax < 5.5f ? 5.5f : _rangeMax; + return spellCasting->range; +} Index: src/game/AggressorCasterAI.h =================================================================== --- src/game/AggressorCasterAI.h (revision 0) +++ src/game/AggressorCasterAI.h (revision 0) @@ -0,0 +1,104 @@ +/* + * 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 + */ + +#ifndef MANGOS_AGGRESSORCASTERAI_H +#define MANGOS_AGGRESSORCASTERAI_H + +#include "Creature.h" +#include "CreatureAI.h" +#include "Timer.h" + +enum SelectSpellType +{ + SELECTSPELL_NONE, + SELECTSPELL_SPEED, + SELECTSPELL_INTERRUPT +}; + +//Will be used like this: CREATURE_GLOBAL_COOLDOWN-(Level*1000) +#define CREATURE_SPELL_GLOBAL_COOLDOWN 1500 +#define CREATURE_WAIT_REACH_RATE 1 + +class Creature; + +struct CreatureSpellCasting +{ + CreatureSpellCasting(CreatureSpellBook *_spellBook) : + spellIndex(-1), targetGUID(0), runningTimer(0), oldTargetRange(0.0f),range(0.0f), + started(false),reached(false),dynamicTarget(false),spellBook(_spellBook) {} + + int8 spellIndex; + uint64 targetGUID; + uint32 runningTimer; + float oldTargetRange, range; + bool started, reached, dynamicTarget; + CreatureSpellBook* spellBook; + + void reset(bool resetRange = false) + { + spellIndex = -1; + targetGUID = 0; + runningTimer = 0; + range = 0.0f; + started = false; + reached = false; + dynamicTarget = false; + if(resetRange) + oldTargetRange = 0.0f; + } +}; +class MANGOS_DLL_DECL AggressorCasterAI : public CreatureAI +{ + enum AggressorState + { + STATE_NORMAL = 1, + STATE_LOOK_AT_VICTIM = 2 + }; + + public: + + AggressorCasterAI(Creature &c); + + void MoveInLineOfSight(Unit *); + void AttackStart(Unit *); + void EnterEvadeMode(); + bool IsVisible(Unit *) const; + void DamageTaken(Unit *done_by, uint32 &damage); + void JustDie(); + void UpdateAI(const uint32); + static int Permissible(const Creature *); + + private: + void AISelectAction(Unit* who); + void AISelectSpell(CreatureSpellCasting *spellCasting, Unit *target = NULL, uint8 specialCase = 0); + void AICast(CreatureSpellCasting *spellCasting); + void AIMotionTarget(Unit *target, float offSet = 0.0f); + bool AICheckFriendTarget(); + bool AICheckVictimTarget(); + bool AICheckTakePower(uint8 spellIndex, bool take = false); + float SetSpellMaxRange(CreatureSpellCasting *spellCasting); + + CreatureSpellCasting *spellCasting; + Creature &i_creature; + uint32 lastAction; + uint64 i_victimGuid; + float selectedAttackRadius; + AggressorState i_state; + TimeTracker i_tracker; +}; +#endif Index: src/game/ConfusedMovementGenerator.cpp =================================================================== --- src/game/ConfusedMovementGenerator.cpp (revision 4763) +++ src/game/ConfusedMovementGenerator.cpp (working copy) @@ -22,9 +22,7 @@ #include "ConfusedMovementGenerator.h" #include "DestinationHolderImp.h" -template -void -ConfusedMovementGenerator::Initialize(T &unit) +template void ConfusedMovementGenerator::Initialize(T &unit) { const float wander_distance=11; float x,y,z; @@ -62,30 +60,22 @@ unit.UpdateGroundPositionZ(i_waypoints[idx][0],i_waypoints[idx][1],z); i_waypoints[idx][2] = z; } - - unit.StopMoving(); unit.setMoveRunFlag(true); } -template<> -void -ConfusedMovementGenerator::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok) +template<> void ConfusedMovementGenerator::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok) { is_water_ok = creature.isCanSwimOrFly(); is_land_ok = creature.isCanWalkOrFly(); } -template<> -void -ConfusedMovementGenerator::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok) +template<> void ConfusedMovementGenerator::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok) { is_water_ok = true; is_land_ok = true; } -template -void -ConfusedMovementGenerator::Reset(T &unit) +template void ConfusedMovementGenerator::Reset(T &unit) { i_nextMove = 1; i_nextMoveTime.Reset(0); @@ -93,15 +83,17 @@ unit.StopMoving(); } -template -bool -ConfusedMovementGenerator::Update(T &unit, const uint32 &diff) +template bool ConfusedMovementGenerator::Update(T &unit, const uint32 &diff) { if(!&unit) - return true; - if(unit.hasUnitState(UNIT_STAT_ROOT) || unit.hasUnitState(UNIT_STAT_STUNDED)) - return true; - + return false; + //if(unit.hasUnitState(UNIT_STAT_ROOT) || unit.hasUnitState(UNIT_STAT_STUNDED)) + //return true; + if(!unit.HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED)) + { + unit.clearUnitState(UNIT_STAT_CONFUSED); + return false; + } if( i_nextMoveTime.Passed() ) { // currently moving, update location @@ -110,9 +102,7 @@ { if( i_destinationHolder.HasArrived()) { - // arrived, stop and wait a bit - unit.StopMoving(); - + unit.clearUnitState(UNIT_STAT_CONFUSED); i_nextMove = urand(1,MAX_CONF_WAYPOINTS); i_nextMoveTime.Reset(urand(0, 1500-1)); // TODO: check the minimum reset time, should be probably higher } @@ -129,6 +119,7 @@ const float x = i_waypoints[i_nextMove][0]; const float y = i_waypoints[i_nextMove][1]; const float z = i_waypoints[i_nextMove][2]; + unit.addUnitState(UNIT_STAT_CONFUSED); Traveller traveller(unit); i_destinationHolder.SetDestination(traveller, x, y, z); } Index: src/game/ConfusedMovementGenerator.h =================================================================== --- src/game/ConfusedMovementGenerator.h (revision 4763) +++ src/game/ConfusedMovementGenerator.h (working copy) @@ -16,8 +16,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MANGOS_RANDOMMOTIONGENERATOR_H -#define MANGOS_RANDOMMOTIONGENERATOR_H +#ifndef MANGOS_CONFUSEDMOTIONGENERATOR_H +#define MANGOS_CONFUSEDMOTIONGENERATOR_H #include "MovementGenerator.h" #include "DestinationHolder.h" @@ -35,7 +35,10 @@ void Initialize(T &); void Reset(T &); bool Update(T &, const uint32 &); - + void UpdateMapPosition(float &x ,float &y, float &z) + { + i_destinationHolder.GetLocationNow(x,y,z); + } MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; } private: void _InitSpecific(T &, bool &, bool &); Index: src/game/Creature.cpp =================================================================== --- src/game/Creature.cpp (revision 4763) +++ src/game/Creature.cpp (working copy) @@ -37,6 +37,7 @@ #include "WaypointMovementGenerator.h" #include "InstanceData.h" #include "BattleGround.h" +#include "spell.h" // apply implementation of the singletons #include "Policies/SingletonImp.h" @@ -56,11 +57,10 @@ for(int i =0; i<4; ++i) m_spells[i] = 0; - m_CreatureSpellCooldowns.clear(); m_CreatureCategoryCooldowns.clear(); m_GlobalCooldown = 0; - - m_AlreadyCallAssistence = false; + m_creatureSpellBook = NULL; + m_AssistenceStatus = new AssistenceStatus(); } Creature::~Creature() @@ -174,6 +174,7 @@ { case JUST_DIED: // Dont must be called, see Creature::setDeathState JUST_DIED -> CORPSE promoting. + getHostilRefManager().deleteReferences(); sLog.outError("Creature (GUIDLow: %u Entry: %u ) in wrong state: JUST_DEAD (1)",GetGUIDLow(),GetEntry()); break; case DEAD: @@ -204,6 +205,9 @@ if( m_deathTimer <= diff ) { RemoveCorpse(); + float x,y,z; + GetRespawnCoord(x, y, z); + Relocate(x,y,z); //Check this out Use World Relocation if still cool, or GridRelocation and here is probaly Grid needed so..., GS DEBUG_LOG("Removing corpse... %u ", GetUInt32Value(OBJECT_FIELD_ENTRY)); } else @@ -1244,7 +1248,7 @@ // checked at loading m_defaultMovementType = MovementGeneratorType(cinfo->MovementType); - + m_creatureSpellBook = objmgr.GetCreatureSpellBook(Entry); //Notify the map's instance data. //Only works if you create the object in it, not if it is moves to that map. //Normally non-players do not teleport to other maps. @@ -1694,14 +1698,13 @@ return false; } -void Creature::CallAssistence() +/*void Creature::CallAssistence() { if( !m_AlreadyCallAssistence && getVictim() ) { - CastSpell(this,SPELL_ID_AGGRO, true, NULL, NULL, getVictim()->GetGUID()); + //CastSpell(this,SPELL_ID_AGGRO, true, NULL, NULL, getVictim()->GetGUID()); } -} - +}*/ void Creature::SaveRespawnTime() { if(isPet()) @@ -1732,7 +1735,10 @@ return false; float rx,ry,rz; - GetRespawnCoord(rx, ry, rz); + if(isPet() && GetOwner() ) + GetOwner()->GetPosition(rx,ry,rz); + else + GetRespawnCoord(rx, ry, rz); float length = pVictim->GetDistanceSq(rx,ry,rz); return ( length > 10000.0f); // real value unknown @@ -1813,14 +1819,39 @@ if(!spellInfo) return; - uint32 cooldown = spellInfo->CategoryRecoveryTime > spellInfo->RecoveryTime ? spellInfo->CategoryRecoveryTime : spellInfo->RecoveryTime; + uint32 cooldown = 0; + + if(m_creatureSpellBook) + { + //When Struct SpellCasting Is available into Core, Use Index comming from there + //So no use to search the spell we are casting ;), GS + CreatureSpellBookInfoMap::const_iterator _info = m_creatureSpellBook->info.begin(); + for(;_info != m_creatureSpellBook->info.end(); ++_info) + { + if( _info->second.id == spellid ) + { + cooldown = _info->second.recoveryTime; + break; + } + } + } + + if(!cooldown) + { + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); + if(!spellInfo) + return; + + cooldown = spellInfo->CategoryRecoveryTime > spellInfo->RecoveryTime ? spellInfo->CategoryRecoveryTime : spellInfo->RecoveryTime; + } + if(cooldown) _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/1000); if(spellInfo->Category) _AddCreatureCategoryCooldown(spellInfo->Category, time(NULL)); - m_GlobalCooldown = 1500; + m_GlobalCooldown = 1500; // We Should Add A Rate, so we Control a little, Strenght Of Spell Casting } bool Creature::HasCategoryCooldown(uint32 spell_id) const @@ -1833,10 +1864,13 @@ return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / 1000)) > time(NULL)); } -bool Creature::HasSpellCooldown(uint32 spell_id) const +bool Creature::HasSpellCooldown(uint32 spell_id,uint32 addTime) const { + if(!spell_id) + return m_GlobalCooldown > 0; + CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spell_id); - return (itr != m_CreatureSpellCooldowns.end() && itr->second > time(NULL)) || m_GlobalCooldown > 0 || HasCategoryCooldown(spell_id); + return (itr != m_CreatureSpellCooldowns.end() && itr->second > time(NULL)+addTime) || m_GlobalCooldown > 0 || HasCategoryCooldown(spell_id); } bool Creature::IsInEvadeMode() const @@ -1852,3 +1886,159 @@ break; return i < CREATURE_MAX_SPELLS; //broke before end of iteration of known spells } + +/*void Creature::LoadSpellInfo() +{ + if(m_creatureSpellBook) + delete m_creatureSpellBook; + + QueryResult* _result = sDatabase.PQuery("SELECT * FROM creature_spellbook WHERE entry=%u",GetEntry()); + if(_result) + { + Field *_spellInfo = _result->Fetch(); + char* _spellId = NULL; + + m_creatureSpellBook = new CreatureSpellBook(); + //spellId, recoveryTime, unknow1, buffDuration, cost, rangeMin, rangeMax + for(uint8 itr = CREATURE_SPELL_MIN; itr < CREATURE_SPELL_MAX; ++itr) + { + const char* rawSpellInfo = _spellInfo[itr+1].GetString(); + _spellId = strtok((char*)rawSpellInfo, " "); + if( _spellId ) + { + uint32 spellId = m_creatureSpellBook->info[itr].id = atoi(_spellId); + + char* _recoveryTime = strtok(NULL, " "); + if(_recoveryTime) + m_creatureSpellBook->info[itr].recoveryTime = atoi(_recoveryTime); + char* _castingTime = strtok(NULL, " "); + if(_castingTime) + m_creatureSpellBook->info[itr].unknow1 = atoi(_castingTime); + char* _buffDuration = strtok(NULL, " "); + if(_buffDuration) + m_creatureSpellBook->info[itr].buffDuration = atoi(_buffDuration); + char* _cost = strtok(NULL, " "); + if(_cost) + m_creatureSpellBook->info[itr].cost = atoi(_cost); + char* _rangeMin = strtok(NULL, " "); + if(_rangeMin) + m_creatureSpellBook->info[itr].range[0] = atof(_rangeMin); + char* _rangeMax = strtok(NULL, " "); + if(_rangeMax) + m_creatureSpellBook->info[itr].range[1] = atof(_rangeMax); + } + } + delete _result; + + SpellEntry const* dbcSpell; + SpellRangeEntry const *dbcRange; + SpellDurationEntry const * dbcDuration; + SpellCastTimesEntry const *dbcCastTime; + + CreatureSpellInfoMap::iterator itr = m_creatureSpellBook->info.begin(); + if(itr == m_creatureSpellBook->info.end()) + return; + + for(; itr != m_creatureSpellBook->info.end(); ++itr) + { + dbcSpell = GetSpellStore()->LookupEntry(itr->second.id); + if( !dbcSpell ) + continue; + dbcRange = GetSpellRangeStore()->LookupEntry(dbcSpell->rangeIndex); + dbcDuration = GetSpellDurationStore()->LookupEntry(dbcSpell->DurationIndex); + dbcCastTime = GetCastTimesStore()->LookupEntry(dbcSpell->CastingTimeIndex); + + for (int j=0; j<3; j++) + { + //Spell targets self + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_SELF ) + itr->second.targets |= 1 << (SELECT_TARGET_SELF-1); + + //Spell targets a single enemy + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE || + dbcSpell->EffectImplicitTargetA[j] == TARGET_CURRENT_SELECTED_ENEMY ) + itr->second.targets |= 1 << (SELECT_TARGET_SINGLE_ENEMY-1); + + //Spell targets AoE at enemy + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA || + dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || + dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED ) + itr->second.targets |= 1 << (SELECT_TARGET_AOE_ENEMY-1); + + //Spell targets an enemy + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE || + dbcSpell->EffectImplicitTargetA[j] == TARGET_CURRENT_SELECTED_ENEMY || + dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA || + dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || + dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED ) + itr->second.targets |= 1 << (SELECT_TARGET_ANY_ENEMY-1); + + //Spell targets a single friend(or self) + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_SELF || + dbcSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_FRIEND || + dbcSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_PARTY ) + itr->second.targets |= 1 << (SELECT_TARGET_SINGLE_FRIEND-1); + + //Spell targets aoe friends + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER || + dbcSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY ) + itr->second.targets |= 1 << (SELECT_TARGET_AOE_FRIEND-1); + + //Spell targets any friend(or self) + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_SELF || + dbcSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_FRIEND || + dbcSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_PARTY || + dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER || + dbcSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY ) + itr->second.targets |= 1 << (SELECT_TARGET_ANY_FRIEND-1); + + //Make sure that this spell includes a damage effect + if ( dbcSpell->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE || + dbcSpell->Effect[j] == SPELL_EFFECT_INSTAKILL || + dbcSpell->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE || + dbcSpell->Effect[j] == SPELL_EFFECT_HEALTH_LEECH ) + itr->second.effects |= 1 << (SELECT_EFFECT_DAMAGE-1); + + //Make sure that this spell includes a healing effect + if ( dbcSpell->Effect[j] == SPELL_EFFECT_HEAL || + dbcSpell->Effect[j] == SPELL_EFFECT_HEAL_MAX_HEALTH || + dbcSpell->Effect[j] == SPELL_EFFECT_HEAL_MECHANICAL ) + itr->second.effects |= 1 << (SELECT_EFFECT_HEALING-1); + + //Make sure that this spell applies an aura + if( dbcSpell->Effect[j] == SPELL_EFFECT_APPLY_AURA ) + itr->second.effects |= 1 << (SELECT_EFFECT_AURA-1); + + // Buff Duration + if( dbcDuration && itr->second.buffDuration < dbcDuration->Duration[j] )//DBC Info Not Good + itr->second.buffDuration = dbcDuration->Duration[j]; + } + + //We use powerCost from SQL on missing DBC value + if( dbcSpell->manaCost ) + { + itr->second.usingDBC = true; + itr->second.cost = dbcSpell->manaCost; + } + + //CoolDown + if( !itr->second.recoveryTime && dbcSpell->RecoveryTime ) + itr->second.recoveryTime = dbcSpell->RecoveryTime; + + if(dbcRange) + { + //Range Min + if( !itr->second.range[0] ) + itr->second.range[0] = dbcRange->minRange; + //Range Max + if( !itr->second.range[1] )//DBC Info Not Good + itr->second.range[1] = dbcRange->maxRange; + } + + //Casttime + if(dbcCastTime && dbcCastTime->CastTime) + itr->second.isDelayed = true; + } + } +} +*/ Index: src/game/Creature.h =================================================================== --- src/game/Creature.h (revision 4763) +++ src/game/Creature.h (working copy) @@ -269,6 +269,124 @@ uint32 modelid_other_gender; }; +enum CreatureSpellSelectTarget +{ + SELECT_TARGET_DONTCARE = 0, //All target types allowed + + SELECT_TARGET_SELF, //Only Self casting + + SELECT_TARGET_SINGLE_ENEMY, //Only Single Enemy + SELECT_TARGET_AOE_ENEMY, //Only AoE Enemy + SELECT_TARGET_ANY_ENEMY, //AoE or Single Enemy + + SELECT_TARGET_SINGLE_FRIEND, //Only Single Friend + SELECT_TARGET_AOE_FRIEND, //Only AoE Friend + SELECT_TARGET_ANY_FRIEND, //AoE or Single Friend +}; + +enum CreatureSpellSelectEffect +{ + SELECT_EFFECT_DONTCARE = 0, //All spell effects allowed + SELECT_EFFECT_DAMAGE, //Spell does damage + SELECT_EFFECT_HEALING, //Spell does healing + SELECT_EFFECT_AURA, //Spell applies an aura +}; +enum CreatureSpellIndex +{ + CREATURE_SPELL_MIN, + CREATURE_SPELL_ATTACK_1 = 0, //Normal Attack + CREATURE_SPELL_ATTACK_2, + CREATURE_SPELL_ATTACK_3, + CREATURE_SPELL_ATTACK_4, + CREATURE_SPELL_ATTACK_5, + CREATURE_SPELL_ATTACK_MAX, + CREATURE_SPELL_SPEED_1 = 5, //When target Run Away + CREATURE_SPELL_SPEED_2, + CREATURE_SPELL_SPEED_MAX, + CREATURE_SPELL_STUNT_1 = 7, + CREATURE_SPELL_STUNT_2, //When target Run Closer + CREATURE_SPELL_STUNT_MAX, + CREATURE_SPELL_HEALING_1 = 9, //When Less Life Or Friend Target + CREATURE_SPELL_HEALING_2, + CREATURE_SPELL_HEALING_MAX, + CREATURE_SPELL_BUFF_1 = 11, //When Begin of the fight Or Friend Target + CREATURE_SPELL_BUFF_2, + CREATURE_SPELL_BUFF_MAX, + CREATURE_SPELL_INTERRUPT_1 = 13, //Select When Target is Casting Channeling Spell + CREATURE_SPELL_INTERRUPT_MAX, + CREATURE_SPELL_MAX = 14 +}; +struct CreatureSpellBookInfo +{ + CreatureSpellBookInfo() : id(NULL), recoveryTime(0), + unknow1(0),buffDuration(0),cost(0), + targets(0),effects(0),usingDBC(false),hasCastTime(false) + { + range[0] = 0.0f; + range[1] = 0.0f; + } + + uint8 targets,effects; + uint32 id,recoveryTime,unknow1,buffDuration,cost; + float range[2]; + bool usingDBC,hasCastTime; +}; +typedef std::map CreatureSpellBookInfoMap; +struct CreatureSpellBook +{ + CreatureSpellBook() { info.clear(); } + CreatureSpellBookInfoMap info; + + bool hasHealing() + { + CreatureSpellBookInfoMap::iterator spellItr; + for(uint8 i = CREATURE_SPELL_HEALING_1; i < CREATURE_SPELL_HEALING_MAX; ++i) + { + spellItr = info.find(i); + if( spellItr != info.end() && spellItr->second.id) + return true; + } + return false; + } + bool hasSpeed() + { + CreatureSpellBookInfoMap::iterator spellItr; + for(uint8 i = CREATURE_SPELL_SPEED_1; i < CREATURE_SPELL_SPEED_MAX; ++i) + { + spellItr = info.find(i); + if( spellItr != info.end() && spellItr->second.id) + return true; + } + return false; + } + bool hasInterrupt() + { + CreatureSpellBookInfoMap::iterator spellItr; + for(uint8 i = CREATURE_SPELL_INTERRUPT_1; i < CREATURE_SPELL_INTERRUPT_MAX; ++i) + { + spellItr = info.find(i); + if( spellItr != info.end() && spellItr->second.id) + return true; + } + return false; + } +}; + + + + +struct AssistenceStatus +{ + AssistenceStatus() : requested(false), obtained(false) {} + bool requested; + bool obtained; + + void reset() + { + obtained = false; + requested = false; + } +}; enum InhabitTypeValues { INHAVIT_GROUND = 1, @@ -364,7 +482,7 @@ void _AddCreatureSpellCooldown(uint32 spell_id, time_t end_time); void _AddCreatureCategoryCooldown(uint32 category, time_t apply_time); void AddCreatureSpellCooldown(uint32 spellid); - bool HasSpellCooldown(uint32 spell_id) const; + bool HasSpellCooldown(uint32 spell_id = 0, uint32 addTime = 0) const; bool HasCategoryCooldown(uint32 spell_id) const; bool HasSpell(uint32 spellID) const; @@ -465,15 +583,16 @@ SpellEntry const *reachWithSpellCure(Unit *pVictim); uint32 m_spells[CREATURE_MAX_SPELLS]; + uint32 m_GlobalCooldown; CreatureSpellCooldowns m_CreatureSpellCooldowns; CreatureSpellCooldowns m_CreatureCategoryCooldowns; - uint32 m_GlobalCooldown; float GetAttackDistance(Unit *pl) const; - void CallAssistence(); - void SetNoCallAssistence(bool val) { m_AlreadyCallAssistence = val; } - + //void CallAssistence(); + //void SetCallAssistence( bool value ) { m_AlreadyCallAssistence = value; } + //bool GetCallAssistence() const { return m_AlreadyCallAssistence; } + AssistenceStatus* m_AssistenceStatus; MovementGeneratorType GetDefaultMovementType() const { return m_defaultMovementType; } void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; } @@ -507,6 +626,7 @@ else return m_charmInfo->GetCharmSpell(pos)->spellId; } + CreatureSpellBook *m_creatureSpellBook; protected: // vendor items @@ -548,7 +668,7 @@ uint32 m_regenTimer; MovementGeneratorType m_defaultMovementType; Cell m_currentCell; // store current cell where creature listed - bool m_AlreadyCallAssistence; + //bool m_AlreadyCallAssistence; uint32 m_DBTableGuid; bool m_regenHealth; Index: src/game/CreatureAI.h =================================================================== --- src/game/CreatureAI.h (revision 4763) +++ src/game/CreatureAI.h (working copy) @@ -32,6 +32,21 @@ #define TIME_INTERVAL_LOOK 5000 #define VISIBILITY_RANGE 10000 +//Creature Follow Range on aggro +#define AI_MANGOS 1 //To add Featured or Normal Version +#define FOLLOW_RANGE_CASTER 9.0f +#define FOLLOW_RANGE_MELEE 0.0f +#define RUNAWAY_AGGRO_RANGE 3.3f // +Aggro == start RunAway at that range +#define RANGE_AUTO_ASSIST 2.0f +#define RANGE_AUTO_ASSIST_PET 20.0f +#define RANGE_AUTO_ASSIST_PLAYER 15.0f +#define RANGE_AUTO_ASSIST_NPC 0.5f +#define RANGE_DYNAMIC_ASSIST 15.0f +#define RANGE_MAX_AUTO_FEAR 15.0f //If player distance from SpawnPoint > RANGE_MAX_AUTO_FEAR, AUTO fear Off. +#define CREATURE_SPELL_RUNAWAY 8225 +#define CREATURE_SPELL_TAP_FRIEND 38192 +#define CREATURE_WAIT_RATE 15 + class MANGOS_DLL_SPEC CreatureAI { public: Index: src/game/CreatureAIRegistry.cpp =================================================================== --- src/game/CreatureAIRegistry.cpp (revision 4763) +++ src/game/CreatureAIRegistry.cpp (working copy) @@ -20,6 +20,7 @@ #include "NullCreatureAI.h" #include "ReactorAI.h" #include "AggressorAI.h" +#include "AggressorCasterAI.h" #include "GuardAI.h" #include "PetAI.h" #include "TotemAI.h" @@ -37,6 +38,7 @@ { (new CreatureAIFactory("NullAI"))->RegisterSelf(); (new CreatureAIFactory("AggressorAI"))->RegisterSelf(); + (new CreatureAIFactory("AggressorCasterAI"))->RegisterSelf(); (new CreatureAIFactory("ReactorAI"))->RegisterSelf(); (new CreatureAIFactory("GuardAI"))->RegisterSelf(); (new CreatureAIFactory("PetAI"))->RegisterSelf(); Index: src/game/DestinationHolder.h =================================================================== --- src/game/DestinationHolder.h (revision 4763) +++ src/game/DestinationHolder.h (working copy) @@ -35,11 +35,12 @@ uint32 i_timeElapsed; bool i_destSet; float i_fromX, i_fromY, i_fromZ; + uint32 i_fromMapId; float i_destX, i_destY, i_destZ; public: DestinationHolder() : i_tracker(TRAVELLER_UPDATE_INTERVAL), i_totalTravelTime(0), i_timeStarted(0), i_timeElapsed(0), - i_destSet(false), i_fromX(0), i_fromY(0), i_fromZ(0), i_destX(0), i_destY(0), i_destZ(0) {} + i_destSet(false), i_fromX(0), i_fromY(0), i_fromZ(0),i_fromMapId(0), i_destX(0), i_destY(0), i_destZ(0) {} uint32 SetDestination(TRAVELLER &traveller, float dest_x, float dest_y, float dest_z, bool sendMove = true); void GetDestination(float &x, float &y, float &z) { x = i_destX; y = i_destY; z = i_destZ; } @@ -48,13 +49,15 @@ uint32 GetTotalTravelTime(void) const { return i_totalTravelTime; } uint32 GetStartTravelTime(void) const { return i_timeStarted; } bool HasDestination(void) const { return i_destSet; } + float GetDestinationDiff2d(float x, float y, float z) const; float GetDestinationDiff(float x, float y, float z) const; bool HasArrived(void) const { return (i_totalTravelTime == 0 || i_timeElapsed >= i_totalTravelTime); } + uint32 GetTimeToDest(void) const { return i_totalTravelTime - i_timeElapsed; } bool UpdateTraveller(TRAVELLER &traveller, uint32 diff, bool force_update); uint32 StartTravel(TRAVELLER &traveller, bool sendMove = true); - void GetLocationNow(float &x, float &y, float &z) const; + void GetLocationNow(float &x, float &y, float &z, bool is3D = false) const; + float GetDistanceFromDest2dSq(const WorldObject &obj) const; float GetDistanceFromDestSq(const WorldObject &obj) const; - private: void _findOffSetPoint(float x1, float y1, float x2, float y2, float offset, float &x, float &y); Index: src/game/DestinationHolderImp.h =================================================================== --- src/game/DestinationHolderImp.h (revision 4763) +++ src/game/DestinationHolderImp.h (working copy) @@ -25,9 +25,7 @@ #include -template -void -DestinationHolder::_findOffSetPoint(float x1, float y1, float x2, float y2, float offset, float &x, float &y) +template void DestinationHolder::_findOffSetPoint(float x1, float y1, float x2, float y2, float offset, float &x, float &y) { /* given the point (x1, y1) and (x2, y2).. need to find the point (x,y) on the same line * such that the distance from (x, y) to (x2, y2) is offset. @@ -61,12 +59,10 @@ } } -template -uint32 -DestinationHolder::SetDestination(TRAVELLER &traveller, float dest_x, float dest_y, float dest_z, bool sendMove) +template uint32 DestinationHolder::SetDestination(TRAVELLER &traveller, float dest_x, float dest_y, float dest_z, bool sendMove) { - if (i_destSet && i_destX == dest_x && i_destY == dest_y && i_destZ == dest_z) - return 0; + //if (i_destSet && i_destX == dest_x && i_destY == dest_y && i_destZ == dest_z) + //return 0; i_destSet = true; i_destX = dest_x; @@ -76,34 +72,39 @@ return StartTravel(traveller, sendMove); } -template -uint32 -DestinationHolder::StartTravel(TRAVELLER &traveller, bool sendMove) +template uint32 DestinationHolder::StartTravel(TRAVELLER &traveller, bool sendMove) { if(!i_destSet) return 0; i_fromX = traveller.GetPositionX(); i_fromY = traveller.GetPositionY(); i_fromZ = traveller.GetPositionZ(); + i_fromMapId = traveller.GetTraveller().GetMapId(); float dx = i_destX - i_fromX; float dy = i_destY - i_fromY; float dz = i_destZ - i_fromZ; - double dist = ::sqrt((dx*dx) + (dy*dy) + (dz*dz)); - double speed = traveller.Speed(); - if(speed<=0) + + float dist; + //Should be for Creature Flying too and Swimming. + if(traveller.GetTraveller().hasUnitState(UNIT_STAT_IN_FLIGHT)) + dist = sqrt((dx*dx) + (dy*dy) + (dz*dz)); + else //Walking on the ground + dist = sqrt((dx*dx) + (dy*dy)); + float speed = traveller.Speed(); + if(speed<=0.0f) // -0 will be reverse ??? for me at lease, GS speed = 2.5f; speed *= 0.001f; // speed is in seconds so convert from second to millisecond - i_totalTravelTime = static_cast( dist/speed + 0.5 ); + i_totalTravelTime = static_cast( dist/speed); i_timeStarted = getMSTime(); i_timeElapsed = 0; - if(sendMove) traveller.MoveTo(i_destX, i_destY, i_destZ, i_totalTravelTime); + if(sendMove) + traveller.MoveTo(i_destX, i_destY, i_destZ, i_totalTravelTime); + return i_totalTravelTime; } -template -bool -DestinationHolder::UpdateTraveller(TRAVELLER &traveller, uint32 diff, bool force_update) +template bool DestinationHolder::UpdateTraveller(TRAVELLER &traveller, uint32 diff, bool force_update) { i_tracker.Update(diff); i_timeElapsed += diff; @@ -112,9 +113,14 @@ ResetUpdate(); if(!i_destSet) return true; float x,y,z; - GetLocationNow(x, y, z); + + if(traveller.GetTraveller().hasUnitState(UNIT_STAT_IN_FLIGHT)) + GetLocationNow(x, y, z,true); //Should repositione Object with right Coord, so i can bypass some Grid Relocation, GS + else + GetLocationNow(x, y, z); if( x == -431602080 ) return false; + if( traveller.GetTraveller().GetPositionX() != x || traveller.GetTraveller().GetPositionY() != y ) { float ori = traveller.GetTraveller().GetAngle(x, y); @@ -125,9 +131,7 @@ return false; } -template -void -DestinationHolder::GetLocationNow(float &x, float &y, float &z) const +template void DestinationHolder::GetLocationNow(float &x, float &y, float &z, bool is3D) const { if( HasArrived() ) { @@ -135,28 +139,49 @@ y = i_destY; z = i_destZ; } - else + else if(HasDestination()) { double percent_passed = (double)i_timeElapsed / (double)i_totalTravelTime; - x = i_fromX + ((i_destX - i_fromX) * percent_passed); - y = i_fromY + ((i_destY - i_fromY) * percent_passed); - z = i_fromZ + ((i_destZ - i_fromZ) * percent_passed); + const float distanceX = ((i_destX - i_fromX) * percent_passed); + const float distanceY = ((i_destY - i_fromY) * percent_passed); + const float distanceZ = ((i_destZ - i_fromZ) * percent_passed); + x = i_fromX + distanceX; + y = i_fromY + distanceY; + //All that is wrong... Probleme is traveller need to fly or swimg... bad way to make it..., GS + if(is3D) + z = i_fromZ + distanceZ; + else + { + //That part is good... for mob Walking on the floor... But the floor is not allways what we thought :(... + z = MapManager::Instance().GetBaseMap(i_fromMapId)->GetHeight(x,y,i_fromZ); + const float groundDist = sqrt(distanceX*distanceX + distanceY*distanceY); + const float zDist = fabs(i_fromZ - z) + 0.000001f; //FineTune with that value..., GS + const float slope = groundDist / zDist; + if(fabs(slope) < 1.0f) // Or i set the Z to -10000 and ExpireMG when Z == -10000 so at that precise place the SetLocation will see something is wrong. + z = i_destZ; + } } } -template -float -DestinationHolder::GetDistanceFromDestSq(const WorldObject &obj) const +template float DestinationHolder::GetDistanceFromDest2dSq(const WorldObject &obj) const { float x,y,z; obj.GetPosition(x,y,z); - return (i_destX-x)*(i_destX-x)+(i_destY-y)*(i_destY-y)+(i_destZ-z)*(i_destZ-z); + return (i_destX-x)*(i_destX-x) + (i_destY-y)*(i_destY-y); } -template -float -DestinationHolder::GetDestinationDiff(float x, float y, float z) const +template float DestinationHolder::GetDistanceFromDestSq(const WorldObject &obj) const { - return sqrt(((x-i_destX)*(x-i_destX)) + ((y-i_destY)*(y-i_destY)) + ((z-i_destZ)*(z-i_destZ))); + float x,y,z; + obj.GetPosition(x,y,z); + return (i_destX-x)*(i_destX-x) + (i_destY-y)*(i_destY-y) + (i_destZ-z)*(i_destZ-z); } +template float DestinationHolder::GetDestinationDiff2d(float x, float y, float z) const +{ + return sqrt( ((x-i_destX)*(x-i_destX)) + ((y-i_destY)*(y-i_destY)) ); +} +template float DestinationHolder::GetDestinationDiff(float x, float y, float z) const +{ + return sqrt( ((x-i_destX)*(x-i_destX)) + ((y-i_destY)*(y-i_destY)) + ((z-i_destZ)*(z-i_destZ))); +} #endif Index: src/game/FleeingMovementGenerator.cpp =================================================================== --- src/game/FleeingMovementGenerator.cpp (revision 0) +++ src/game/FleeingMovementGenerator.cpp (revision 0) @@ -0,0 +1,164 @@ +/* + * 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 "Creature.h" +#include "MapManager.h" +#include "FleeingMovementGenerator.h" +#include "DestinationHolderImp.h" +#include "ObjectAccessor.h" + +template void FleeingMovementGenerator::_setNextLocation(T &unit) +{ + //no grid search since, i just need a angle... and badly need one if i fear something, GS + Unit* fearWho = ObjectAccessor::Instance().GetUnit(unit, fearWhoGuid); + if(!fearWho) + return; + + float fearMoveAngle = unit.GetAngle(fearWho)+M_PI; + float fearWhoAngle = fearWho->GetOrientation(); + float speed = unit.GetSpeed(MOVE_RUN); + float X = unit.GetPositionX(); + float Y = unit.GetPositionY(); + float Z = unit.GetPositionZ(); + uint32 mapid = unit.GetMapId(); + + //TODO: Play with max range, GS + if(unit.IsWithinDistInMap(fearWho, 28)) + { + float x = X + speed * cos(fearMoveAngle); + float y = Y + speed * sin(fearMoveAngle); + float z; + + if(unit.HasPathTo(x,y)) + { + z = MapManager::Instance().GetBaseMap(mapid)->GetHeight(x,y,Z); + Traveller traveller(unit); + unit.SetOrientation(fearMoveAngle); + i_destinationHolder.SetDestination(traveller, x, y, z); + unit.addUnitState(UNIT_STAT_FLEEING); + } + else + { + Traveller traveller(unit); + unit.SetOrientation(fearMoveAngle); + i_destinationHolder.SetDestination(traveller, X, Y, Z); + unit.clearUnitState(UNIT_STAT_FLEEING); + } + + /*uint8 interlevingSide = 0; + float leftAngle, rightAngle; + leftAngle = rightAngle = 0.0f; + + //That is a path like searching with LOS.... I have another one without LOS... need fine tune and add that, GS. + while( (!unit.IsWithinLOS(x,y,z) ) && interlevingSide < 76 ) //interlevingSide 63 gave me about 90 Degree each Side + { + fearMoveAngle = fearWhoAngle+M_PI; + if ( !(interlevingSide & 1)) //Pair Number + { + leftAngle += 0.05; + if(fearMoveAngle + leftAngle > (M_PI*2)) + fearMoveAngle = fearMoveAngle + leftAngle - (M_PI*2); + else + fearMoveAngle += leftAngle; + ++interlevingSide; + } + else //Impair Number + { + rightAngle -= 0.05; + if( fearMoveAngle + rightAngle < 0) + fearMoveAngle = fearMoveAngle + rightAngle + (M_PI*2); + else + fearMoveAngle += rightAngle; + ++interlevingSide; + } + x = X + (speed*3+unit.GetObjectSize()) * cos(fearMoveAngle); + y = Y + (speed*3+unit.GetObjectSize()) * sin(fearMoveAngle); + z = MapManager::Instance().GetBaseMap(mapid)->GetHeight(x,y,Z); + } + + if( interlevingSide < 76 ) + { + x = X + (speed*3) * cos(fearMoveAngle); + y = Y + (speed*3) * sin(fearMoveAngle); + //z = Z; //Doing Test... Probaly not needed... + z = MapManager::Instance().GetBaseMap(mapid)->GetHeight(x,y,Z); + + + } + else //At this point i think is better UnFear the MV owner or at max Range.. , GS + { + Traveller traveller(unit); + unit.SetOrientation(fearMoveAngle); + i_destinationHolder.SetDestination(traveller, X, Y, Z); + unit.clearUnitState(UNIT_STAT_FLEEING); + }*/ + } +} + +template void FleeingMovementGenerator::Initialize(T &unit) +{ + if(!&unit || !unit.isAlive()) + return; + + unit.setMoveRunFlag(true); + _setNextLocation(unit); +} +template void FleeingMovementGenerator::Reset(T &unit) +{ + +} + +template bool FleeingMovementGenerator::Update(T &unit, const uint32 &diff) +{ + if(!&unit || !unit.isAlive()) + return false; + + if(!i_destinationHolder.HasArrived() && unit.IsStopped()) + unit.addUnitState(UNIT_STAT_FLEEING); + + if( i_destinationHolder.GetTimeToDest() < diff ) + { + float x,y,z; + i_destinationHolder.GetLocationNow(x,y,z); + unit.Relocate(x,y,z); + _setNextLocation(unit); + } + Traveller traveller(unit); + i_destinationHolder.UpdateTraveller(traveller, diff, false); + + if( !unit.HasFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_FLEEING) ) + { + /*if(i_destinationHolder.GetTimeToDest()) + { + float x,y,z; + i_destinationHolder.GetLocationNow(x,y,z); + unit.Relocate(x,y,z); + }*/ + return false; + } + return true; +} + +template void FleeingMovementGenerator::_setNextLocation(Player &); +template void FleeingMovementGenerator::_setNextLocation(Creature &); +template void FleeingMovementGenerator::Initialize(Player &player); +template void FleeingMovementGenerator::Initialize(Creature &creature); +template void FleeingMovementGenerator::Reset(Player &player); +template void FleeingMovementGenerator::Reset(Creature &creature); +template bool FleeingMovementGenerator::Update(Player &player, const uint32 &diff); +template bool FleeingMovementGenerator::Update(Creature &creature, const uint32 &diff); Index: src/game/FleeingMovementGenerator.h =================================================================== --- src/game/FleeingMovementGenerator.h (revision 0) +++ src/game/FleeingMovementGenerator.h (revision 0) @@ -0,0 +1,46 @@ +/* + * 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 + */ + +#ifndef MANGOS_FLEEINGMOTIONGENERATOR_H +#define MANGOS_FLEEINGMOTIONGENERATOR_H + +#include "MovementGenerator.h" +#include "DestinationHolder.h" +#include "Traveller.h" + +template +class MANGOS_DLL_DECL FleeingMovementGenerator + : public MovementGeneratorMedium< T, FleeingMovementGenerator > +{ + public: + FleeingMovementGenerator(const Unit &, const uint64 _fearWhoGuid) : fearWhoGuid(_fearWhoGuid) {} + + void Initialize(T &); + void Reset(T &); + bool Update(T &, const uint32 &); + void UpdateMapPosition(float &x ,float &y, float &z) + { + i_destinationHolder.GetLocationNow(x,y,z); + } + MovementGeneratorType GetMovementGeneratorType() { return FLEEING_MOTION_TYPE; } + private: + void _setNextLocation(T &); + DestinationHolder< Traveller > i_destinationHolder; + uint64 fearWhoGuid; +}; +#endif Index: src/game/GridDefines.h =================================================================== --- src/game/GridDefines.h (revision 4763) +++ src/game/GridDefines.h (working copy) @@ -33,7 +33,7 @@ #define MAX_NUMBER_OF_GRIDS 64 -#define SIZE_OF_GRIDS 533.33333f +#define SIZE_OF_GRIDS 533.333333f #define CENTER_GRID_ID (MAX_NUMBER_OF_GRIDS/2) #define CENTER_GRID_OFFSET (SIZE_OF_GRIDS/2) Index: src/game/GridNotifiers.h =================================================================== --- src/game/GridNotifiers.h (revision 4763) +++ src/game/GridNotifiers.h (working copy) @@ -623,10 +623,10 @@ return false; // only from same creature faction - if(u->getFaction() != i_funit->getFaction() ) + if( u->getFaction() != i_funit->getFaction() ) return false; - if(!u->IsWithinLOSInMap(i_enemy) ) + if( !u->IsWithinLOSInMap(i_enemy) ) return false; return true; Index: src/game/GridNotifiersImpl.h =================================================================== --- src/game/GridNotifiersImpl.h (revision 4763) +++ src/game/GridNotifiersImpl.h (working copy) @@ -70,24 +70,26 @@ pl->UpdateVisibilityOf(c); // Creature AI reaction - if(!c->hasUnitState(UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING)) + if( !c->hasUnitState( UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING ) ) { - if( c->AI() && c->AI()->IsVisible(pl) && !c->IsInEvadeMode() ) + if( c->AI() && c->AI()->IsVisible(pl) /*&& !c->IsInEvadeMode()*/ ) c->AI()->MoveInLineOfSight(pl); } } inline void CreatureCreatureRelocationWorker(Creature* c1, Creature* c2) { - if(!c1->hasUnitState(UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING)) + //C2 is the Mover + if( !c1->hasUnitState( UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING) ) { - if( c1->AI() && c1->AI()->IsVisible(c2) && !c1->IsInEvadeMode() ) + if( c1->AI() && c1->AI()->IsVisible(c2) ) c1->AI()->MoveInLineOfSight(c2); } - - if(!c2->hasUnitState(UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING)) + + //To make Pet See everyone they call MILOS on... + if( c2->isPet() && !c2->hasUnitState( (UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING )) ) { - if( c2->AI() && c2->AI()->IsVisible(c1) && !c2->IsInEvadeMode() ) + if( c2->AI() && c2->AI()->IsVisible(c1) ) c2->AI()->MoveInLineOfSight(c1); } } @@ -107,6 +109,15 @@ inline void MaNGOS::CreatureRelocationNotifier::Visit(PlayerMapType &m) { + //Here should be when a creature move with RandomMV as example + //So she be sure to make all player around call for MILOS, is Player move with have double MILOS and before my change we had 4x MILOS + //So now we have MILOS comming from player only when Player move... + //All that maybe sound like a probleme... but not if grid notifier. + //Grid notifier should get update at all 100MS and checked for changed XY and Z + //So we have maximun 1 MILOS by target for a total 100MS. + //Why i think is better, not used that needed case here for the moment. + //Not good way to solve this situation... Is taking too much CPU, GS. + //return; if(!i_creature.isAlive()) return; @@ -119,7 +130,7 @@ inline void MaNGOS::CreatureRelocationNotifier::Visit(CreatureMapType &m) { - if(!i_creature.isAlive()) + if(!i_creature.isAlive() || (!i_creature.hasUnitState(UNIT_STAT_CHASE) && !i_creature.isPet() ) ) return; for(CreatureMapType::iterator iter=m.begin(); iter != m.end(); ++iter) Index: src/game/GuardAI.cpp =================================================================== --- src/game/GuardAI.cpp (revision 4763) +++ src/game/GuardAI.cpp (working copy) @@ -44,7 +44,7 @@ u->isInAccessablePlaceFor(&i_creature)) { float attackRadius = i_creature.GetAttackDistance(u); - if(i_creature.IsWithinDistInMap(u,attackRadius) && i_creature.GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE) + if(i_creature.IsWithinDistInMap(u,attackRadius) && !i_creature.HasPathTo(u)) { //Need add code to let guard suport player AttackStart(u); @@ -58,8 +58,6 @@ if( !i_creature.isAlive() ) { DEBUG_LOG("Creature stopped attacking because he's dead [guid=%u]", i_creature.GetGUIDLow()); - i_creature.StopMoving(); - i_creature.GetMotionMaster()->Idle(); i_state = STATE_NORMAL; @@ -97,10 +95,6 @@ i_victimGuid = 0; i_creature.CombatStop(true); i_state = STATE_NORMAL; - - // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead - if( i_creature.GetMotionMaster()->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE ) - i_creature.GetMotionMaster()->TargetedHome(); } void GuardAI::UpdateAI(const uint32 diff) Index: src/game/HomeMovementGenerator.cpp =================================================================== --- src/game/HomeMovementGenerator.cpp (revision 4763) +++ src/game/HomeMovementGenerator.cpp (working copy) @@ -23,50 +23,58 @@ #include "ObjectAccessor.h" #include "DestinationHolderImp.h" -void -HomeMovementGenerator::Initialize(Creature & owner) +#define WALKING_CHANCE_HOMEMV 20 //Chance will be " 1 / WALKING_CHANCE_HOMEMV" + +void HomeMovementGenerator::_setHomeLocation(Creature & creature) { - owner.setMoveRunFlag(true); - _setTargetLocation(owner); + //I don't think set destination should start traveller.... so we are little more flexible, GS + if( !&creature || !creature.isAlive() ) + return; + + float x, y, z; + creature.GetRespawnCoord(x, y, z); + + Traveller traveller(creature); + creature.SetOrientation(creature.GetAngle(x,y));//Need Badly Orientation of Spawn, GS + i_destinationHolder.SetDestination(traveller, x, y, z); + creature.addUnitState(UNIT_STAT_SEARCHING); } - -void -HomeMovementGenerator::Reset(Creature &) +void HomeMovementGenerator::Initialize(Creature & creature) { + creature.setMoveRunFlag(irand(0,WALKING_CHANCE_HOMEMV)); + _setHomeLocation(creature); } - -void -HomeMovementGenerator::_setTargetLocation(Creature & owner) +void HomeMovementGenerator::Reset(Creature &) { - if( !&owner ) - return; - if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED) ) - return; - - float x, y, z; - owner.GetRespawnCoord(x, y, z); - - CreatureTraveller traveller(owner); - - uint32 travel_time = i_destinationHolder.SetDestination(traveller, x, y, z); - modifyTravelTime(travel_time); - owner.clearUnitState(UNIT_STAT_ALL_STATE); } - -bool -HomeMovementGenerator::Update(Creature &owner, const uint32& time_diff) +bool HomeMovementGenerator::Update(Creature &creature, const uint32& diff) { - CreatureTraveller traveller( owner); - i_destinationHolder.UpdateTraveller(traveller, time_diff, false); + //TODO: Complete UNIT_STAT_SEARCHING, when creature has this status she won't accept any MILOS... + //So a creature going home won't change mind into the first 50% of mv... other MV will herite too. + //Juts have to think really with is the best UNIT STAT to use... and Do automation with checking unit field flag, as the client do is best. + if(!&creature || !creature.isAlive()) + return false; - if (time_diff > i_travel_timer) + CreatureTraveller traveller(creature); + + if(!i_destinationHolder.HasDestination()) { - owner.setMoveRunFlag(false); + Initialize(creature); + return true; + } + else if( i_destinationHolder.HasArrived() ) + { + if(!creature.IsStopped()) + creature.clearUnitState(UNIT_STAT_SEARCHING); return false; } + else if( i_destinationHolder.GetTimeToDest() < i_destinationHolder.GetTotalTravelTime()/2.0f) + creature.clearUnitState(UNIT_STAT_SEARCHING); + else if ( creature.IsStopped() ) + creature.addUnitState((UNIT_STAT_SEARCHING | UNIT_STAT_ROAMING)); - i_travel_timer -= time_diff; + i_destinationHolder.UpdateTraveller(traveller, diff, false); return true; } Index: src/game/HomeMovementGenerator.h =================================================================== --- src/game/HomeMovementGenerator.h (revision 4763) +++ src/game/HomeMovementGenerator.h (working copy) @@ -40,14 +40,15 @@ void Initialize(Creature &); void Reset(Creature &); bool Update(Creature &, const uint32 &); - void modifyTravelTime(uint32 travel_time) { i_travel_timer = travel_time; } + void UpdateMapPosition(float &x ,float &y, float &z) + { + i_destinationHolder.GetLocationNow(x,y,z); + } MovementGeneratorType GetMovementGeneratorType() { return HOME_MOTION_TYPE; } private: - void _setTargetLocation(Creature &); + void _setHomeLocation(Creature &); DestinationHolder< Traveller > i_destinationHolder; - - uint32 i_travel_timer; }; #endif Index: src/game/IdleMovementGenerator.cpp =================================================================== --- src/game/IdleMovementGenerator.cpp (revision 4763) +++ src/game/IdleMovementGenerator.cpp (working copy) @@ -17,11 +17,28 @@ */ #include "IdleMovementGenerator.h" -#include "Creature.h" +#include "Unit.h" +//IdleMovementGenerator si_idleMovement; -IdleMovementGenerator si_idleMovement; - -void -IdleMovementGenerator::Reset(Unit &owner) +void IdleMovementGenerator::Reset(Unit &owner) { + //Maybe reset lastTimer if not infinite } +void IdleMovementGenerator::Initialize(Unit &owner) +{ +} +bool IdleMovementGenerator::Update(Unit &owner, const uint32 &diff) +{ + if(!owner.IsStopped()) + owner.clearUnitState(UNIT_STAT_MOVING); + + if(!i_infinite) + { + if( i_lastTimer < diff ) + return false; + else + i_lastTimer -= diff; + } + + return true; +} Index: src/game/IdleMovementGenerator.h =================================================================== --- src/game/IdleMovementGenerator.h (revision 4763) +++ src/game/IdleMovementGenerator.h (working copy) @@ -24,12 +24,20 @@ class MANGOS_DLL_SPEC IdleMovementGenerator : public MovementGenerator { public: + IdleMovementGenerator(Unit &, bool infinite = true, uint32 lastTimer = 0) : + i_infinite(infinite), i_lastTimer(lastTimer) {} + ~IdleMovementGenerator() {} - void Initialize(Unit &) { } + void Initialize(Unit &); void Reset(Unit &); - bool Update(Unit &, const uint32 &) { return true; } + bool Update(Unit &, const uint32 &); MovementGeneratorType GetMovementGeneratorType() { return IDLE_MOTION_TYPE; } + + private: + uint32 i_lastTimer; + bool i_infinite; + }; -extern IdleMovementGenerator si_idleMovement; +//extern IdleMovementGenerator si_idleMovement; #endif Index: src/game/Map.cpp =================================================================== --- src/game/Map.cpp (revision 4763) +++ src/game/Map.cpp (working copy) @@ -1448,7 +1448,7 @@ { CellLock cell_lock(cell, cellpair); MaNGOS::CreatureRelocationNotifier relocationNotifier(*creature); - cell.data.Part.reserved = ALL_DISTRICT; + cell.data.Part.reserved = CENTER_DISTRICT; cell.SetNoCreate(); // not trigger load unloaded grids at notifier call TypeContainerVisitor c2world_relocation(relocationNotifier); Index: src/game/MotionMaster.cpp =================================================================== --- src/game/MotionMaster.cpp (revision 4763) +++ src/game/MotionMaster.cpp (working copy) @@ -20,123 +20,106 @@ #include "CreatureAISelector.h" #include "Creature.h" #include "Traveller.h" +#include "TargetedMovementGenerator.h" #include "HomeMovementGenerator.h" #include "IdleMovementGenerator.h" +#include "RandomMovementGenerator.h" #include -inline bool isStatic(MovementGenerator *mv) +MotionMaster::~MotionMaster() { - return (mv == &si_idleMovement); -} - -void -MotionMaster::Initialize() -{ - // clear ALL movement generators (including default) - while(!empty()) + // clear ALL movement generators + while( !empty() ) { MovementGenerator *curr = top(); pop(); - if( !isStatic( curr ) ) - delete curr; + delete curr; } +} +void MotionMaster::Initialize() +{ + i_owner->StopMoving(); - // set new default movement generator - if(i_owner->GetTypeId() == TYPEID_UNIT) + if(!empty()) + top()->Initialize(*i_owner); + else if(i_owner->GetTypeId() == TYPEID_UNIT && !((Creature*)i_owner)->isPet()) { MovementGenerator* movement = FactorySelector::selectMovementGenerator((Creature*)i_owner); - push( movement == NULL ? &si_idleMovement : movement ); + push( movement == NULL ? new IdleMovementGenerator(*i_owner) : movement ); top()->Initialize(*i_owner); } + else if(i_owner->GetTypeId() == TYPEID_UNIT && ((Creature*)i_owner)->isPet()) + { + push(new RandomMovementGenerator(*i_owner)); + } else - push(&si_idleMovement); -} - -MotionMaster::~MotionMaster() -{ - // clear ALL movement generators (including default) - while(!empty()) { - MovementGenerator *curr = top(); - pop(); - if( !isStatic( curr ) ) - delete curr; + push(new IdleMovementGenerator(*i_owner)); + //top()->Initialize(*i_owner); } } - -void -MotionMaster::UpdateMotion(const uint32 &diff) +void MotionMaster::UpdateMotion(const uint32 &diff) { - if(i_owner->hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED)) + //TODO: Remove Both with the Use of Idle MV , GS + if(i_owner->hasUnitState((UNIT_STAT_ROOT | UNIT_STAT_STUNDED )) ) return; - assert( !empty() ); + if (!top()->Update(*i_owner, diff)) MovementExpired(); } - -void -MotionMaster::Clear(bool reset) +void MotionMaster::Clear(bool reset) { - while( !empty() && size() > 1 ) + while( size() > 1 ) { MovementGenerator *curr = top(); pop(); - if( !isStatic( curr ) ) - delete curr; + delete curr; } - assert( !empty() ); - if (reset) top()->Reset(*i_owner); + if(reset) + top()->Initialize(*i_owner); } - -void -MotionMaster::MovementExpired(bool reset) +void MotionMaster::MovementExpired(bool reset) { - if( empty() || size() == 1 ) + if( size() < 2 ) //Never Delete the Index 0, and if we are at 0... something is wrong, GS return; - MovementGenerator *curr = top(); - pop(); + i_owner->StopMoving(); // In Fact No need to STOP, but need to update the Current Map Location before expiration, GS + MovementGenerator *curr = top(); // Save pointer to object into the list as Current One + pop(); // delete from List current one and put the next one on top. + delete curr; // Delete object at pointer, old current one - if( !isStatic(curr) ) - delete curr; + curr = top(); - assert( !empty() ); - while( !empty() && top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE ) - { - // Should check if target is still valid? If not valid it will crash. - curr = top(); - pop(); - delete curr; - } - if( empty() ) - Initialize(); - if (reset) top()->Reset(*i_owner); + if( reset ) + curr->Initialize(*i_owner); } -void -MotionMaster::TargetedHome() +void MotionMaster::TargetedHome() { - if(i_owner->hasUnitState(UNIT_STAT_FLEEING)) - return; - - DEBUG_LOG("Target home location %u", i_owner->GetGUIDLow()); - - Clear(false); Mutate(new HomeMovementGenerator()); } -void MotionMaster::Idle(void) +void MotionMaster::Idle(bool infinite, uint32 lastTimer) { - if( empty() || !isStatic( top() ) ) - push( &si_idleMovement ); + Mutate(new IdleMovementGenerator(*i_owner,infinite,lastTimer) ); } void MotionMaster::Mutate(MovementGenerator *m) { - // HomeMovement is not that important, delete it if meanwhile a new comes - if (!empty() && top()->GetMovementGeneratorType() == HOME_MOTION_TYPE) - MovementExpired(false); + i_owner->StopMoving(); + + if(size() == 1 && i_owner->GetTypeId() == TYPEID_UNIT && !((Creature*)i_owner)->isPet()&& (m->GetMovementGeneratorType() & (TARGETED_MOTION_TYPE | FLEEING_MOTION_TYPE)) ) + { + //Buggy case, but for moment ok doki... + switch(top()->GetMovementGeneratorType()) + { + case IDLE_MOTION_TYPE: + { + push(new HomeMovementGenerator()); + } + } + } m->Initialize(*i_owner); push(m); } Index: src/game/MotionMaster.h =================================================================== --- src/game/MotionMaster.h (revision 4763) +++ src/game/MotionMaster.h (working copy) @@ -38,7 +38,8 @@ CONFUSED_MOTION_TYPE = 4, // ConfusedMovementGenerator.h TARGETED_MOTION_TYPE = 5, // TargetedMovementGenerator.h HOME_MOTION_TYPE = 6, // HomeMovementGenerator.h - FLIGHT_MOTION_TYPE = 7 // WaypointMovementGenerator.h + FLIGHT_MOTION_TYPE = 7, // WaypointMovementGenerator.h + FLEEING_MOTION_TYPE = 8 // FleeingMovementGenerator.h }; class MANGOS_DLL_SPEC MotionMaster : private std::stack @@ -67,7 +68,7 @@ void MovementExpired(bool reset = true); - void Idle(void); + void Idle(bool infinite = true, uint32 lastTimer = 0); void TargetedHome(); Index: src/game/MovementHandler.cpp =================================================================== --- src/game/MovementHandler.cpp (revision 4763) +++ src/game/MovementHandler.cpp (working copy) @@ -111,7 +111,7 @@ { CHECK_PACKET_SIZE(recv_data, 4+4+4+4+4+4); - if(GetPlayer()->GetDontMove()) + if( GetPlayer()->GetDontMove() ) return; /* extract packet */ @@ -239,15 +239,19 @@ GetPlayer()->SetInWater( !GetPlayer()->IsInWater() ); /*----------------------*/ - /* process position-change */ - recv_data.put(4, getMSTime()); - WorldPacket data(recv_data.GetOpcode(), (GetPlayer()->GetPackGUID().size()+recv_data.size())); - data.append(GetPlayer()->GetPackGUID()); - data.append(recv_data.contents(), recv_data.size()); - GetPlayer()->SendMessageToSet(&data, false); - GetPlayer()->SetPosition(x, y, z, orientation); - GetPlayer()->SetMovementFlags(flags); + + if(!GetPlayer()->HasFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_FLEEING)) + { + /* process position-change */ + recv_data.put(4, getMSTime()); + WorldPacket data(recv_data.GetOpcode(), (GetPlayer()->GetPackGUID().size()+recv_data.size())); + data.append(GetPlayer()->GetPackGUID()); + data.append(recv_data.contents(), recv_data.size()); + GetPlayer()->SendMessageToSet(&data, false); + GetPlayer()->SetPosition(x, y, z, orientation); + GetPlayer()->SetMovementFlags(flags); + } } void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data) Index: src/game/NPCHandler.cpp =================================================================== --- src/game/NPCHandler.cpp (revision 4763) +++ src/game/NPCHandler.cpp (working copy) @@ -35,6 +35,7 @@ #include "MapManager.h" #include "Pet.h" #include "WaypointMovementGenerator.h" +#include "IdleMovementGenerator.h" #include "BattleGroundMgr.h" #include "BattleGround.h" @@ -250,7 +251,8 @@ if( unit->isArmorer() || unit->isCivilian() || unit->isQuestGiver() || unit->isServiceProvider()) { - unit->StopMoving(); + //TODO: Add static until all Gossip are closed. + unit->GetMotionMaster()->Mutate(new IdleMovementGenerator(*unit,false,30000)); //npcIsStopped[unit->GetGUID()] = true; } Index: src/game/Object.cpp =================================================================== --- src/game/Object.cpp (revision 4763) +++ src/game/Object.cpp (working copy) @@ -864,18 +864,19 @@ return ( dist > 0 ? dist : 0); } -bool WorldObject::IsWithinDistInMap(const WorldObject* obj, const float dist2compare) const +//TODO: Rename to: IsWithinDistInMap2D and create 3D, GS +bool WorldObject::IsWithinDistInMap(const WorldObject* obj, const float withInDist) const { + //I wan faster, faster, faster Code here :) , GS if (!obj || !IsInMap(obj)) return false; float dx = GetPositionX() - obj->GetPositionX(); float dy = GetPositionY() - obj->GetPositionY(); float dz = GetPositionZ() - obj->GetPositionZ(); - float distsq = dx*dx + dy*dy + dz*dz; - float sizefactor = GetObjectSize() + obj->GetObjectSize(); - float maxdist = dist2compare + sizefactor; + float dist2D = sqrt(dx*dx + dy*dy) - GetObjectSize() - obj->GetObjectSize(); + float distZ = sqrt(dz*dz) - 4.0f; //Each Moving Object is in size 2.0f Tall, event maybe a little more, GS. - return distsq < maxdist * maxdist; + return withInDist >= dist2D && withInDist >= distZ; } bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const @@ -891,7 +892,7 @@ float x,y,z; GetPosition(x,y,z); VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager(); - return vMapManager->isInLineOfSight(GetMapId(), x, y, z+2.0f, ox, oy, oz+2.0f); + return vMapManager->isInLineOfSight(GetMapId(), x, y, z+1.4f, ox, oy, oz+1.4f); } float WorldObject::GetAngle(const WorldObject* obj) const @@ -946,7 +947,9 @@ } // angle to face `obj` to `this` - float angle = rand_norm()*2*M_PI; + srand(getMSTime()); + float angle = rand_norm()*(M_PI*2); + srand(getMSTime()); float new_dist = rand_norm()*distance; rand_x = x + new_dist * cos(angle); @@ -962,7 +965,7 @@ { float new_z = MapManager::Instance().GetBaseMap(GetMapId())->GetHeight(x,y,z); if(new_z > VMAP_INVALID_HEIGHT) - z = new_z+ 0.2f; // just to be sure that we are not a few pixel under the surface + z = new_z/*+ 0.1f*/; // just to be sure that we are not a few pixel under the surface } } @@ -1203,20 +1206,82 @@ } // namespace MaNGOS //=================================================================================================== +//HasPathTo is to test not complete and doing really not good job... so...TESTING, GS +bool WorldObject::HasPathTo(WorldObject const* who) +{ + const float angleToWho = GetAngle(who); + const float distanceToWho = GetDistance2d(who); + float distanceToCheck = 0.5; + float X,Y,Z,x,y,z, checkX,checkY,checkZ; + who->GetPosition(x,y,z); + GetPosition(X,Y,Z); + const Map* map = MapManager::Instance().GetBaseMap(GetMapId()); + + //if( fabs(z - Z) + 0.000001f > 3.0f) + // return false; + for(;distanceToWho > distanceToCheck; distanceToCheck += 0.5) + { + checkX = X + (distanceToCheck * cos(angleToWho)); + checkY = Y + (distanceToCheck * sin(angleToWho)); + checkZ = map->GetHeight(checkX,checkY,Z); //Disable LOS when a creature is the target... For saving cpu... but is needed in fact, GS. + const float zDist = fabs(checkZ - Z) + 0.000001f; //FineTune with that value..., GS + const float slope = distanceToCheck / zDist; + if(fabs(slope) < 1.00 || (map->GetTerrainType(checkX,checkY) & 0x01)) + return false; + } + return true; +} +bool WorldObject::HasPathTo(float x, float y) +{ + + const float angleToWho = GetAngle(x,y); + const float distanceToWho = GetDistance2d(x,y); + float distanceToCheck = 0.5; + float X,Y,Z, checkX,checkY,checkZ; + GetPosition(X,Y,Z); + const Map* map = MapManager::Instance().GetBaseMap(GetMapId()); + for(;distanceToWho > distanceToCheck; distanceToCheck += 0.5) + { + checkX = X + (distanceToCheck * cos(angleToWho)); + checkY = Y + (distanceToCheck * sin(angleToWho)); + checkZ = map->GetHeight(checkX,checkY,Z); + const float zDist = fabs(checkZ - Z) + 0.000001f; //FineTune with that value..., GS + const float slope = distanceToCheck / zDist; + if(fabs(slope) < 1.00 || (map->GetTerrainType(checkX,checkY) & 0x01) ) + return false; + } + return true; +} void WorldObject::GetNearPoint2D(float &x, float &y, float distance2d, float absAngle ) const { - x = GetPositionX() + (GetObjectSize() + distance2d) * cos(absAngle); - y = GetPositionY() + (GetObjectSize() + distance2d) * sin(absAngle); + x = GetPositionX() + ((distance2d+GetObjectSize()) * cos(absAngle)); + y = GetPositionY() + ((distance2d+GetObjectSize()) * sin(absAngle)); } - -void WorldObject::GetNearPoint(WorldObject const* searcher, float &x, float &y, float &z, float distance2d, float absAngle ) const +//Pet Follow Will be wrong there, i think best way will be to specifie if Combat Targeted or Following Targeted, GS +void WorldObject::GetNearPoint(WorldObject const* searcher, float &x, float &y, float &z, float distance2d, float absAngle) const { - float size = searcher ? searcher->GetObjectSize() : 0; - - GetNearPoint2D(x,y,distance2d+size,absAngle); + //Need to fix that so Pet are not using that distance check, should be since searcher is NULL on pet Follow... But... GS + float minReachRadius = searcher ? searcher->GetObjectSize() + (searcher->GetFloatValue(UNIT_FIELD_COMBATREACH)/1.75) : 0; + distance2d = distance2d < minReachRadius ? minReachRadius : distance2d; + GetNearPoint2D(x,y,distance2d,absAngle); z = GetPositionZ(); + const float nz = MapManager::Instance().GetBaseMap(GetMapId())->GetHeight(x,y,z); + + //2D-MG + const float zDist = fabs(z - nz) + 0.000001f; //FineTune with that value..., GS + const float slope = distance2d / zDist; + if(fabs(slope) < 1.00 || (MapManager::Instance().GetBaseMap(GetMapId())->GetTerrainType(x,y) & 0x01)) // Doing Loop To find good One is better and Put Next Update time very High if can find any. + { + if(searcher) + { + searcher->GetPosition(x,y,z); // all wrong but for testing... as i say ofent into comment Targeted MV need replacement so.., GS + } + } + return; + //All below are such complicated for what we really need for poscollision. + //TODO: Update that... will be done in fact from MovementGenerator // if detection disabled, return first point if(!sWorld.getConfig(CONFIG_DETECT_POS_COLLISION)) { @@ -1230,7 +1295,7 @@ bool first_los_conflict = false; // first point LOS problems // prepare selector for for work - ObjectPosSelector selector(GetPositionX(),GetPositionY(),GetObjectSize(),distance2d+size); + ObjectPosSelector selector(GetPositionX(),GetPositionY(),GetObjectSize(),distance2d+minReachRadius); // adding used positions around object { Index: src/game/Object.h =================================================================== --- src/game/Object.h (revision 4763) +++ src/game/Object.h (working copy) @@ -24,13 +24,14 @@ #include "UpdateFields.h" #include "UpdateData.h" #include "GameSystem/GridReference.h" +#include "../shared/Util.h" #include #include #define CONTACT_DISTANCE 0.5 #define INTERACTION_DISTANCE 5 -#define ATTACK_DISTANCE 5 +#define ATTACK_DISTANCE 5 #define DETECT_DISTANCE 20 // max distance to successful detect stealthed unit #define MAX_VISIBILITY_DISTANCE (5*SIZE_OF_GRID_CELL/2) // max distance for visible object show, limited by active zone for player based at cell size (active zone = 5x5 cells) #define DEFAULT_VISIBILITY_DISTANCE (SIZE_OF_GRID_CELL) // default visible distance @@ -278,17 +279,23 @@ void GetPosition( float &x, float &y, float &z ) const { x = m_positionX; y = m_positionY; z = m_positionZ; } float GetOrientation( ) const { return m_orientation; } + bool HasPathTo(WorldObject const* who); + bool HasPathTo(float x, float y); void GetNearPoint2D( float &x, float &y, float distance, float absAngle) const; - void GetNearPoint( WorldObject const* searcher, float &x, float &y, float &z, float distance2d,float absAngle) const; + void GetNearPoint( WorldObject const* follower, float &x, float &y, float &z, float distance2d,float absAngle) const; void GetClosePoint(float &x, float &y, float &z, float distance2d = 0, float angle = 0) const { // angle calculated from current orientation GetNearPoint(NULL,x,y,z,distance2d,GetOrientation() + angle); + } void GetContactPoint( const WorldObject* obj, float &x, float &y, float &z, float distance2d = CONTACT_DISTANCE) const { - // angle to face `obj` to `this` using distance includes size of `obj` - GetNearPoint(obj,x,y,z,distance2d,GetAngle( obj )); + //angle to face `obj` to `this` using distance includes size of `obj` + //Will be much more better make a MASK 32 Bits on each Unit... and we make 32 possible solution + //16 Front 16 Back... i think will be enought, or 64Bits mask... so 32/32 + //To replace PosCollision, PosCollision is good system but will better, for a global collision system, GS + GetNearPoint(obj,x,y,z,distance2d, /*GetAngle(obj)*/rand_norm()*(M_PI*2)); } const float GetObjectSize() const { Index: src/game/ObjectAccessor.cpp =================================================================== --- src/game/ObjectAccessor.cpp (revision 4763) +++ src/game/ObjectAccessor.cpp (working copy) @@ -604,6 +604,9 @@ update_cell << 1; update_cell -= 1; + //Here is Creating Loop under some Circonstance, GS... + // >> / << Update X and +/- Update Y + //Seem to happen when we are very near Bondary... of a Grid.... for( ; abs(int32(standing_cell.x_coord) - int32(update_cell.x_coord)) < 2; update_cell >> 1 ) { for(CellPair cell_iter=update_cell; abs(int32(standing_cell.y_coord) - int32(cell_iter.y_coord)) < 2; cell_iter += 1 ) Index: src/game/ObjectMgr.cpp =================================================================== --- src/game/ObjectMgr.cpp (revision 4763) +++ src/game/ObjectMgr.cpp (working copy) @@ -693,7 +693,11 @@ data.movementType = fields[19].GetUInt8(); data.auras = fields[20].GetCppString(); int16 gameEvent = fields[21].GetInt16(); - + if(data.movementType == 1 && data.spawndist <= 0.0f) + { + sLog.outErrorDb("table creature, GUID:%u , table creature_template, ENTRY:%u , Has RandomMovement but SpawnDist==%f",guid,data.id,data.spawndist); + data.movementType = 0; + } if (gameEvent==0) // if not this is to be managed by GameEvent System AddCreatureToGrid(guid, &data); count++; @@ -706,6 +710,191 @@ sLog.outString( ">> Loaded %u creatures", mCreatureDataMap.size() ); } +void ObjectMgr::LoadCreatureSpellBook() +{ + + QueryResult* _result = WorldDatabase.Query("SELECT * FROM creature_spellbook"); + + if(!_result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(""); + sLog.outErrorDb(">> Loaded 0 creature_spellBook definition. DB table `creature_spellBook` is empty."); + return; + } + + barGoLink bar(_result->GetRowCount()); + + do + { + bar.step(); + Field *_spellInfo = _result->Fetch(); + uint32 creatureEntry = _spellInfo[0].GetUInt32(); + if(!creatureEntry) + continue; + + char* _spellId = NULL; + + //spellId, recoveryTime, unknow1, buffDuration, cost, rangeMin, rangeMax + for(uint8 itr = CREATURE_SPELL_MIN; itr < CREATURE_SPELL_MAX; ++itr) + { + const char* rawSpellInfo = _spellInfo[itr+1].GetString(); + _spellId = strtok((char*)rawSpellInfo, " "); + if( _spellId ) + { + uint32 spellId = mCreatureSpellBookMap[creatureEntry].info[itr].id = atoi(_spellId); + + char* _recoveryTime = strtok(NULL, " "); + if(_recoveryTime) + mCreatureSpellBookMap[creatureEntry].info[itr].recoveryTime = atoi(_recoveryTime); + + char* _castingTime = strtok(NULL, " "); + if(_castingTime) + mCreatureSpellBookMap[creatureEntry].info[itr].unknow1 = atoi(_castingTime); + + char* _buffDuration = strtok(NULL, " "); + if(_buffDuration) + mCreatureSpellBookMap[creatureEntry].info[itr].buffDuration = atoi(_buffDuration); + + char* _cost = strtok(NULL, " "); + if(_cost) + mCreatureSpellBookMap[creatureEntry].info[itr].cost = atoi(_cost); + + char* _rangeMin = strtok(NULL, " "); + if(_rangeMin) + mCreatureSpellBookMap[creatureEntry].info[itr].range[0] = atof(_rangeMin); + + char* _rangeMax = strtok(NULL, " "); + if(_rangeMax) + mCreatureSpellBookMap[creatureEntry].info[itr].range[1] = atof(_rangeMax); + } + } + + SpellEntry const* dbcSpell; + SpellRangeEntry const *dbcRange; + SpellDurationEntry const *dbcDuration; + SpellCastTimesEntry const *dbcCastTime; + + CreatureSpellBookInfoMap::iterator itr = mCreatureSpellBookMap[creatureEntry].info.begin(); + + for(; itr != mCreatureSpellBookMap[creatureEntry].info.end(); ++itr) + { + dbcSpell = GetSpellStore()->LookupEntry(itr->second.id); + if( !dbcSpell ) + continue; + dbcRange = GetSpellRangeStore()->LookupEntry(dbcSpell->rangeIndex); + dbcDuration = GetSpellDurationStore()->LookupEntry(dbcSpell->DurationIndex); + dbcCastTime = GetCastTimesStore()->LookupEntry(dbcSpell->CastingTimeIndex); + + for (int j=0; j<3; j++) + { + //Spell targets self + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_SELF ) + itr->second.targets |= 1 << (SELECT_TARGET_SELF-1); + + //Spell targets a single enemy + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE || + dbcSpell->EffectImplicitTargetA[j] == TARGET_CURRENT_SELECTED_ENEMY ) + itr->second.targets |= 1 << (SELECT_TARGET_SINGLE_ENEMY-1); + + //Spell targets AoE at enemy + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA || + dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || + dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED ) + itr->second.targets |= 1 << (SELECT_TARGET_AOE_ENEMY-1); + + //Spell targets an enemy + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE || + dbcSpell->EffectImplicitTargetA[j] == TARGET_CURRENT_SELECTED_ENEMY || + dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA || + dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || + dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED ) + itr->second.targets |= 1 << (SELECT_TARGET_ANY_ENEMY-1); + + //Spell targets a single friend(or self) + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_SELF || + dbcSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_FRIEND || + dbcSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_PARTY ) + itr->second.targets |= 1 << (SELECT_TARGET_SINGLE_FRIEND-1); + + //Spell targets aoe friends + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER || + dbcSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY ) + itr->second.targets |= 1 << (SELECT_TARGET_AOE_FRIEND-1); + + //Spell targets any friend(or self) + if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_SELF || + dbcSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_FRIEND || + dbcSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_PARTY || + dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER || + dbcSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY ) + itr->second.targets |= 1 << (SELECT_TARGET_ANY_FRIEND-1); + + //Make sure that this spell includes a damage effect + if ( dbcSpell->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE || + dbcSpell->Effect[j] == SPELL_EFFECT_INSTAKILL || + dbcSpell->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE || + dbcSpell->Effect[j] == SPELL_EFFECT_HEALTH_LEECH ) + itr->second.effects |= 1 << (SELECT_EFFECT_DAMAGE-1); + + //Make sure that this spell includes a healing effect + if ( dbcSpell->Effect[j] == SPELL_EFFECT_HEAL || + dbcSpell->Effect[j] == SPELL_EFFECT_HEAL_MAX_HEALTH || + dbcSpell->Effect[j] == SPELL_EFFECT_HEAL_MECHANICAL ) + itr->second.effects |= 1 << (SELECT_EFFECT_HEALING-1); + + //Make sure that this spell applies an aura + if( dbcSpell->Effect[j] == SPELL_EFFECT_APPLY_AURA ) + itr->second.effects |= 1 << (SELECT_EFFECT_AURA-1); + + // Buff Duration + if( dbcDuration && itr->second.buffDuration < dbcDuration->Duration[j] )//DBC Info Not Good + itr->second.buffDuration = dbcDuration->Duration[j]; + } + + //We use powerCost from SQL on missing DBC value + if( dbcSpell->manaCost ) + { + itr->second.usingDBC = true; + itr->second.cost = dbcSpell->manaCost; + } + + //CoolDown + if( !itr->second.recoveryTime && dbcSpell->RecoveryTime ) + itr->second.recoveryTime = dbcSpell->RecoveryTime; + + if(dbcRange) + { + //Range Min + if( !itr->second.range[0] ) + itr->second.range[0] = dbcRange->minRange; + //Range Max + if( !itr->second.range[1] )//DBC Info Not Good + itr->second.range[1] = dbcRange->maxRange; + } + + //Casttime + if(dbcCastTime && dbcCastTime->CastTime) + itr->second.hasCastTime = true; + } + }while( _result->NextRow() ); + + delete _result; + + sLog.outString(); + sLog.outString( ">> Loaded %u creatures", mCreatureSpellBookMap.size() ); +} +CreatureSpellBook* ObjectMgr::GetCreatureSpellBook(uint32 entry) +{ + CreatureSpellBookMap::iterator itr = mCreatureSpellBookMap.find(entry); + if( itr != mCreatureSpellBookMap.end() ) + return &itr->second; + else + return NULL; +} void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data) { CellPair cell_pair = MaNGOS::ComputeCellPair(data->posX, data->posY); Index: src/game/ObjectMgr.h =================================================================== --- src/game/ObjectMgr.h (revision 4763) +++ src/game/ObjectMgr.h (working copy) @@ -45,6 +45,7 @@ extern SQLStorage sCreatureStorage; extern SQLStorage sCreatureDataAddonStorage; +//extern SQLStorage sCreatureRawSpellBookStorage; extern SQLStorage sCreatureInfoAddonStorage; extern SQLStorage sCreatureModelStorage; extern SQLStorage sEquipmentStorage; @@ -74,7 +75,6 @@ float z; float o; }; - typedef std::multimap ScriptMap; typedef std::map ScriptMapMap; extern ScriptMapMap sQuestEndScripts; @@ -115,6 +115,7 @@ typedef HM_NAMESPACE::hash_map RespawnTimes; +typedef HM_NAMESPACE::hash_map CreatureSpellBookMap; typedef HM_NAMESPACE::hash_map CreatureDataMap; typedef HM_NAMESPACE::hash_map GameObjectDataMap; typedef HM_NAMESPACE::hash_map CreatureLocaleMap; @@ -267,15 +268,16 @@ void RemoveArenaTeam(ArenaTeam* arenateam) { mArenaTeamSet.erase( arenateam ); } CreatureInfo const *GetCreatureTemplate( uint32 id ); + CreatureModelInfo const *GetCreatureModelInfo( uint32 modelid ); CreatureModelInfo const* GetCreatureModelRandomGender(uint32 display_id); uint32 ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data); EquipmentInfo const *GetEquipmentInfo( uint32 entry ); + static CreatureDataAddon const *GetCreatureAddon( uint32 lowguid ) { return sCreatureDataAddonStorage.LookupEntry(lowguid); } - static CreatureDataAddon const *GetCreatureTemplateAddon( uint32 entry ) { return sCreatureInfoAddonStorage.LookupEntry(entry); @@ -464,6 +466,8 @@ void LoadCreatureRespawnTimes(); void LoadCreatureAddons(); void LoadCreatureModelInfo(); + void LoadCreatureSpellBook(); + CreatureSpellBook* GetCreatureSpellBook(uint32 entry); void LoadEquipmentTemplates(); void LoadGameObjectLocales(); void LoadGameobjects(); @@ -821,6 +825,7 @@ MapObjectGuids mMapObjectGuids; CreatureDataMap mCreatureDataMap; CreatureLocaleMap mCreatureLocaleMap; + CreatureSpellBookMap mCreatureSpellBookMap; GameObjectDataMap mGameObjectDataMap; GameObjectLocaleMap mGameObjectLocaleMap; ItemLocaleMap mItemLocaleMap; @@ -836,6 +841,6 @@ typedef HM_NAMESPACE::hash_map SpellProcEventMap; SpellProcEventMap mSpellProcEventMap; }; - +//SQLStorage const* GetCreaturesSpellsBooks() {return &sCreatureSpellBookStorage;} #define objmgr MaNGOS::Singleton::Instance() #endif Index: src/game/Pet.h =================================================================== --- src/game/Pet.h (revision 4763) +++ src/game/Pet.h (working copy) @@ -100,11 +100,12 @@ #define ACTIVE_SPELLS_MAX 4 -#define OWNER_MAX_DISTANCE 100 +#define OWNER_MAX_DISTANCE 100.0f -#define PET_FOLLOW_DIST 1 -#define PET_FOLLOW_ANGLE (M_PI/2) +#define PET_FOLLOW_DIST 2.0f +#define PET_FOLLOW_ANGLE (M_PI/2.0f) + class Pet : public Creature { public: Index: src/game/PetAI.cpp =================================================================== --- src/game/PetAI.cpp (revision 4763) +++ src/game/PetAI.cpp (working copy) @@ -42,41 +42,80 @@ UpdateAllies(); } -void PetAI::MoveInLineOfSight(Unit *u) +void PetAI::MoveInLineOfSight(Unit *who) { - if( !i_pet.getVictim() && (i_pet.isPet() || i_pet.isCharmed()) && i_pet.GetCharmInfo()->HasReactState(REACT_AGGRESSIVE) && - u->isTargetableForAttack() && i_pet.IsHostileTo( u ) && - u->isInAccessablePlaceFor(&i_pet)) + if( i_pet.GetCharmInfo() ) { - float attackRadius = i_pet.GetAttackDistance(u); - if(i_pet.IsWithinDistInMap(u, attackRadius) && i_pet.GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE) + //Planning something to get Rid of all Z check like that will be great since it wrong, GS + if( !i_pet.HasPathTo(who) ) + return; + + if( !i_pet.GetUInt64Value(UNIT_FIELD_TARGET) ) { - if(!i_pet.IsWithinLOSInMap(u)) return; - AttackStart(u); - u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + if( i_pet.GetCharmInfo()->HasReactState(REACT_AGGRESSIVE)) + { + if( i_pet.IsWithinDistInMap(who,RANGE_AUTO_ASSIST_PET ) && i_pet.IsFriendlyTo(who) ) + { + if(!i_pet.IsWithinLOSInMap(who)) + return; + + //TODO: make it with IsInCombat() + Take target from AttackerList. + if(!who->getAttackers().empty()) + { + Unit* victimTarget = *(who->getAttackers().begin()); + if( i_pet.IsHostileTo(victimTarget) && (Unit::getHpPercent(victimTarget) < 100 || Unit::getHpPercent(who) < 100) + && i_pet.IsWithinDistInMap(victimTarget, 46.0f) ) //Just a safe distance. + AttackStart( victimTarget ); + } + } + return; + } + + if((i_pet.GetCharmInfo()->HasReactState(REACT_DEFENSIVE) && Unit::getHpPercent(who) < 100)) + { + if( i_pet.IsWithinDistInMap(who,RANGE_AUTO_ASSIST_PET ) && i_pet.IsFriendlyTo(who) ) + { + if(!i_pet.IsWithinLOSInMap(who)) + return; + + //TODO: make it with IsInCombat() + Take target from AttackerList. + if(!who->getAttackers().empty()) + { + Unit* victimTarget = *(who->getAttackers().begin()); + if( i_pet.IsHostileTo(victimTarget) && i_pet.IsWithinDistInMap(victimTarget, 46.0f)) //Just a safe distance. + AttackStart( victimTarget ); + } + } + } } } } -void PetAI::AttackStart(Unit *u) +void PetAI::AttackStart(Unit *unit) { - if( i_pet.getVictim() || !u || i_pet.isPet() && ((Pet&)i_pet).getPetType()==MINI_PET ) + if( !unit || i_pet.isPet() && ((Pet&)i_pet).getPetType()==MINI_PET ) return; - if(i_pet.Attack(u)) + if( unit->isTargetableForAttack() && i_pet.HasPathTo(unit)) { - i_pet.clearUnitState(UNIT_STAT_FOLLOW); - // TMGs call CreatureRelocation which via MoveInLineOfSight can call this function - // thus with the following clear the original TMG gets invalidated and crash, doh - // hope it doesn't start to leak memory without this :-/ - //i_pet->Clear(); - i_victimGuid = u->GetGUID(); - i_pet.GetMotionMaster()->Mutate(new TargetedMovementGenerator(*u)); + if ( i_pet.Attack(unit) ) + { + i_pet.AddThreat(unit, 0.0f); + i_pet.resetAttackTimer(); + if(i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) + AIMotionTarget(unit,FOLLOW_RANGE_MELEE); + + return; + }else if( i_pet.getVictim() == unit && i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW) ) //HackWay... Cause Attack() will return false if same attacker. + AIMotionTarget(unit,FOLLOW_RANGE_MELEE); } } void PetAI::EnterEvadeMode() { + i_pet.DeleteThreatList(); + i_pet.AttackStop(); + i_pet.ClearInCombat(false); } bool PetAI::IsVisible(Unit *pl) const @@ -98,136 +137,50 @@ void PetAI::_stopAttack() { - if( !i_victimGuid ) - return; + EnterEvadeMode(); +} - Unit* victim = ObjectAccessor::Instance().GetUnit(i_pet, i_victimGuid ); - - if ( !victim ) +void PetAI::UpdateAI(const uint32 diff) +{ + Unit* owner = i_pet.GetCharmerOrOwner(); + if(!owner) return; - assert(!i_pet.getVictim() || i_pet.getVictim() == victim); - - if( !i_pet.isAlive() ) + // update i_victimGuid if i_pet.getVictim() !=0 and changed + if(!i_pet.getVictim() && !i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE)) { - DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_pet.GetGUIDLow()); - i_pet.StopMoving(); - i_pet.GetMotionMaster()->Clear(); - i_pet.GetMotionMaster()->Idle(); - i_victimGuid = 0; - i_pet.CombatStop(true); - i_pet.getHostilRefManager().deleteReferences(); + + Unit* ownerAttacker = owner->getAttackerForHelper(); + if(ownerAttacker) + { - return; + if(i_pet.GetCharmInfo()->HasReactState(REACT_AGGRESSIVE) && (Unit::getHpPercent(ownerAttacker) < 100 || Unit::getHpPercent(owner) < 100)) + AttackStart(ownerAttacker); + else if(Unit::getHpPercent(owner) < 100) + AttackStart(ownerAttacker); + } } - else if( !victim ) - { - DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_pet.GetGUIDLow()); - } - else if( !victim->isAlive() ) - { - DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", i_pet.GetGUIDLow()); - } - else if( victim->HasStealthAura() ) - { - DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_pet.GetGUIDLow()); - } - else if( victim->isInFlight() ) - { - DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_pet.GetGUIDLow()); - } - else - { - DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", i_pet.GetGUIDLow()); - } - Unit* owner = i_pet.GetCharmerOrOwner(); - - if(owner && i_pet.GetCharmInfo() && i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) - { - i_pet.addUnitState(UNIT_STAT_FOLLOW); - i_pet.GetMotionMaster()->Clear(); - i_pet.GetMotionMaster()->Mutate(new TargetedMovementGenerator(*owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE)); - } - else - { - i_pet.clearUnitState(UNIT_STAT_FOLLOW); - i_pet.addUnitState(UNIT_STAT_STOPPED); - i_pet.GetMotionMaster()->Clear(); - i_pet.GetMotionMaster()->Idle(); - } - i_victimGuid = 0; - i_pet.AttackStop(); -} - -void PetAI::UpdateAI(const uint32 diff) -{ - // update i_victimGuid if i_pet.getVictim() !=0 and changed - if(i_pet.getVictim()) - i_victimGuid = i_pet.getVictim()->GetGUID(); - - Unit* owner = i_pet.GetCharmerOrOwner(); - if(m_updateAlliesTimer <= diff) // UpdateAllies self set update timer UpdateAllies(); else m_updateAlliesTimer -= diff; - - // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. - if( i_victimGuid ) + + if( !i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) && !_needToStop() ) { - if( _needToStop() ) + if( i_pet.isAttackReady() && i_pet.IsWithinDistInMap(i_pet.getVictim(), i_pet.GetFloatValue(UNIT_FIELD_COMBATREACH)) ) { - DEBUG_LOG("Pet AI stoped attacking [guid=%u]", i_pet.GetGUIDLow()); - _stopAttack(); // i_victimGuid == 0 && i_pet.getVictim() == NULL now - return; + i_pet.AttackerStateUpdate(i_pet.getVictim()); + i_pet.resetAttackTimer(); + i_pet.SelectHostilTarget(); } - else if( i_pet.IsStopped() || i_pet.IsWithinDistInMap(i_pet.getVictim(), ATTACK_DISTANCE)) - { - // required to be stopped cases - if ( i_pet.IsStopped() && i_pet.IsNonMeleeSpellCasted(false) ) - { - if( i_pet.hasUnitState(UNIT_STAT_FOLLOW) ) - i_pet.InterruptNonMeleeSpells(false); - else - return; - } - // not required to be stopped case - else if( i_pet.isAttackReady() && i_pet.canReachWithAttack(i_pet.getVictim()) ) - { - i_pet.AttackerStateUpdate(i_pet.getVictim()); - - i_pet.resetAttackTimer(); - - if ( !i_pet.getVictim() ) - return; - - //if pet misses its target, it will also be the first in threat list - i_pet.getVictim()->AddThreat(&i_pet,0.0f); - - if( _needToStop() ) - _stopAttack(); - } - } } - else if(owner && i_pet.GetCharmInfo()) + if( i_pet.isInCombat() && _needToStop() ) { - if(owner->isInCombat() && !(i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) || i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY))) - { - AttackStart(owner->getAttackerForHelper()); - } - else if(i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) - { - if (!i_pet.hasUnitState(UNIT_STAT_FOLLOW) ) - { - i_pet.addUnitState(UNIT_STAT_FOLLOW); - i_pet.GetMotionMaster()->Clear(); - i_pet.GetMotionMaster()->Mutate(new TargetedMovementGenerator(*owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE)); - } - } + _stopAttack(); + return; } - //Autocast HM_NAMESPACE::hash_map targetMap; targetMap.clear(); @@ -242,7 +195,7 @@ Spell *spell = new Spell(&i_pet, spellInfo, false, 0); - if(!IsPositiveSpell(spellInfo->Id) && i_pet.getVictim() && !_needToStop() && !i_pet.hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(i_pet.getVictim())) + if(!IsPositiveSpell(spellInfo->Id) && i_pet.getVictim() && !_needToStop() /*&& !i_pet.hasUnitState(UNIT_STAT_FOLLOW)*/ && spell->CanAutoCast(i_pet.getVictim())) targetMap[spellID] = i_pet.getVictim(); else { @@ -294,6 +247,8 @@ spell->prepare(&targets); } targetMap.clear(); + + //if(i_pet.getVictim()) } bool PetAI::_isVisible(Unit *u) const @@ -342,10 +297,48 @@ m_AllySet.insert(owner->GetGUID()); } -void PetAI::AttackedBy(Unit *attacker) +void PetAI::AIMotionTarget(Unit* target, float offSet) { + bool exist = false; + MotionMaster* creatureMM = i_pet.GetMotionMaster(); + + if(!target /*&& creatureMM->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE*/) + { + //creatureMM->MovementExpired(); + return; + } + //Here is wrong... TODO fix angle... but in fact i wan MV for Follower and One for Attacker. + float angle = NULL; + if( !i_pet.IsHostileTo(target) ) + { + angle = target->GetOrientation()*2.0f; + } + + if(creatureMM->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE) + { + TargetedMovementGenerator* targetMV = static_cast*> (creatureMM->top()); + if( targetMV && targetMV->GetTarget() ) + { + if( targetMV->GetTarget()->GetGUID() != target->GetGUID() ) + { + creatureMM->MovementExpired(false); + creatureMM->Mutate(new TargetedMovementGenerator(*target,offSet,angle)); + } + else if(targetMV->GetOffSet() != offSet) + targetMV->SetOffSet(offSet); + } + } + else + { + i_pet.GetMotionMaster()->Mutate(new TargetedMovementGenerator(*target,offSet,angle)); + } +} + +/*void PetAI::AttackedBy(Unit *attacker) +{ //when attacked, fight back in case 1)no victim already AND 2)not set to passive AND 3)not set to stay, unless can it can reach attacker with melee attack anyway if(!i_pet.getVictim() && i_pet.GetCharmInfo() && !i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) && (!i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY) || i_pet.canReachWithAttack(attacker))) AttackStart(attacker); } +*/ Index: src/game/PetAI.h =================================================================== --- src/game/PetAI.h (revision 4763) +++ src/game/PetAI.h (working copy) @@ -34,7 +34,7 @@ void AttackStart(Unit *); void EnterEvadeMode(); void DamageTaken(Unit *done_by, uint32 &damage) { AttackedBy(done_by); } - void AttackedBy(Unit *attacker); + void AttackedBy(Unit *attacker) {} bool IsVisible(Unit *) const; void UpdateAI(const uint32); @@ -45,6 +45,7 @@ bool _isVisible(Unit *) const; bool _needToStop(void) const; void _stopAttack(void); + void AIMotionTarget(Unit *, float); Creature &i_pet; uint64 i_victimGuid; Index: src/game/PetHandler.cpp =================================================================== --- src/game/PetHandler.cpp (revision 4763) +++ src/game/PetHandler.cpp (working copy) @@ -26,6 +26,7 @@ #include "Spell.h" #include "ObjectAccessor.h" #include "MapManager.h" +#include "IdleMovementGenerator.h" #include "TargetedMovementGenerator.h" #include "CreatureAI.h" #include "Util.h" @@ -71,46 +72,51 @@ sLog.outError("WorldSession::HandlePetAction: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); return; } - + //All here was wrong... i just tryed to make my own version, until i got real data from official, GS + // I have the good data from official and will have some probleme doing the defencive Status.. all other will be much more sample + // then the actual one :) , GS switch(flag) { case ACT_COMMAND: //0x0700 switch(spellid) { case COMMAND_STAY: //flat=1792 //STAY - pet->StopMoving(); - pet->GetMotionMaster()->Clear(); - pet->GetMotionMaster()->Idle(); + if(!pet->hasUnitState(UNIT_STAT_FOLLOW)) + break; + if(pet->GetMotionMaster()->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE) + pet->GetMotionMaster()->MovementExpired(); + + //Will be possible to him to overide Fleeing MV, Check this out, GS + //In fact FleeingMV should not accept to be Overide so the fix should be very great that way. + //I must be able to add a new MG but at index (top() - 1) + pet->GetMotionMaster()->Mutate(new IdleMovementGenerator(*pet)); + pet->clearUnitState(UNIT_STAT_FOLLOW); charmInfo->SetCommandState( COMMAND_STAY ); break; case COMMAND_FOLLOW: //spellid=1792 //FOLLOW - pet->AttackStop(); - pet->addUnitState(UNIT_STAT_FOLLOW); + //Here i use clear() to return to originalMG, but fear must not be overide here.. so same as STAY.. solution will come later, GS pet->GetMotionMaster()->Clear(); - pet->GetMotionMaster()->Mutate(new TargetedMovementGenerator(*_player,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE)); + pet->addUnitState(UNIT_STAT_FOLLOW); + ((Creature*)pet)->AI()->EnterEvadeMode(); charmInfo->SetCommandState( COMMAND_FOLLOW ); break; case COMMAND_ATTACK: //spellid=1792 //ATTACK { - // only place where pet can be player - pet->clearUnitState(UNIT_STAT_FOLLOW); uint64 selguid = _player->GetSelection(); Unit *TargetUnit = ObjectAccessor::Instance().GetUnit(*_player, selguid); - if(TargetUnit == NULL) return; + if(TargetUnit == NULL) + return; - // not let attack friendly units. if( GetPlayer()->IsFriendlyTo(TargetUnit)) return; - if(pet->getVictim() && TargetUnit!=pet->getVictim()) - pet->AttackStop(); + if(((Creature*)pet)->AI()) + ((Creature*)pet)->AI()->AttackStart(TargetUnit); - if(pet->GetTypeId() != TYPEID_PLAYER) + break; + //TODO: check this out and add it correctely + /*if(pet->GetTypeId() != TYPEID_PLAYER) { - pet->GetMotionMaster()->Clear(); - if (((Creature*)pet)->AI()) - ((Creature*)pet)->AI()->AttackStart(TargetUnit); - //10% chance to play special pet attack talk, else growl if(((Creature*)pet)->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10) pet->SendPetTalk((uint32)PET_TALK_ATTACK); @@ -119,13 +125,8 @@ // 90% chance for pet and 100% chance for charmed creature pet->SendPetAIReaction(guid1); } - } - else // charmed player - { - pet->Attack(TargetUnit); - pet->SendPetAIReaction(guid1); - } - break; + break; + }*/ } case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) if(((Creature*)pet)->isPet()) @@ -151,7 +152,6 @@ case REACT_DEFENSIVE: //recovery case REACT_AGGRESSIVE: //activete charmInfo->SetReactState( ReactStates(spellid) ); - break; } break; case ACT_DISABLED: //0x8100 spell (disabled), ignore @@ -182,8 +182,6 @@ if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) return; - pet->clearUnitState(UNIT_STAT_FOLLOW); - Spell *spell = new Spell(pet, spellInfo, false, 0); int16 result = spell->PetCanCast(unit_target); @@ -218,10 +216,6 @@ if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) { - pet->clearUnitState(UNIT_STAT_FOLLOW); - if(unit_target!=pet->getVictim()) - pet->AttackStop(); - pet->GetMotionMaster()->Clear(); if (((Creature*)pet)->AI()) ((Creature*)pet)->AI()->AttackStart(unit_target); } @@ -569,8 +563,6 @@ if(!targets.read(&recvPacket,pet)) return; - pet->clearUnitState(UNIT_STAT_FOLLOW); - Spell *spell = new Spell(pet, spellInfo, false, 0); spell->m_targets = targets; Index: src/game/RandomMovementGenerator.cpp =================================================================== --- src/game/RandomMovementGenerator.cpp (revision 4763) +++ src/game/RandomMovementGenerator.cpp (working copy) @@ -22,124 +22,105 @@ #include "RandomMovementGenerator.h" #include "DestinationHolderImp.h" -/* -Here interpolation is disabled by default due to it causing crashing -in some compile environments. -If your server can handle the small amount of lag this may cause -and you can build without experiencing a significant increase in crashes -then you may uncomment the following line to have correct random motion. -*/ -// uncomment now for wide testing purpose -#define USE_INTERPOLATION +#define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV" -template<> -void -RandomMovementGenerator::Initialize(Creature &creature) +template<> void RandomMovementGenerator::_setRandomLocation(Creature &creature) { - float x,y,z,z2, wander_distance; - creature.GetRespawnCoord(x, y, z); - creature.GetRespawnDist(wander_distance); - uint32 mapid=creature.GetMapId(); + //Doing temporay Use of this Generator for Pet.... But still need it Own.. with Life Feature ;), GS + float X,Y,Z,z,nx,ny,nz,wander_distance; - Map const* map = MapManager::Instance().GetBaseMap(mapid); - // Initialization is done in bulk. Don’t use vamps for that (4. parameter = false). It is too costly when entering a new map grid - z2 = map->GetHeight(x,y,z, false); - if( fabs( z2 - z ) < 5 ) - z = z2; - - i_nextMove = 1; - i_waypoints[0][0] = x; - i_waypoints[0][1] = y; - i_waypoints[0][2] = z; - - bool is_water_ok = creature.isCanSwimOrFly(); - bool is_land_ok = creature.isCanWalkOrFly(); - - for(unsigned int idx=1; idx < MAX_RAND_WAYPOINTS+1; ++idx) + if( creature.isPet() ) { - const float angle = 2*M_PI*rand_norm(); - const float range = wander_distance*rand_norm(); + if( !creature.GetOwner() ) + return; + creature.GetOwner()->GetPosition(X, Y, Z); + wander_distance = (rand_norm()*PET_FOLLOW_DIST)+2.5f; + } + else + { + creature.GetRespawnCoord(X, Y, Z); + creature.GetRespawnDist(wander_distance); + } - i_waypoints[idx][0] = x+ range * cos(angle); - i_waypoints[idx][1] = y+ range * sin(angle); + z = creature.GetPositionZ(); + uint32 mapid=creature.GetMapId(); + Map const* map = MapManager::Instance().GetBaseMap(mapid); - // prevent invalid coordinates generation - MaNGOS::NormalizeMapCoord(i_waypoints[idx][0]); - MaNGOS::NormalizeMapCoord(i_waypoints[idx][1]); + //Both water and fly , will do later... since will be cool to select random Z and will need to check dist into 3d + //bool is_water_ok = creature.isCanSwimOrFly(); + //bool is_land_ok = creature.isCanWalkOrFly(); + const float angle = rand_norm()*(M_PI*2); + srand(getMSTime()); + const float range = rand_norm()*wander_distance; + const float distanceX = range * cos(angle); + const float distanceY = range * sin(angle); + const float distanceZ = range * tan(angle); + nx = X + distanceX; + ny = Y + distanceY; + //nz = Z + distanceZ; //3D-MG + nz = map->GetHeight(nx,ny,Z); + + //2D-MG + const float groundDist = sqrt(distanceX*distanceX + distanceY*distanceY); + const float zDist = fabs(z - nz) + 0.000001f; //FineTune with that value..., GS + const float slope = groundDist / zDist; + if(fabs(slope) < 1.0f || (map->GetTerrainType(nx,ny) & 0x01)) + nz = Z; - bool is_water = map->IsInWater(i_waypoints[idx][0],i_waypoints[idx][1],z); - // if generated wrong path just ignore - if( is_water && !is_water_ok || !is_water && !is_land_ok ) - { - i_waypoints[idx][0] = i_waypoints[idx-1][0]; - i_waypoints[idx][1] = i_waypoints[idx-1][1]; - i_waypoints[idx][2] = i_waypoints[idx-1][2]; - continue; - } + Traveller traveller(creature); + creature.SetOrientation(creature.GetAngle(nx,ny)); + i_destinationHolder.SetDestination(traveller, nx, ny, nz); + i_nextMoveTime.Reset(rand32(500+i_destinationHolder.GetTotalTravelTime(),5000+i_destinationHolder.GetTotalTravelTime())); + creature.addUnitState(UNIT_STAT_ROAMING); +} - // Initialization is done in bulk. Don’t use vamps for that (4. parameter = false). It is too costly when entering a new map grid - z2 = map->GetHeight(i_waypoints[idx][0],i_waypoints[idx][1],z, false); - if( fabs( z2 - z ) < 5 ) - z = z2; - i_waypoints[idx][2] = z; - } - i_nextMoveTime.Reset(urand(0, 10000-1)); // TODO: check the lower bound (it is probably too small) - creature.StopMoving(); +template<> void RandomMovementGenerator::Initialize(Creature &creature) +{ + if(!&creature || !creature.isAlive()) + return; + + creature.setMoveRunFlag(irand(0,RUNNING_CHANCE_RANDOMMV) ? false : true); + _setRandomLocation(creature); } -template<> -void -RandomMovementGenerator::Reset(Creature &creature) +template<> void RandomMovementGenerator::Reset(Creature &creature) { - i_nextMove = 1; - i_nextMoveTime.Reset(urand(0,10000-1)); // TODO: check the lower bound (it is probably too small) - creature.StopMoving(); + Initialize(creature); } -template<> -bool -RandomMovementGenerator::Update(Creature &creature, const uint32 &diff) +template<> bool RandomMovementGenerator::Update(Creature &creature, const uint32 &diff) { + //During a creature death... we will request at each 300Ms the delete of tha Motion... But he can be the index 0 + //Not creating any probleme... but suck cpu for absolute nothing if(!&creature) + return false; + + if(!creature.isAlive()) + { + i_nextMoveTime.Reset(3000); return true; - if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED)) - return true; + } + i_nextMoveTime.Update(diff); - #ifdef USE_INTERPOLATION + + if(i_destinationHolder.HasArrived() && !creature.IsStopped()) + creature.clearUnitState(UNIT_STAT_ROAMING); + + if(!i_destinationHolder.HasArrived() && creature.IsStopped()) + creature.addUnitState(UNIT_STAT_ROAMING); + CreatureTraveller traveller(creature); - i_destinationHolder.UpdateTraveller(traveller, diff, false); - #endif - if( i_nextMoveTime.Passed() ) + if( i_destinationHolder.UpdateTraveller(traveller, diff, false) ) { - if( creature.IsStopped() ) + if(i_nextMoveTime.Passed()) { - assert( i_nextMove <= MAX_RAND_WAYPOINTS ); - const float x = i_waypoints[i_nextMove][0]; - const float y = i_waypoints[i_nextMove][1]; - const float z = i_waypoints[i_nextMove][2]; - creature.addUnitState(UNIT_STAT_ROAMING); - CreatureTraveller traveller(creature); - i_destinationHolder.SetDestination(traveller, x, y, z); - #ifndef USE_INTERPOLATION - traveller.Relocation(x,y,z); - #endif - i_nextMoveTime.Reset( i_destinationHolder.GetTotalTravelTime() ); + creature.setMoveRunFlag(irand(0,20) ? false : true); + _setRandomLocation(creature); } - else + else if(creature.isPet() && creature.GetOwner() && creature.GetDistance(creature.GetOwner()) > PET_FOLLOW_DIST+2.5f) { - creature.StopMoving(); - creature.setMoveRunFlag(!urand(0,10)); - - ++i_nextMove; - if( i_nextMove == MAX_RAND_WAYPOINTS ) - { - i_nextMove = 0; - i_nextMoveTime.Reset(urand(0, 3000-1)); // TODO: check the lower bound (it is probably too small) - } - else - { - i_nextMoveTime.Reset(urand(0, 7000-1)); // TODO: check the lower bound (it is probably too small) - } + creature.setMoveRunFlag(true); + _setRandomLocation(creature); } } return true; Index: src/game/RandomMovementGenerator.h =================================================================== --- src/game/RandomMovementGenerator.h (revision 4763) +++ src/game/RandomMovementGenerator.h (working copy) @@ -23,7 +23,6 @@ #include "DestinationHolder.h" #include "Traveller.h" -#define MAX_RAND_WAYPOINTS 8 template class MANGOS_DLL_DECL RandomMovementGenerator @@ -32,14 +31,18 @@ public: RandomMovementGenerator(const Unit &) : i_nextMoveTime(0) {} + void _setRandomLocation(T &); void Initialize(T &); void Reset(T &); bool Update(T &, const uint32 &); - + void UpdateMapPosition(float &x ,float &y, float &z) + { + i_destinationHolder.GetLocationNow(x,y,z); + } MovementGeneratorType GetMovementGeneratorType() { return RANDOM_MOTION_TYPE; } private: - TimeTracker i_nextMoveTime; - float i_waypoints[MAX_RAND_WAYPOINTS+1][3]; + TimeTracker2 i_nextMoveTime; + //float i_waypoints[MAX_RAND_WAYPOINTS+1][3]; DestinationHolder< Traveller > i_destinationHolder; uint32 i_nextMove; }; Index: src/game/ReactorAI.cpp =================================================================== --- src/game/ReactorAI.cpp (revision 4763) +++ src/game/ReactorAI.cpp (working copy) @@ -41,19 +41,26 @@ } void -ReactorAI::AttackStart(Unit *p) +ReactorAI::AttackStart(Unit *unit) { - if(!p) + if( !unit ) return; - if(i_creature.Attack(p)) + if( unit->isTargetableForAttack() /*&& attacker!= m_creature, That should not happen*/ ) { - DEBUG_LOG("Tag unit GUID: %u (TypeId: %u) as a victim", p->GetGUIDLow(), p->GetTypeId()); - i_creature.AddThreat(p, 0.0f); - i_victimGuid = p->GetGUID(); - i_creature.GetMotionMaster()->Mutate(new TargetedMovementGenerator(*p)); - if (p->GetTypeId() == TYPEID_PLAYER) - i_creature.SetLootRecipient((Player*)p); + if ( i_creature.Attack(unit) ) + { + i_creature.AddThreat(unit, 0.0f); + selectedAttackRadius = i_creature.GetAttackDistance(unit); + i_creature.resetAttackTimer(); + + if (unit->GetTypeId() == TYPEID_PLAYER) + i_creature.SetLootRecipient((Player*)unit); + + AIMotionTarget(unit,FOLLOW_RANGE_MELEE); + + return; + } } } @@ -66,61 +73,86 @@ void ReactorAI::UpdateAI(const uint32 time_diff) { - // update i_victimGuid if i_creature.getVictim() !=0 and changed - if(!i_creature.SelectHostilTarget() || !i_creature.getVictim()) + if( i_creature.isDead() || i_creature.hasUnitState( UNIT_STAT_FLEEING ) ) return; - i_victimGuid = i_creature.getVictim()->GetGUID(); - - if( i_creature.IsWithinDistInMap(i_creature.getVictim(), ATTACK_DISTANCE)) - { - if( i_creature.isAttackReady() ) + //RunAway At friend + if( !i_creature.hasUnitState( UNIT_STAT_ROOT ) && selectedAttackRadius <= irand(0,40) && i_creature.getVictim() && Unit::getHpPercent(&i_creature)+irand(1,20) < Unit::getHpPercent(i_creature.getVictim()) ) + { + if( !i_creature.m_AssistenceStatus->requested ) { - i_creature.AttackerStateUpdate(i_creature.getVictim()); - i_creature.resetAttackTimer(); + i_creature.CastSpell( (Unit*)&i_creature, CREATURE_SPELL_TAP_FRIEND, false ); + i_creature.m_AssistenceStatus->requested = true; + return; } + //Check spell http://www.wowhead.com/?spell=32126, who is Flee Combat Much More Better, and keep fear when no combat + else if(i_creature.m_AssistenceStatus->obtained = false && Unit::getHpPercent(&i_creature) < irand(7,14) ) + i_creature.CastSpell( (Unit*)&i_creature, CREATURE_SPELL_RUNAWAY, false ,NULL,NULL, i_creature.GetUInt64Value(UNIT_FIELD_TARGET)); } -} + + if( i_creature.SelectHostilTarget() && i_creature.getVictim() && i_creature.isAttackReady() && i_creature.IsWithinDistInMap(i_creature.getVictim(), i_creature.GetFloatValue(UNIT_FIELD_COMBATREACH)) ) + { + //TODO:Add a Chance To win and Go to Friend. + i_creature.AttackerStateUpdate(i_creature.getVictim()); + i_creature.resetAttackTimer(); + lastAction = getMSTime(); + } -void -ReactorAI::EnterEvadeMode() -{ - if( !i_creature.isAlive() ) + //Wait Time Reset Action Selected, TODO: Change that to put more dynamic action at different timer. + //A text Chat can be done there, will be cool. + //Do search on wowhead for Gossip before any chat text system. + if(lastAction+irand(5000*CREATURE_WAIT_RATE,7000*CREATURE_WAIT_RATE) < getMSTime() ) { - DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_creature.GetGUIDLow()); - i_creature.GetMotionMaster()->MovementExpired(); - i_creature.GetMotionMaster()->Idle(); - i_victimGuid = 0; - i_creature.CombatStop(true); - i_creature.DeleteThreatList(); - return; + i_creature.m_AssistenceStatus->reset(); + + i_creature.SelectHostilTarget(); + + lastAction = getMSTime(); } +} - Unit* victim = ObjectAccessor::Instance().GetUnit(i_creature, i_victimGuid ); +void ReactorAI::EnterEvadeMode() +{ + lastAction = getMSTime()+30000; + i_creature.m_AssistenceStatus->reset(); //TODO: fix that scruct, at least make it proctected + if( !i_creature.isCharmed() ) + i_creature.RemoveAllAuras(); + i_creature.DeleteThreatList(); + i_creature.CombatStop(true); +} +void ReactorAI::AIMotionTarget(Unit* target, float offSet) +{ + bool exist = false; + MotionMaster* creatureMM = i_creature.GetMotionMaster(); - if( !victim ) + if(!target /*&& creatureMM->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE*/) { - DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_creature.GetGUIDLow()); + //creatureMM->MovementExpired(); + return; } - else if( victim->HasStealthAura() ) + //Here is wrong... TODO fix angle... but in fact i wan MV for Follower and One for Attacker. + float angle = NULL; + if( !i_creature.IsHostileTo(target) ) { - DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_creature.GetGUIDLow()); + angle = target->GetOrientation()*2.0f; } - else if( victim->isInFlight() ) + + if(creatureMM->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE) { - DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_creature.GetGUIDLow()); + TargetedMovementGenerator* targetMV = static_cast*> (creatureMM->top()); + if( targetMV && targetMV->GetTarget() ) + { + if( targetMV->GetTarget()->GetGUID() != target->GetGUID() ) + { + creatureMM->MovementExpired(false); + creatureMM->Mutate(new TargetedMovementGenerator(*target,offSet,angle)); + } + else if(targetMV->GetOffSet() != offSet) + targetMV->SetOffSet(offSet); + } } else - { - DEBUG_LOG("Creature stopped attacking due to target %s [guid=%u]", victim->isAlive() ? "out run him" : "is dead", i_creature.GetGUIDLow()); + { + i_creature.GetMotionMaster()->Mutate(new TargetedMovementGenerator(*target,offSet,angle)); } - - i_creature.RemoveAllAuras(); - i_creature.DeleteThreatList(); - i_victimGuid = 0; - i_creature.CombatStop(true); - - // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead - if( i_creature.GetMotionMaster()->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE ) - i_creature.GetMotionMaster()->TargetedHome(); } Index: src/game/ReactorAI.h =================================================================== --- src/game/ReactorAI.h (revision 4763) +++ src/game/ReactorAI.h (working copy) @@ -27,7 +27,7 @@ { public: - ReactorAI(Creature &c) : i_creature(c), i_victimGuid(0) {} + ReactorAI(Creature &c) : i_creature(c) {} void MoveInLineOfSight(Unit *); void AttackStart(Unit *); @@ -38,7 +38,9 @@ static int Permissible(const Creature *); private: + void AIMotionTarget(Unit* target, float offSet); + uint32 lastAction; + float selectedAttackRadius; Creature &i_creature; - uint64 i_victimGuid; }; #endif Index: src/game/ScriptCalls.cpp =================================================================== --- src/game/ScriptCalls.cpp (revision 4763) +++ src/game/ScriptCalls.cpp (working copy) @@ -41,8 +41,18 @@ { ScriptsSet testScript=new _ScriptSet; - std::string name = strlen(libName) ? libName : MANGOS_SCRIPT_NAME; + std::string name; + name = strlen(libName) ? libName : MANGOS_TIER_SCRIPT_NAME; name += MANGOS_SCRIPT_EXT; + + const char* TierScriptName = name.c_str(); + + if( !fopen( TierScriptName, "r") ) //Tier Script Not Present Taking Default MaNGOSScript.dll + { + printf("Error loading TIER Scripts Library %s !\n",name.c_str()); + name = strlen(libName) ? libName : MANGOS_SCRIPT_NAME; + name += MANGOS_SCRIPT_EXT; + } testScript->hScriptsLib=MANGOS_LOAD_LIBRARY(name.c_str()); Index: src/game/Spell.cpp =================================================================== --- src/game/Spell.cpp (revision 4763) +++ src/game/Spell.cpp (working copy) @@ -1480,6 +1480,8 @@ void Spell::SendSpellCooldown() { + if( m_caster->GetTypeId() == TYPEID_UNIT && !((Creature*)m_caster)->isPet() ) + ((Creature*)m_caster)->AddCreatureSpellCooldown(m_spellInfo->Id); if(m_caster->GetTypeId() != TYPEID_PLAYER) return; @@ -1639,9 +1641,11 @@ } // check if the player caster has moved before the spell finished - if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && - (m_castPositionX != m_caster->GetPositionX() || m_castPositionY != m_caster->GetPositionY() || m_castPositionZ != m_caster->GetPositionZ()) && - (m_spellInfo->Effect[0] != SPELL_EFFECT_STUCK || !((Player*)m_caster)->HasMovementFlags(MOVEMENTFLAG_FALLING)) ) + if( m_timer != 0 + && ( m_castPositionX != m_caster->GetPositionX() + || m_castPositionY != m_caster->GetPositionY() + || m_castPositionZ != m_caster->GetPositionZ() ) + && ( m_spellInfo->Effect[0] != SPELL_EFFECT_STUCK || ( m_caster->GetTypeId() == TYPEID_PLAYER && ((Player*)m_caster)->HasMovementFlags(MOVEMENTFLAG_FALLING))) ) { // always cancel for channeled spells if( m_spellState == SPELL_STATE_CASTING ) @@ -1686,6 +1690,12 @@ if( m_spellInfo->ChannelInterruptFlags & CHANNEL_FLAG_TURNING && m_castOrientation != m_caster->GetOrientation() ) cancel(); } + else + { + // check for incapacitating NPC states + if( m_caster->hasUnitState( UNIT_STAT_STUNDED | UNIT_STAT_CONFUSED ) ) + cancel(); + } // check if there are alive targets left for(int i=0;i<3;i++) @@ -2073,6 +2083,7 @@ { if(time == 0) { + m_timer = 0; //reset Channeling Timer, Should not be there, in place Must Check with State , but have to Be sure Deleyed and CASTING are never together. m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT,0); m_caster->SetUInt32Value(UNIT_CHANNEL_SPELL,0); } @@ -3117,7 +3128,7 @@ if(target && target != m_caster) { - float dist = m_caster->GetDistanceSq(target); + float dist = m_caster->GetDistance2dSq(target); if(dist > max_range * max_range) return SPELL_FAILED_OUT_OF_RANGE; //0x5A; if(dist < min_range * min_range) @@ -3129,7 +3140,7 @@ if(m_targets.m_targetMask == TARGET_FLAG_DEST_LOCATION && m_targets.m_destX != 0 && m_targets.m_destY != 0 && m_targets.m_destZ != 0) { - float dist = m_caster->GetDistanceSq(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ); + float dist = m_caster->GetDistance2d(m_targets.m_destX, m_targets.m_destY) * m_caster->GetDistance2d(m_targets.m_destX, m_targets.m_destY); if(dist > max_range * max_range) return SPELL_FAILED_OUT_OF_RANGE; if(dist < min_range * min_range) Index: src/game/Spell.h =================================================================== --- src/game/Spell.h (revision 4763) +++ src/game/Spell.h (working copy) @@ -577,6 +577,7 @@ CurrentSpellTypes GetCurrentContainer(); + uint32 GetTimer() { return m_timer; } Unit* GetCaster() { return m_caster; } Unit* GetOriginalCaster() { return m_originalCaster; } Index: src/game/SpellAuras.cpp =================================================================== --- src/game/SpellAuras.cpp (revision 4763) +++ src/game/SpellAuras.cpp (working copy) @@ -39,6 +39,8 @@ #include "Creature.h" #include "ConfusedMovementGenerator.h" #include "TargetedMovementGenerator.h" +#include "FleeingMovementGenerator.h" +#include "HomeMovementGenerator.h" #include "Formulas.h" #include "BattleGround.h" #include "BattleGroundAV.h" @@ -297,7 +299,7 @@ m_procCharges(0), m_spellmod(NULL), m_spellId(spellproto->Id), m_effIndex(eff), m_caster_guid(0), m_target(target), m_timeCla(1000), m_castItemGuid(castItem?castItem->GetGUID():0), m_auraSlot(MAX_AURAS), m_positive(false), m_permanent(false), m_isPeriodic(false), m_isTrigger(false), m_isAreaAura(false), m_isPersistent(false), -m_periodicTimer(0), m_PeriodicEventId(0), m_updated(false), m_removeOnDeath(false),m_fearMoveAngle(0) +m_periodicTimer(0), m_PeriodicEventId(0), m_updated(false), m_removeOnDeath(false),m_fearMoveAngle(0),m_fearMovementTimer(0) { assert(target); @@ -452,52 +454,7 @@ caster->ModifyPower(powertype,-manaPerSecond); caster->ModifyPower(powertype,-manaPerSecondPerLevel); } - if(caster && m_target->isAlive() && m_target->HasFlag(UNIT_FIELD_FLAGS,(UNIT_STAT_FLEEING<<16))) - { - int q = rand() % 80; - if(q == 8) m_fearMoveAngle += (float)(urand(45, 90)); - else if(q == 23) m_fearMoveAngle -= (float)(urand(45, 90)); - - // If the m_target is player,and if the speed is too slow,change it :P - float speed = m_target->GetSpeed(MOVE_RUN); - // Speed modifier, may need to find correct one - float mod = m_target->GetTypeId() != TYPEID_PLAYER ? 10 : 6; - float pos_x = m_target->GetPositionX(); - float pos_y = m_target->GetPositionY(); - uint32 mapid = m_target->GetMapId(); - float pos_z = MapManager::Instance().GetBaseMap(mapid)->GetHeight(pos_x,pos_y, m_target->GetPositionZ()); - // Control the max Distance; 28 for temp. - if(m_target->IsWithinDistInMap(caster, 28)) - { - float x = m_target->GetPositionX() - (speed*cosf(m_fearMoveAngle))/mod; - float y = m_target->GetPositionY() - (speed*sinf(m_fearMoveAngle))/mod; - float z = MapManager::Instance().GetBaseMap(mapid)->GetHeight(x,y, m_target->GetPositionZ()); - // Control the target to not climb or drop when dz > |x|,x = 1.3 for temp. - // fixed me if it needs checking when the position will be in water? - //+vmaps - if((z<=pos_z+1.3 && z>=pos_z-1.3) && m_target->IsWithinLOS(x,y,z)) - { - m_target->SendMonsterMove(x,y,z,0,true,(diff*2)); - if(m_target->GetTypeId() != TYPEID_PLAYER) - MapManager::Instance().GetMap(m_target->GetMapId(), m_target)->CreatureRelocation((Creature*)m_target,x,y,z,m_target->GetOrientation()); - } - else - { - //Complete the move only if z coord is now correct - m_fearMoveAngle += 120; - x = m_target->GetPositionX() + (speed*sinf(m_fearMoveAngle))/mod; - y = m_target->GetPositionY() + (speed*cosf(m_fearMoveAngle))/mod; - z = MapManager::Instance().GetBaseMap(mapid)->GetHeight(x,y, m_target->GetPositionZ()); - //+vmaps - if((z<=pos_z+1.3 && z>=pos_z-1.3) && m_target->IsWithinLOS(x,y,z)) - { - m_target->SendMonsterMove(x,y,z,0,true,(diff*2)); - if(m_target->GetTypeId() != TYPEID_PLAYER) - MapManager::Instance().GetMap(m_target->GetMapId(), m_target)->CreatureRelocation((Creature*)m_target,x,y,z,m_target->GetOrientation()); - } - } - } - } + //Better remove all this and create a runaway MV, and we simpli stack one over the other, GS } if(m_isPeriodic && (m_duration >= 0 || m_isPassive || m_permanent)) @@ -1761,8 +1718,7 @@ m_target->DeleteThreatList(); if(m_target->GetTypeId() == TYPEID_UNIT) { - m_target->StopMoving(); - m_target->GetMotionMaster()->Clear(); + m_target->GetMotionMaster()->Clear(false); m_target->GetMotionMaster()->Idle(); CharmInfo *charmInfo = ((Creature*)m_target)->InitCharmInfo(m_target); charmInfo->InitPossessCreateSpells(); @@ -1969,7 +1925,6 @@ if( apply ) { - m_target->addUnitState(UNIT_STAT_CONFUSED); m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED); if(m_target->GetTypeId() == TYPEID_PLAYER) @@ -2004,7 +1959,6 @@ } else { - m_target->clearUnitState(UNIT_STAT_CONFUSED); m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED); if(m_target->GetTypeId() == TYPEID_PLAYER) @@ -2019,15 +1973,7 @@ // only at real remove aura if(Real) { - m_target->GetMotionMaster()->MovementExpired(false); - if (m_target->GetTypeId() == TYPEID_UNIT) - { - Creature* c = (Creature*)m_target; - // if in combat restore movement generator - if(c->getVictim()) - c->GetMotionMaster()->Mutate(new TargetedMovementGenerator(*c->getVictim())); - } } } } @@ -2036,53 +1982,23 @@ { if( Apply ) { - m_target->addUnitState(UNIT_STAT_FLEEING); m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING); - - // only at real add aura + + //only at real add aura if(Real) { - //FIX ME: we need Mutate to now not existed FleeMovementGenerator (see Aura::Update hack code) - // at this moment Idle instead - m_target->GetMotionMaster()->MovementExpired(false); - m_target->GetMotionMaster()->Idle(); - - // what is this for ? (doesn't work anyway) - /*WorldPacket data(SMSG_DEATH_NOTIFY_OBSOLETE, 9); - data.append(m_target->GetPackGUID()); - data << uint8(0x00); - m_target->SendMessageToSet(&data,true);*/ + if (m_target->GetTypeId() == TYPEID_UNIT) + m_target->GetMotionMaster()->Mutate(new FleeingMovementGenerator(*m_target,m_caster_guid) ); + else + m_target->GetMotionMaster()->Mutate(new FleeingMovementGenerator(*m_target,m_caster_guid) ); } } else { - m_target->clearUnitState(UNIT_STAT_FLEEING); m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING); - // only at real remove aura if(Real) { - m_target->GetMotionMaster()->MovementExpired(false); - - if(m_target->GetTypeId() != TYPEID_PLAYER) - { - // restore appropriate movement generator - if(m_target->getVictim()) - m_target->GetMotionMaster()->Mutate(new TargetedMovementGenerator(*m_target->getVictim())); - else - m_target->GetMotionMaster()->Initialize(); - - // attack caster if can - Unit* caster = GetCaster(); - if(caster && caster != m_target->getVictim()) - ((Creature*)m_target)->AI()->AttackStart(caster); - } - - // what is this for ? (doesn't work anyway) - /*WorldPacket data(SMSG_DEATH_NOTIFY_OBSOLETE, 9); - data.append(m_target->GetPackGUID()); - data << uint8(0x01); - m_target->SendMessageToSet(&data,true);*/ } } } @@ -2156,7 +2072,7 @@ if (apply) { m_target->addUnitState(UNIT_STAT_STUNDED); - m_target->SetUInt64Value (UNIT_FIELD_TARGET, 0); + //m_target->SetUInt64Value (UNIT_FIELD_TARGET, 0); m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); if (caster) @@ -2174,14 +2090,16 @@ // Creature specific if(m_target->GetTypeId() != TYPEID_PLAYER) { + WorldPacket data(SMSG_FORCE_MOVE_ROOT, 8); + + data.append(m_target->GetPackGUID()); + data << uint32(0); + m_target->SendMessageToSet(&data,true); + } + else + { ((Creature *)m_target)->StopMoving(); } - - WorldPacket data(SMSG_FORCE_MOVE_ROOT, 8); - - data.append(m_target->GetPackGUID()); - data << uint32(0); - m_target->SendMessageToSet(&data,true); } else { @@ -2194,11 +2112,16 @@ if(m_target->getVictim() && m_target->isAlive()) m_target->SetUInt64Value (UNIT_FIELD_TARGET,m_target->getVictim()->GetGUID() ); + //if(caster && m_target->isAlive()) + //m_target->SetUInt64Value (UNIT_FIELD_TARGET,GetCasterGUID()); - WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 8+4); - data.append(m_target->GetPackGUID()); - data << uint32(0); - m_target->SendMessageToSet(&data,true); + if(m_target->GetTypeId() == TYPEID_PLAYER) + { + WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 8+4); + data.append(m_target->GetPackGUID()); + data << uint32(0); + m_target->SendMessageToSet(&data,true); + } if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_HUNTER && GetSpellProto()->SpellIconID == 1721) { if( !caster || caster->GetTypeId()!=TYPEID_PLAYER ) @@ -2386,14 +2309,16 @@ if (apply) { m_target->addUnitState(UNIT_STAT_ROOT); - m_target->SetUInt64Value (UNIT_FIELD_TARGET, 0); + //m_target->SetUInt64Value (UNIT_FIELD_TARGET, 0); // probably wrong - m_target->SetFlag(UNIT_FIELD_FLAGS,(apply_stat<<16)); + //m_target->SetFlag(UNIT_FIELD_FLAGS,(apply_stat<<16)); //Save last orientation if( m_target->getVictim() ) m_target->SetOrientation(m_target->GetAngle(m_target->getVictim())); + //if (caster) + // m_target->SetOrientation(m_target->GetAngle(caster)); if(m_target->GetTypeId() == TYPEID_PLAYER) { WorldPacket data(SMSG_FORCE_MOVE_ROOT, 10); @@ -2402,7 +2327,9 @@ m_target->SendMessageToSet(&data,true); } else + { ((Creature *)m_target)->StopMoving(); + } } else { @@ -2414,6 +2341,9 @@ // probably wrong m_target->RemoveFlag(UNIT_FIELD_FLAGS,(apply_stat<<16)); + //m_target->RemoveFlag(UNIT_FIELD_FLAGS,(apply_stat<<16)); + //if(caster && m_target->isAlive()) // set creature facing on root effect if alive + //m_target->SetUInt64Value (UNIT_FIELD_TARGET,GetCasterGUID()); if(m_target->getVictim() && m_target->isAlive()) m_target->SetUInt64Value (UNIT_FIELD_TARGET,m_target->getVictim()->GetGUID() ); Index: src/game/SpellAuras.h =================================================================== --- src/game/SpellAuras.h (revision 4763) +++ src/game/SpellAuras.h (working copy) @@ -264,6 +264,7 @@ uint64 m_caster_guid; Unit* m_target; int32 m_maxduration; + uint16 m_fearMovementTimer; int32 m_duration; int32 m_timeCla; // it is NOT safe to keep a pointer to the item because it may get deleted Index: src/game/SpellEffects.cpp =================================================================== --- src/game/SpellEffects.cpp (revision 4763) +++ src/game/SpellEffects.cpp (working copy) @@ -53,6 +53,8 @@ #include "BattleGroundWS.h" #include "VMapFactory.h" #include "Language.h" +#include "TargetedMovementGenerator.h" +#include "IdleMovementGenerator.h" pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= { @@ -1233,7 +1235,11 @@ sLog.outDebug("Spell: Aura is: %u", m_spellInfo->EffectApplyAuraName[i]); - Aura* Aur = new Aura(m_spellInfo, i, &m_currentBasePoints[i], unitTarget,m_caster, m_CastItem); + Aura* Aur; + if( m_originalCaster ) + Aur = new Aura(m_spellInfo, i, &m_currentBasePoints[i], unitTarget, m_originalCaster, m_CastItem); + else + Aur = new Aura(m_spellInfo, i, &m_currentBasePoints[i], unitTarget, m_caster, m_CastItem); if (!Aur->IsPositive() && Aur->GetCasterGUID() != Aur->GetTarget()->GetGUID()) { @@ -3073,11 +3079,14 @@ switch(m_spellInfo->Id) { // Keep in top, because core use it for npc aggro other npc - case SPELL_ID_AGGRO: + case CREATURE_SPELL_TAP_FRIEND: { if( !m_caster || m_caster->GetTypeId() != TYPEID_UNIT || !m_caster->getVictim() ) return; + // Should be == to diff from Home Location to Location, with a maximun of 50 like, GS + //So we can bring her too far away from original location, GS + // Need to readjuste, AnyAssistCreatureInRangeCheck to be more smart, Like select Strong First, GS float radius = sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS); if(radius > 0) { @@ -3097,16 +3106,14 @@ CellLock cell_lock(cell, p); cell_lock->Visit(cell_lock, grid_creature_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)); } - - for(std::list::iterator iter = assistList.begin(); iter != assistList.end(); ++iter) + std::list::iterator firstTarget = assistList.begin(); + if( firstTarget != assistList.end() ) { - ((Creature*)m_caster)->SetNoCallAssistence(true); - (*iter)->SetNoCallAssistence(true); - if((*iter)->AI()) - (*iter)->AI()->AttackStart(m_caster->getVictim()); + //TODO:Create Bondary Range Check into Object..., GS + m_caster->GetMotionMaster()->Mutate(new IdleMovementGenerator(*m_caster,false,1000)); + m_caster->GetMotionMaster()->Mutate(new TargetedMovementGenerator( *(*firstTarget),(*firstTarget)->GetObjectSize()+m_caster->GetObjectSize(),(rand_norm()*(M_PI*2)),true )); } } - return; } @@ -3764,15 +3771,17 @@ return; float x, y, z; - unitTarget->GetContactPoint(m_caster, x, y, z); - if(unitTarget->GetTypeId() != TYPEID_PLAYER) - ((Creature *)unitTarget)->StopMoving(); - + unitTarget->GetPosition( x, y, z); m_caster->SendMonsterMove(x, y, z, 0, true,1); - if(m_caster->GetTypeId() != TYPEID_PLAYER) + if(m_caster->GetMotionMaster()->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE) + { + m_caster->Relocate(x, y, z); + m_caster->GetMotionMaster()->top()->Initialize(*m_caster); + } + else if(m_caster->GetTypeId() == TYPEID_UNIT) MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)->CreatureRelocation((Creature*)m_caster,x,y,z,m_caster->GetOrientation()); + m_caster->Attack(unitTarget,true); - m_caster->Attack(unitTarget,true); } void Spell::EffectSummonCritter(uint32 i) Index: src/game/TargetedMovementGenerator.cpp =================================================================== --- src/game/TargetedMovementGenerator.cpp (revision 4763) +++ src/game/TargetedMovementGenerator.cpp (working copy) @@ -22,6 +22,7 @@ #include "Creature.h" #include "MapManager.h" #include "DestinationHolderImp.h" +#include "Spell.h" #define SMALL_ALPHA 0.05f @@ -39,129 +40,122 @@ }; */ -template -void -TargetedMovementGenerator::_setTargetLocation(T &owner) +template void TargetedMovementGenerator::_setTargetLocation(T &owner) { - if( !&i_target || !&owner ) + if( !&i_target || !&owner || !i_target.isValid()) //Added To make Script.dll don't crash into DEBUG Mode return; - if( owner.hasUnitState(UNIT_STAT_ROOT) || owner.hasUnitState(UNIT_STAT_STUNDED) ) - return; - - // prevent redundant micro-movement for pets, other followers. - if(i_offset && i_target->IsWithinDistInMap(&owner,2*i_offset)) - return; - float x, y, z; - if(!i_offset) - { - // to nearest contact position - i_target->GetContactPoint( &owner, x, y, z ); - } - else - { - // to at i_offset distance from target and i_angle from target facing - i_target->GetClosePoint(x,y,z,owner.GetObjectSize() + i_offset,i_angle); - } + + const float minReachRadius = owner.GetObjectSize() + (owner.GetFloatValue(UNIT_FIELD_COMBATREACH)/1.75); + i_offset = i_offset < minReachRadius ? minReachRadius : i_offset; - //We don't update Mob Movement, if the difference between New destination and last destination is < BothObjectSize - float bothObjectSize = i_target->GetObjectSize() + owner.GetObjectSize() + CONTACT_DISTANCE; - if( i_destinationHolder.HasDestination() && i_destinationHolder.GetDestinationDiff(x,y,z) < bothObjectSize ) - return; + //absAngle should herite PositionCollision + float const absAngle = owner.GetAngle(i_target.getTarget()); + x = i_target.getTarget()->GetPositionX() + ((i_offset+i_target.getTarget()->GetObjectSize()) * cos(absAngle)); + y = i_target.getTarget()->GetPositionY() + ((i_offset+i_target.getTarget()->GetObjectSize()) * sin(absAngle)); + z = i_target.getTarget()->GetPositionZ(); + Traveller traveller(owner); + owner.SetOrientation(owner.GetAngle(x,y)); i_destinationHolder.SetDestination(traveller, x, y, z); owner.addUnitState(UNIT_STAT_CHASE); } -template -void -TargetedMovementGenerator::Initialize(T &owner) +template void TargetedMovementGenerator::Initialize(T &owner) { if(!&owner) return; + + //Will have to change that..., so we can dynamic check for Best walk or run, GS + //TODO: AIMotionMaster after spell Casting Walk.... and Lower range, GS owner.setMoveRunFlag(true); _setTargetLocation(owner); } - -template -void -TargetedMovementGenerator::Reset(T &owner) +template void TargetedMovementGenerator::Reset(T &owner) { Initialize(owner); } -template -bool -TargetedMovementGenerator::Update(T &owner, const uint32 & time_diff) +template bool TargetedMovementGenerator::Update(T &owner, const uint32 & time_diff) { - if(!i_target.isValid()) - { + //Hum that is cause ReferenceFollower... Not sure yet again if we really need that... since we should search allways for XYZ + // and fuck the target.... so target should never never appear here... only XYZ + //TODO: Change the way Targeted Work and make it: FollowerMovementGenerator(), ChasingMovementGenerator() + //For the moment is working much better then actual but ... is more like little finetune here and there. + //We doo at the end with Path Searching...., GS + + //ReferenceFollower are no more needed i think so, if we keep that Generator only for Victim... :) + //if( owner.getVictim() != i_target.getTarget() ) + // return false; + + if(!i_target.isValid() || !&owner || !owner.isAlive() ) return false; - } - if( !&owner || !owner.isAlive() || !&i_target ) - return true; - if( owner.hasUnitState(UNIT_STAT_ROOT) || owner.hasUnitState(UNIT_STAT_STUNDED) || owner.hasUnitState(UNIT_STAT_FLEEING)) - return true; - if( !owner.isInCombat() && !owner.hasUnitState(UNIT_STAT_FOLLOW) ) - { - //owner.AIM_Initialize(); This case must be the one, when a creature aggroed you. By Initalized a new AI, we prevented to Ai::_stopAttack() to be executed properly. - return true; - } - // prevent crash after creature killed pet - if (!owner.hasUnitState(UNIT_STAT_FOLLOW) && owner.getVictim() != i_target.getTarget()) - return true; + if( !owner.GetUInt32Value(UNIT_FIELD_TARGET)) + return false; + //To Debug why sometime Mangos screw: UNIT_FIELD_TARGET, GS + if( owner.getVictim() && i_target.getTarget() && owner.GetUInt64Value(UNIT_FIELD_TARGET) != i_target.getTarget()->GetGUID() ) + owner.GetUInt64Value(UNIT_FIELD_TARGET); + + // prevent crash after creature killed pet //HEIN??? , GS + //if( !owner.hasUnitState(UNIT_STAT_FOLLOW) && owner.getVictim() != i_target.getTarget()) + //return true; + Traveller traveller(owner); if( !i_destinationHolder.HasDestination() ) _setTargetLocation(owner); + if( owner.IsStopped() && !i_destinationHolder.HasArrived() ) - { owner.addUnitState(UNIT_STAT_CHASE); - i_destinationHolder.StartTravel(traveller); - return true; - } if (i_destinationHolder.UpdateTraveller(traveller, time_diff, false)) { - // put targeted movement generators on a higher priority - if (owner.GetObjectSize()) - i_destinationHolder.ResetUpdate(50); + // put targeted movement generators on a higher priority, No way Man, GS + i_destinationHolder.ResetUpdate(100); + //DEBUG value, GS + float _asdf = sqrt(i_destinationHolder.GetDistanceFromDest2dSq(*i_target.getTarget())); + float _asdf2 = owner.GetFloatValue(UNIT_FIELD_COMBATREACH); + float _asdf3 = i_target->GetObjectSize() + owner.GetFloatValue(UNIT_FIELD_COMBATREACH) + owner.GetObjectSize() + i_offset; - float dist = i_target->GetObjectSize() + owner.GetObjectSize() + CONTACT_DISTANCE; - - // try to counter precision differences - if( i_destinationHolder.GetDistanceFromDestSq(*i_target.getTarget()) > dist * dist + 0.8) - { - owner.SetInFront(i_target.getTarget()); // Set new Angle For Map:: - _setTargetLocation(owner); //Calculate New Dest and Send data To Player + // try to counter precision differences, + float targetDistToDest = sqrt(i_destinationHolder.GetDistanceFromDest2dSq(*i_target.getTarget())) - (i_target->GetObjectSize() + owner.GetFloatValue(UNIT_FIELD_COMBATREACH) + owner.GetObjectSize() + i_offset); + // -0.5 should be Bounding radius of this.... but for test use that one... be sure NPC move if your too close. + if( targetDistToDest > -0.5f && (i_destinationHolder.HasArrived() || i_destinationHolder.GetTimeToDest() > TRAVELLER_UPDATE_INTERVAL) ) + { + float x,y,z; + i_destinationHolder.GetLocationNow(x,y,z); + traveller.Relocation(x,y,z); + _setTargetLocation(owner); } - // Update the Angle of the target only for Map::, no need to send packet for player - else if ( !i_angle && !owner.HasInArc( 0.01f, i_target.getTarget() ) ) - owner.SetInFront(i_target.getTarget()); + //Angle update will be made with Operator Overload into GetOrientation() + //For fast Angle Update during fight, MeanMachine Rocking idea :), GS if( !owner.IsStopped() && i_destinationHolder.HasArrived()) { - //Angle update will take place into owner.StopMoving() + owner.clearUnitState(UNIT_STAT_MOVING); owner.SetInFront(i_target.getTarget()); - - owner.StopMoving(); - if(owner.canReachWithAttack(i_target.getTarget()) && !owner.hasUnitState(UNIT_STAT_FOLLOW)) - owner.Attack(i_target.getTarget()); + if(i_forceExpireAtDest) + return false; } } return true; } -template -Unit* -TargetedMovementGenerator::GetTarget() const +template Unit* TargetedMovementGenerator::GetTarget() const { return i_target.getTarget(); } - +template float TargetedMovementGenerator::GetOffSet() const +{ + return i_offset; +} +template void TargetedMovementGenerator::SetOffSet(float newOffSet) +{ + i_offset = *new float(newOffSet); +} template void TargetedMovementGenerator::_setTargetLocation(Player &); template void TargetedMovementGenerator::_setTargetLocation(Creature &); template void TargetedMovementGenerator::Initialize(Player &); @@ -172,3 +166,7 @@ template bool TargetedMovementGenerator::Update(Creature &, const uint32 &); template Unit* TargetedMovementGenerator::GetTarget() const; template Unit* TargetedMovementGenerator::GetTarget() const; +template float TargetedMovementGenerator::GetOffSet() const; +template float TargetedMovementGenerator::GetOffSet() const; +template void TargetedMovementGenerator::SetOffSet(float newOffSet); +template void TargetedMovementGenerator::SetOffSet(float newOffSet); Index: src/game/TargetedMovementGenerator.h =================================================================== --- src/game/TargetedMovementGenerator.h (revision 4763) +++ src/game/TargetedMovementGenerator.h (working copy) @@ -39,10 +39,10 @@ { public: - TargetedMovementGenerator(Unit &target) - : TargetedMovementGeneratorBase(target), i_offset(0), i_angle(0) {} - TargetedMovementGenerator(Unit &target, float offset, float angle) - : TargetedMovementGeneratorBase(target), i_offset(offset), i_angle(angle) {} + TargetedMovementGenerator(Unit &target, bool forceExpireAtDest = false) + : TargetedMovementGeneratorBase(target), i_offset(0), i_angle(0),i_forceExpireAtDest(forceExpireAtDest) {} + TargetedMovementGenerator(Unit &target, float offset, float angle, bool forceExpireAtDest = false) + : TargetedMovementGeneratorBase(target), i_offset(offset), i_angle(angle),i_forceExpireAtDest(forceExpireAtDest) {} ~TargetedMovementGenerator() {} void Initialize(T &); @@ -51,7 +51,12 @@ MovementGeneratorType GetMovementGeneratorType() { return TARGETED_MOTION_TYPE; } Unit* GetTarget() const; - + float GetOffSet() const; + void SetOffSet(float newOffSet); + void UpdateMapPosition(float &x ,float &y, float &z) + { + i_destinationHolder.GetLocationNow(x,y,z); + } bool GetDestination(float &x, float &y, float &z) { if(!i_destinationHolder.HasDestination()) return false; @@ -61,9 +66,10 @@ private: void _setTargetLocation(T &); - + float i_offset; float i_angle; + bool i_forceExpireAtDest; DestinationHolder< Traveller > i_destinationHolder; }; #endif Index: src/game/Traveller.h =================================================================== --- src/game/Traveller.h (revision 4763) +++ src/game/Traveller.h (working copy) @@ -47,6 +47,7 @@ inline float GetPositionX() const { return i_traveller.GetPositionX(); } inline float GetPositionY() const { return i_traveller.GetPositionY(); } inline float GetPositionZ() const { return i_traveller.GetPositionZ(); } + inline float GetOrientation() const { return i_traveller.GetOrientation(); } inline T& GetTraveller(void) { return i_traveller; } float Speed(void) { assert(false); return 0.0f; } @@ -71,7 +72,7 @@ template<> inline void Traveller::MoveTo(float x, float y, float z, uint32 t) { - i_traveller.AI_SendMoveToPacket(x, y, z, t, i_traveller.getMoveRunFlag(), 0); + i_traveller.AI_SendMoveToPacket(x, y, z, t, Speed(), 0); } // specialization for players Index: src/game/Unit.cpp =================================================================== --- src/game/Unit.cpp (revision 4763) +++ src/game/Unit.cpp (working copy) @@ -39,6 +39,12 @@ #include "Totem.h" #include "BattleGround.h" #include "MovementGenerator.h" +#include "TargetedMovementGenerator.h" +#include "ConfusedMovementGenerator.h" +#include "RandomMovementGenerator.h" +#include "WaypointMovementGenerator.h" +#include "HomeMovementGenerator.h" +#include "FleeingMovementGenerator.h" #include @@ -132,6 +138,7 @@ for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++) m_currentSpells[i] = NULL; + m_hasHealer = false; m_addDmgOnce = 0; m_TotemSlot[0] = m_TotemSlot[1] = m_TotemSlot[2] = m_TotemSlot[3] = 0; m_ObjectSlot[0] = m_ObjectSlot[1] = m_ObjectSlot[2] = m_ObjectSlot[3] = 0; @@ -302,17 +309,16 @@ SendMonsterMove(x,y,z,0,run,transitTime); } -void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, bool Run, uint32 Time) +void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, bool Run, uint32 Time, bool tryFlag512) { WorldPacket data( SMSG_MONSTER_MOVE, (41 + GetPackGUID().size()) ); data.append(GetPackGUID()); - // Point A, starting location data << GetPositionX() << GetPositionY() << GetPositionZ(); // unknown field - unrelated to orientation // seems to increment about 1000 for every 1.7 seconds // for now, we'll just use mstime - data << getMSTime(); + data << getMSTime()/* + ((Time / 1000) * 140)*/; //Simulate 140 allways Ping data << uint8(type); // unknown switch(type) @@ -323,14 +329,17 @@ SendMessageToSet( &data, true ); return; case 3: // not used currently - data << uint64(0); // probably target guid + data << uint64(GetUInt64Value(UNIT_FIELD_TARGET)); // probably target guid break; case 4: // not used currently data << float(0); // probably orientation break; } - data << uint32(Run ? 0x00000100 : 0x00000000); // flags (0x100 - running, 0x200 - taxi) + if(tryFlag512) + data << uint32(0x11111111); + else + data << uint32(Run ? 0x00000100 : 0x00000000); // flags (0x100 - running, 0x200 - taxi) /* Flags: 512: Floating, moving without walking/running */ @@ -383,6 +392,10 @@ return (!m_modAuras[auraType].empty()); } +bool Unit::HasSpellChannelActiveTimer() +{ + return m_currentSpells[CURRENT_GENERIC_SPELL] ? m_currentSpells[CURRENT_GENERIC_SPELL]->GetTimer() > 0 : false; +} /* Called by DealDamage for auras that have a chance to be dispelled on damage taken. */ void Unit::RemoveSpellbyDamageTaken(uint32 auraType, uint32 damage) { @@ -643,6 +656,9 @@ ((Creature*)pVictim)->AI()->JustDied(this); } + + m_hasHealer = false; + //judge if GainXP, Pet kill like player kill,kill pet not like PvP bool PvP = false; Player *player = NULL; @@ -877,7 +893,7 @@ if (damagetype != NODAMAGE) { - if(pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL] && pVictim->GetTypeId() == TYPEID_PLAYER && damage) + if(pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL] /*&& pVictim->GetTypeId() == TYPEID_PLAYER*/ && damage) { if (pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() == SPELL_STATE_CASTING) { @@ -2077,7 +2093,7 @@ } } - if (pVictim->GetTypeId() == TYPEID_PLAYER && *damage) + if (/*pVictim->GetTypeId() == TYPEID_PLAYER &&*/ *damage) { for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++) { @@ -4724,19 +4740,21 @@ //Set our target SetUInt64Value(UNIT_FIELD_TARGET, victim->GetGUID()); + SetInFront(victim); addUnitState(UNIT_STAT_ATTACKING); SetInCombat(); m_attacking = victim; m_attacking->_addAttacker(this); - if(m_attacking->GetTypeId()==TYPEID_UNIT && ((Creature*)m_attacking)->AI()) - ((Creature*)m_attacking)->AI()->AttackedBy(this); + //if(m_attacking->GetTypeId()==TYPEID_UNIT && ((Creature*)m_attacking)->AI()) + //((Creature*)m_attacking)->AI()->AttackedBy(this); //Heu.. don't understand why we need that??? - if( GetTypeId()==TYPEID_UNIT && !(((Creature*)this)->isPet() || isCharmed()) ) + /*if( GetTypeId()==TYPEID_UNIT && !(((Creature*)this)->isPet() || isCharmed()) ) { ((Creature*)this)->CallAssistence(); - } + }*/ + //if(!isAttackReady(BASE_ATTACK)) //resetAttackTimer(BASE_ATTACK); @@ -4766,11 +4784,12 @@ clearUnitState(UNIT_STAT_ATTACKING); InterruptSpell(CURRENT_MELEE_SPELL); - + m_hasHealer = false; // I don't wan that, I must get rid at the end.. system should be smooth enought so no need at all, GS + if( GetTypeId()==TYPEID_UNIT ) { - // reset call assistance - ((Creature*)this)->SetNoCallAssistence(false); + // reset assistance + ((Creature*)this)->m_AssistenceStatus->reset(); } SendAttackStop(victim); @@ -6338,13 +6357,12 @@ } // no target but something prevent go to evade mode + // !isInCombat(), Do EnterEvade Only if inCombat(), no need after + // HasAuraType(SPELL_AURA_MOD_TAUNT), Cause EnterEvadeTake Place here : Unit::TauntFadeOut(Unit *taunter) if( !isInCombat() || HasAuraType(SPELL_AURA_MOD_TAUNT) || !CanFreeMove() ) return false; - // last case when creature don't must go to evade mode: - // it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list - // for example at owner command to pet attack some far away creature - // Note: creature not have targeted movement generator but have attacker in this case + // this, is waiting the Attacker to come to aggro range, no yet Threat Created from Attacker, GS if( GetMotionMaster()->empty() || GetMotionMaster()->top()->GetMovementGeneratorType() != TARGETED_MOTION_TYPE ) { for(AttackerSet::const_iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr) @@ -7392,14 +7410,69 @@ void Unit::StopMoving() { + //Should Add other Moving MV and prepare for Compatibility + if( !hasUnitState(UNIT_STAT_MOVING)) + return; + + //Was for testing in begin but since is pretty good, must be updated, GS + //TODO: make that much more simple, and Support Player as Creature. + if( GetTypeId() == TYPEID_UNIT ) + { + MotionMaster* currentMotion = GetMotionMaster(); + float x,y,z; //Dummy Usage into that case but needed since we carry pointer. + switch( currentMotion->top()->GetMovementGeneratorType() ) + { + case IDLE_MOTION_TYPE: + { + break; + } + case TARGETED_MOTION_TYPE: + { + TargetedMovementGenerator* currentMV = static_cast*> (currentMotion->top()); + currentMV->UpdateMapPosition(x,y,z); + MapManager::Instance().GetMap(GetMapId(), this)->CreatureRelocation((Creature*)this, x, y, z, this->GetOrientation()); + break; + } + case CONFUSED_MOTION_TYPE: + { + ConfusedMovementGenerator* currentMV = static_cast*> (currentMotion->top()); + currentMV->UpdateMapPosition(x,y,z); + MapManager::Instance().GetMap(GetMapId(), this)->CreatureRelocation((Creature*)this, x, y, z, this->GetOrientation()); + break; + } + case FLEEING_MOTION_TYPE: + { + FleeingMovementGenerator* currentMV = static_cast*> (currentMotion->top()); + currentMV->UpdateMapPosition(x,y,z); + MapManager::Instance().GetMap(GetMapId(), this)->CreatureRelocation((Creature*)this, x, y, z, this->GetOrientation()); + break; + } + case HOME_MOTION_TYPE: + { + HomeMovementGenerator* currentMV = static_cast*> (currentMotion->top()); + currentMV->UpdateMapPosition(x,y,z); + MapManager::Instance().GetMap(GetMapId(), this)->CreatureRelocation((Creature*)this, x, y, z, this->GetOrientation()); + break; + } + case RANDOM_MOTION_TYPE: + { + RandomMovementGenerator* currentMV = static_cast*> (currentMotion->top()); + currentMV->UpdateMapPosition(x,y,z); + MapManager::Instance().GetMap(GetMapId(), this)->CreatureRelocation((Creature*)this, x, y, z, this->GetOrientation()); + break; + } + case WAYPOINT_MOTION_TYPE: + { + WaypointMovementGenerator* currentMV = static_cast*> (currentMotion->top()); + currentMV->UpdateMapPosition(x,y,z); + MapManager::Instance().GetMap(GetMapId(), this)->CreatureRelocation((Creature*)this, x, y, z, this->GetOrientation()); + break; + } + } + } clearUnitState(UNIT_STAT_MOVING); // send explicit stop packet - SendMonsterMove(GetPositionX(), GetPositionY(), GetPositionZ(),0,true,0); - - // update position and orientation; - WorldPacket data; - BuildHeartBeatMsg(&data); - SendMessageToSet(&data,false); + SendMonsterMove(GetPositionX(), GetPositionY(), GetPositionZ(),1,true,0); } bool Unit::IsStandState() const @@ -7409,4 +7482,4 @@ s != PLAYER_STATE_SIT_MEDIUM_CHAIR && s != PLAYER_STATE_SIT_HIGH_CHAIR && s != PLAYER_STATE_SIT && s != PLAYER_STATE_SLEEP && s != PLAYER_STATE_KNEEL; -} \ No newline at end of file +} Index: src/game/Unit.h =================================================================== --- src/game/Unit.h (revision 4763) +++ src/game/Unit.h (working copy) @@ -314,9 +314,9 @@ UNIT_STAT_STUNDED = 8, UNIT_STAT_ROAMING = 16, UNIT_STAT_CHASE = 32, - UNIT_STAT_SEARCHING = 64, + UNIT_STAT_SEARCHING = 64, //Creature is Moving but no MILOS called to Her... example: She go Home first 25% distance reached creature has SEARCHING, the rest will have normal ROAMING UNIT_STAT_FLEEING = 128, - UNIT_STAT_MOVING = (UNIT_STAT_ROAMING | UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING), + UNIT_STAT_MOVING = (UNIT_STAT_ROAMING | UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING | 2048 | 256 | 512), UNIT_STAT_IN_FLIGHT = 256, // player is in flight mode UNIT_STAT_FOLLOW = 512, UNIT_STAT_ROOT = 1024, @@ -326,6 +326,9 @@ enum UnitMoveType { + //All that is very important for prediction im gonna do... + //I will allways call for bigger Motion and will check if i can stop from there, we do reverse. + //Angle will be very important... since a creature will target right in front a player + 5 yard is player is MOVE WALK or RUN, so creature will allways act as intercept and not chasing retared.. MOVE_WALK = 0, MOVE_RUN = 1, MOVE_WALKBACK = 2, @@ -907,6 +910,7 @@ { return m_Auras.find(spellEffectPair(spellId, effIndex)) != m_Auras.end(); } bool virtual HasSpell(uint32 /*spellID*/) const { return false; } + bool HasSpellChannelActiveTimer(); bool HasStealthAura() const { return HasAuraType(SPELL_AURA_MOD_STEALTH); } bool HasInvisibilityAura() const { return HasAuraType(SPELL_AURA_MOD_INVISIBILITY); } @@ -937,7 +941,7 @@ void SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchools DamageType, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount); void SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, SpellSchools DamageType,uint32 AbsorbedDamage, uint32 Resist,bool PhysicalDamage, uint32 Blocked, bool CriticalHit = false); - void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, bool Run, uint32 Time); + void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, bool Run, uint32 Time, bool tryFlag512 = false); virtual void MoveOutOfRange(Player &) { }; @@ -1022,6 +1026,7 @@ void InterruptNonMeleeSpells(bool withDelayed); Spell* m_currentSpells[CURRENT_MAX_SPELL]; + bool m_hasHealer; uint32 m_addDmgOnce; uint64 m_TotemSlot[4]; uint64 m_ObjectSlot[4]; @@ -1152,7 +1157,27 @@ bool IsStopped() const { return !(hasUnitState(UNIT_STAT_MOVING)); } void StopMoving(); - + + //AI Improuved Function + const bool hasFriendTarget() + { + return ( !getVictim() && GetUInt64Value(UNIT_FIELD_TARGET) ); + } + const bool hasVictim() + { + return getVictim() ? true : false; + } + static const uint8 getHpPercent(Unit* unit) + { + return unit->GetMaxHealth() ? uint8( (float)unit->GetHealth() / (float)unit->GetMaxHealth() * 100 ) : 0; + } + static const uint8 getMpPercent(Unit* unit) + { + return unit->GetMaxPower( POWER_MANA) ? uint8((float)unit->GetPower(POWER_MANA) / (float)unit->GetMaxPower(POWER_MANA) * 100 ) : 0; + } + void DoDoSay(const char *text, ...); //Debug Function Should get Removed, GS + // END AI Improuved Feature + void setMoveRunFlag(bool f) { m_moveRun = f; } bool getMoveRunFlag() const { return m_moveRun; } Index: src/game/WaypointMovementGenerator.cpp =================================================================== --- src/game/WaypointMovementGenerator.cpp (revision 4763) +++ src/game/WaypointMovementGenerator.cpp (working copy) @@ -159,6 +159,8 @@ { QueryResult *result = WorldDatabase.Query("SELECT distinct(`id`) as uniqueid FROM `creature_movement`"); + //Need to redone everything here... MeanMachine has created a patch for this.... , GS + //Badly need MeanMachine Patch + add my change to it. if( result ) { do @@ -181,8 +183,8 @@ // Waypoint movement can be switched on/off // This is quite handy for escort quests and other stuff - if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED)) - return true; + //if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED)) + //return true; // prevent crash at empty waypoint path. if(i_path.Empty()) @@ -373,34 +375,39 @@ bool FlightPathMovementGenerator::Update(Player &player, const uint32 &diff) { - if( MovementInProgress() ) - { + //if( MovementInProgress() ) + //{ Traveller traveller(player); - if( i_destinationHolder.UpdateTraveller(traveller, diff, false) ) - { - i_destinationHolder.ResetUpdate(FLIGHT_TRAVEL_UPDATE); + i_destinationHolder.UpdateTraveller(traveller, diff, false); + //if( i_destinationHolder.UpdateTraveller(traveller, diff, false) ) + //{ + //i_destinationHolder.ResetUpdate(FLIGHT_TRAVEL_UPDATE); if( i_destinationHolder.HasArrived() ) { + if(i_path.Size() <= i_currentNode+1) + { + EndFlight(player); + return false; + } ++i_currentNode; - if( MovementInProgress() ) - { - DEBUG_LOG("loading node %u for player %s", i_currentNode, player.GetName()); + //if( MovementInProgress() ) + //{ + //DEBUG_LOG("loading node %u for player %s", i_currentNode, player.GetName()); // do not send movement, it was sent already i_destinationHolder.SetDestination(traveller, i_path[i_currentNode].x, i_path[i_currentNode].y, i_path[i_currentNode].z, false); - return true; - } + //} //else HasArrived() } - else - return true; - } - else - return true; - } - + //else + //return true; + //} + //else + // return true; + //} + return true; // we have arrived at the end of the path - EndFlight(player); - return false; + //EndFlight(player); + //return false; } void Index: src/game/WaypointMovementGenerator.h =================================================================== --- src/game/WaypointMovementGenerator.h (revision 4763) +++ src/game/WaypointMovementGenerator.h (working copy) @@ -67,7 +67,10 @@ { return i_currentNode; } - + void UpdateMapPosition(float &x ,float &y, float &z) + { + i_destinationHolder.GetLocationNow(x,y,z); + } protected: uint32 i_currentNode; DestinationHolder< Traveller > i_destinationHolder; Index: src/game/World.cpp =================================================================== --- src/game/World.cpp (revision 4763) +++ src/game/World.cpp (working copy) @@ -380,7 +380,7 @@ m_configs[CONFIG_GROUP_XP_DISTANCE] = m_configs[CONFIG_GROUP_XP_DISTANCE]*m_configs[CONFIG_GROUP_XP_DISTANCE]; /// \todo Add MonsterSight and GuarderSight (with meaning) in mangosd.conf or put them as define m_configs[CONFIG_SIGHT_MONSTER] = sConfig.GetIntDefault("MonsterSight", 400); - m_configs[CONFIG_SIGHT_GUARDER] = sConfig.GetIntDefault("GuarderSight", 500); + m_configs[CONFIG_SIGHT_GUARDER] = sConfig.GetIntDefault("GuarderSight", 800); m_configs[CONFIG_GAME_TYPE] = sConfig.GetIntDefault("GameType", 0); m_configs[CONFIG_ALLOW_TWO_SIDE_ACCOUNTS] = sConfig.GetIntDefault("AllowTwoSide.Accounts", 0); m_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT] = sConfig.GetIntDefault("AllowTwoSide.Interaction.Chat",0); @@ -633,6 +633,9 @@ sLog.outString( "Loading Creature Respawn Data..." ); // must be after PackInstances() objmgr.LoadCreatureRespawnTimes(); + sLog.outString( "Loading Creature SpellBook Data..." ); + objmgr.LoadCreatureSpellBook(); + sLog.outString( "Loading Gameobject Data..." ); objmgr.LoadGameobjects(); Index: src/shared/Database/DBCStores.cpp =================================================================== --- src/shared/Database/DBCStores.cpp (revision 4763) +++ src/shared/Database/DBCStores.cpp (working copy) @@ -819,3 +819,5 @@ // script support functions MANGOS_DLL_SPEC DBCStorage const* GetSpellStore() { return &sSpellStore; } MANGOS_DLL_SPEC DBCStorage const* GetSpellRangeStore() { return &sSpellRangeStore; } +MANGOS_DLL_SPEC DBCStorage const* GetSpellDurationStore() { return &sSpellDurationStore; } +MANGOS_DLL_SPEC DBCStorage const* GetCastTimesStore() { return &sCastTimesStore; } Index: src/shared/Database/DBCStores.h =================================================================== --- src/shared/Database/DBCStores.h (revision 4763) +++ src/shared/Database/DBCStores.h (working copy) @@ -202,6 +202,8 @@ void LoadDBCStores(std::string dataPath); // script support functions -MANGOS_DLL_SPEC DBCStorage const* GetSpellStore() ; -MANGOS_DLL_SPEC DBCStorage const* GetSpellRangeStore() ; +MANGOS_DLL_SPEC DBCStorage const* GetSpellStore(); +MANGOS_DLL_SPEC DBCStorage const* GetSpellRangeStore(); +MANGOS_DLL_SPEC DBCStorage const* GetSpellDurationStore(); +MANGOS_DLL_SPEC DBCStorage const* GetCastTimesStore(); #endif Index: src/shared/Timer.h =================================================================== --- src/shared/Timer.h (revision 4763) +++ src/shared/Timer.h (working copy) @@ -74,4 +74,13 @@ time_t GetExpiry(void) const { return i_expiryTime; } time_t i_expiryTime; }; + +struct TimeTracker2 +{ + TimeTracker2(int32 expiry) : i_expiryTime(expiry) {} + void Update(int32 diff) { i_expiryTime -= diff; } + bool Passed(void) const { return (i_expiryTime <= 0); } + void Reset(int32 interval) { i_expiryTime = interval; } + int32 i_expiryTime; +}; #endif Index: src/shared/Util.h =================================================================== --- src/shared/Util.h (revision 4763) +++ src/shared/Util.h (working copy) @@ -36,6 +36,7 @@ /* Return a random number in the range min..max; (max-min) must be smaller than 32768. * Note: Not reentrant - if two threads call this simultaneously, they will likely * get the same random number. */ +MANGOS_DLL_SPEC extern int32 irand(int32 min, int32 max); /* Return a random number in the range min..max; (max-min) must be smaller than 32768. @@ -52,11 +53,13 @@ /* Return a random number in the range 0 .. RAND32_MAX. * Note: Not reentrant - if two threads call this simultaneously, they will likely * get the same random number. */ +MANGOS_DLL_SPEC extern int32 rand32(void); /* Return a random double from 0.0 to 1.0 (exclusive). Floats support only 7 valid decimal digits. * A double supports up to 15 valid decimal digits and is used internaly (RAND32_MAX has 10 digits). * With an FPU, there is usually no difference in performance between float and double. */ +MANGOS_DLL_SPEC inline double rand_norm(void) { return double(rand32()) / double(RAND32_MAX+1); Index: src/shared/vmap/VMapManager.cpp =================================================================== --- src/shared/vmap/VMapManager.cpp (revision 4763) +++ src/shared/vmap/VMapManager.cpp (working copy) @@ -431,9 +431,9 @@ { height = VMAP_INVALID_HEIGHT; //no height } - Command c = Command(); - c.fillTestHeightCmd(pMapId,Vector3(x,y,z),height); - iCommandLogger.appendCmd(c); + //Command c = Command(); + //c.fillTestHeightCmd(pMapId,Vector3(x,y,z),height); + //iCommandLogger.appendCmd(c); } return(height); } Index: win/mangosdVC80.sln =================================================================== --- win/mangosdVC80.sln (revision 4763) +++ win/mangosdVC80.sln (working copy) @@ -7,10 +7,10 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shared", "VC80\shared.vcproj", "{90297C34-F231-4DF4-848E-A74BCC0E40ED}" ProjectSection(ProjectDependencies) = postProject + {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} = {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} + {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} = {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} + {8072769E-CF10-48BF-B9E1-12752A5DAC6E} = {8072769E-CF10-48BF-B9E1-12752A5DAC6E} {262199E8-EEDF-4700-A1D1-E9CC901CF480} = {262199E8-EEDF-4700-A1D1-E9CC901CF480} - {8072769E-CF10-48BF-B9E1-12752A5DAC6E} = {8072769E-CF10-48BF-B9E1-12752A5DAC6E} - {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} = {8F1DEA42-6A5B-4B62-839D-C141A7BFACF2} - {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} = {BF6F5D0E-33A5-4E23-9E7D-DD481B7B5B9E} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mangosd", "VC80\mangosd.vcproj", "{A3A04E47-43A2-4C08-90B3-029CEF558594}" Index: win/VC80/game.vcproj =================================================================== --- win/VC80/game.vcproj (revision 4763) +++ win/VC80/game.vcproj (working copy) @@ -1,7 +1,7 @@ + + + + @@ -694,6 +702,14 @@ > + + + + Index: win/VC80/mangosd.vcproj =================================================================== --- win/VC80/mangosd.vcproj (revision 4763) +++ win/VC80/mangosd.vcproj (working copy) @@ -1,7 +1,7 @@