#include "../sc_defines.h" struct MANGOS_DLL_DECL npc_generic_creatureAI : public ScriptedAI { npc_generic_creatureAI(Creature *c) : ScriptedAI(c), spellCasting(new CreatureSpellCasting()), lastAction(getMSTime()) { EnterEvadeMode(); c->LoadSpellInfo(); spellCasting->spellBook = c->m_creatureSpellBook; } //float attackReach; CreatureSpellCasting *spellCasting; uint64 lastAction; uint32 runawayTimer; float chanceToWin; // Float cause use attackrange for the moment, who sound good but wrong manner to take it void EnterEvadeMode() { /*** SpellSystem ***/ //For selecting spell case //attackReach = 3.0f; //m_creature->GetFloatValue(UNIT_FIELD_COMBATREACH) ? m_creature->GetFloatValue(UNIT_FIELD_COMBATREACH) : 1.5f; runawayTimer = 2000; spellCasting->reset(true); lastAction = getMSTime()+30000; //m_creature->RemoveAllAuras(); m_creature->DeleteThreatList(); m_creature->CombatStop(); chanceToWin = 100.0f; DoGoHome(); } void JustDie() { if( AICheckFriendTarget() ) // a healer should never ne target by a healer so, else case should be good { Unit* friendTarget = m_creature->GetUnit((*m_creature), m_creature->GetUInt64Value(UNIT_FIELD_TARGET)); friendTarget->m_hasHealer = false; } } void DamageTaken(Unit *done_by, uint32 &damage) { // Make a helper Attack the Persone is attacking him, so next tick he will be a attacker. if( !m_creature->getVictim() && AIHPPercent(m_creature) < irand(80,99) ) { m_creature->clearUnitState(UNIT_STAT_FOLLOW); m_creature->SetUInt64Value(UNIT_FIELD_TARGET,0); MoveInLineOfSight(done_by); } } void AttackStart(Unit *attacker) { if( !attacker ) return; // AICheckFriendTarget() should not appear here, Must be Checked to be sure she never needed here. if( AICheckFriendTarget() && AIHPPercent(attacker) > AIHPPercent(m_creature)+20 ) { m_creature->AddThreat(attacker, 0.0f); //target can come from threat, should put at better place return; } if( attacker->isTargetableForAttack() /*&& attacker!= m_creature, That should not happen*/ ) { if ( m_creature->Attack(attacker) ) { m_creature->AddThreat(attacker, 0.0f); //target can come from threat, should put at better place m_creature->resetAttackTimer(); if (attacker->GetTypeId() == TYPEID_PLAYER) m_creature->SetLootRecipient((Player*)attacker); return; } } } void MoveInLineOfSight(Unit *who) { if( !m_creature->IsWithinLOSInMap(who) || m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE ) return; if( !m_creature->GetUInt64Value(UNIT_FIELD_TARGET) ) { //MoveInLineOfSight Normal if( m_creature->IsHostileTo(who) && who->isInAccessablePlaceFor(m_creature) ) { float attackRadius = m_creature->GetAttackDistance(who); if( m_creature->IsWithinDistInMap(who, attackRadius) ) { chanceToWin = attackRadius; // TODO: my Stock CombatPoint < is Stock Combat Point // Is target got too stronger, Search Friend Closer If No One, Cast a fear Spell on her, or similar // If she walk near ally they will do what they have to do. // If friend around they will catch him if possible if( who->HasStealthAura() ) who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); AttackStart(who); if(m_creature->m_creatureSpellBook) AIMotionTarget(who,FOLLOW_RANGE_CASTER); else AIMotionTarget(who,FOLLOW_RANGE_MELEE); // Should be Aggro Rate from Config in place 1 if( who->GetTypeId() == TYPEID_PLAYER && attackRadius/ 1 <= irand(5,8) ) { m_creature->CastSpell( (Unit*)m_creature, CREATURE_SPELL_RUNAWAY, true ); if(m_creature->getVictim()->GetTypeId() == TYPEID_PLAYER) m_creature->getVictim()->SetUInt64Value(UNIT_FIELD_TARGET,0); runawayTimer = 5000; } return; } return; } //MoveInLineOfSight Friendly if( m_creature->IsWithinDistInMap(who,RANGE_ASSIST ) && m_creature->IsFriendlyTo(who) && ( !who->m_hasHealer || !irand(0,20) ) ) //!irand(0,20) , can be used for Strongnest Adjustement, less Chance more easy { //TODO: test with IsInCombat() + Take target from AttackerList Unit *victimTarget = who->getVictim(); if( who->getVictim() ) { if( m_creature->IsHostileTo(victimTarget) ) { if( spellCasting->spellBook && spellCasting->spellBook->hasHealing() && AIManaPercent(m_creature) > 10 ) { m_creature->SetUInt64Value( UNIT_FIELD_TARGET, who->GetGUID() ); who->m_hasHealer = true; AIMotionTarget(who,(rand_norm()*3.0f)+1.0f);//Not sure i love that here... to be continued AISelectSpell( spellCasting ); } else { AttackStart( victimTarget ); AIMotionTarget(who,(rand_norm()*3.0f)+1.0f);//Not sure i love that here... to be continued } } } else if(spellCasting->spellBook && AIHPPercent(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 do something on a dynamic target if( m_creature->IsWithinDistInMap(who, RANGE_DYNAMIC_ASSIST ) && m_creature->IsFriendlyTo(who) ) { if( who->getVictim() && m_creature->IsFriendlyTo(who->getVictim())) return; else if( AIHPPercent(who) < 100 && spellCasting->spellBook && spellCasting->spellBook->hasHealing() && AIManaPercent(m_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 UpdateAI(const uint32 diff) { if( m_creature->isDead() ) return; //run Away Spell, Causing a Like fear But no Speed Mod and No Visual Status //Spell Work First Time: 4 Seconde, Second Time: 2 Seconde, After won't work until a Laps of Time ;) //So pretty neet for what i need here :) if( m_creature->getVictim() && runawayTimer > -1 ) { if( runawayTimer / 5000 == 1 ) { ++runaway; m_creature->CastSpell( (Unit*)m_creature, CREATURE_SPELL_TAP_FRIEND, true ); return; } else if( !m_creature->HasAura(CREATURE_SPELL_RUNAWAY,0) ) { //Check if we can use Original Caster Value in place Hack into Aura Update For fear. m_creature->CastSpell( (Unit*)m_creature, CREATURE_SPELL_RUNAWAY, true ); if(m_creature->getVictim()->GetTypeId() == TYPEID_PLAYER) m_creature->getVictim()->SetUInt64Value(UNIT_FIELD_TARGET,0); return; } } //Will need a runaway Timer or Something.... //TODO: OVERIDE spellCasting->range, Check if Target is Melee range from US, if so , then Run away or follow about same range // me->AttacPower() - him->armor() < him->AttackPower() - me-Armor(), try stick away by little push MV //Spell Casting if(spellCasting->spellIndex > -1) { Unit *target = m_creature->GetUnit( (*m_creature),m_creature->GetUInt64Value(UNIT_FIELD_TARGET) ); Unit *spellTarget = NULL; if( spellCasting->targetGUID != m_creature->GetGUID() ) spellTarget = m_creature->GetUnit((*m_creature),spellCasting->targetGUID ); else spellTarget = ((Unit*)m_creature); bool dynamicTarget = false; if(spellCasting->targetGUID != m_creature->GetGUID() && spellCasting->targetGUID != m_creature->GetUInt64Value(UNIT_FIELD_TARGET)) dynamicTarget = true; if(spellTarget) { if( !spellCasting->reached ) { if(dynamicTarget) { if( m_creature->IsWithinDistInMap(spellTarget, spellCasting->range) ) spellCasting->reached = true; else { spellCasting->reset(); return; } } else { if( spellTarget->GetGUID() == m_creature->GetGUID() || m_creature->IsWithinDistInMap(spellTarget, spellCasting->range) ) spellCasting->reached = true; else { spellCasting->runningTimer += diff; if(spellCasting->runningTimer > irand(1000*CREATURE_WAIT_REACH_RATE,2000*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 ",m_creature->IsStopped()); spellCasting->runningTimer = 0; spellCasting->reset(); if( m_creature->m_creatureSpellBook->hasSpeed() ) AISelectSpell(spellCasting, NULL, SELECTSPELL_SPEED); //Else If CreatureSpeed < TargetSpeed , Chance To Return Home. } //return; } } //if( !spellCasting->reached && m_creature->IsStopped() ) // Hack //AIMotionTarget(target,1.0f); } if( spellCasting->reached ) { if(!spellCasting->started) { if(!m_creature->IsStopped() && spellCasting->spellBook->info[spellCasting->spellIndex].isDelayed) m_creature->StopMoving(); AICast(spellCasting); lastAction = getMSTime(); return; } //Spell Finish else if( m_creature->m_currentSpells[CURRENT_CHANNELED_SPELL] ) { if( !m_creature->HasSpellChannelActiveTimer() ) { //if(!dynamicTarget && target && target->isAlive() && m_creature->hasUnitState(UNIT_STAT_FOLLOW)) //AIMotionTarget(target, spellCasting->range/2); spellCasting->reset(); } return; } else { spellCasting->reset(); //if(!dynamicTarget && target && target->isAlive() && m_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; } //Must fix Oder so creature can Strike when Running at Target waiting Spell To reach. if( !AICheckFriendTarget() && m_creature->SelectHostilTarget() && AICheckVictimTarget() && m_creature->isAttackReady() && m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE) ) { if(m_creature->getVictim()->HasSpellChannelActiveTimer() && m_creature->m_creatureSpellBook->hasInterrupt()) { AISelectSpell( spellCasting, m_creature->getVictim(), SELECTSPELL_INTERRUPT); return; } //Add a Chance To win and Go to Friend. m_creature->AttackerStateUpdate(m_creature->getVictim()); m_creature->resetAttackTimer(); lastAction = getMSTime(); } //Choses a Spell To Cast if( spellCasting->spellIndex < 0 && m_creature->GetUInt64Value(UNIT_FIELD_TARGET) ) AISelectSpell( spellCasting ); //Wait Time Reset Action Selected if(lastAction+irand(5000*CREATURE_WAIT_RATE,7000*CREATURE_WAIT_RATE) < getMSTime() ) { if( spellCasting->spellIndex > -1 ) { if( !spellCasting->reached ) spellCasting->reset(); else return; } if( AICheckFriendTarget() ) { Unit* friendTarget = m_creature->GetUnit((*m_creature), m_creature->GetUInt64Value(UNIT_FIELD_TARGET)); if( friendTarget && friendTarget->getVictim() ) { AISelectSpell(spellCasting,friendTarget->getVictim()); } else if(friendTarget && AIHPPercent(friendTarget) > irand(90,99)) { friendTarget->m_hasHealer = false; m_creature->SelectHostilTarget(); } } else m_creature->SelectHostilTarget(); lastAction = getMSTime(); } } }; CreatureAI* GetAI_npc_generic_creature(Creature *_Creature) { return new npc_generic_creatureAI (_Creature); } void AddSC_npc_generic_creature() { Script *newscript; newscript = new Script; newscript->Name="generic_creature"; newscript->GetAI = GetAI_npc_generic_creature; m_scripts[nrscripts++] = newscript; }