1 (edited by Areradon 2015-03-07 11:50:50)

Hey,
i have heared that the most TC2 devs think that:

Although smartAI and eventAI is convenient to insert generic data via sniffer, but the readability of it is totaly #$%^, thats why TC2 (actually the developer of SmartAI) want to get away from it.

I now do some structurs for scripting normal Trash NPCs via C++ scripts. But i could need some help doing this.

Archer
Video example: https://www.youtube.com/watch?v=gXn7HRgYe1c
At 1:08
Description:
The NPC xyz is starting whith a range weapon in his main hand. When getting Infight he only starts with shooting and dont move. When the Tank gets in Melee Range he switch to melee mode. He gets other weapons and start chasing. If he get stunned and the tank is away again he stops moving and start shooting again.

#define SPELL_SHOOT 
#define SPELL_MELEE

struct mob_archerAI : public ScriptedAI
{
    mob_archerAI(Creature* c) : ScriptedAI(c) {}

    uint32 Check_Timer;
    uint32 Shoot_Timer;

    bool InMeleeRange

    void Reset()
    {
        Check_Timer = 1000;
        Shoot_Timer = 2000;
    }

    void EnterCombat(Unit* /*who*/)
    {
        InMeleeRange = false;
    }

    void UpdateAI(const uint32 diff)
    {
        if(!UpdateVictim())
            return;

        if(Check_Timer < diff)
        {
                Unit *target;
                std::list<HostilReference *> t_list = me->getThreatManager().getThreatList();
                for(std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
                {
                    target = Unit::GetUnit(*me, (*itr)->getUnitGuid());
                    //if in melee range
                    if(target && target->IsWithinDistInMap(me, 5))
                    {
                        InMeleeRange = true;
                        break;
                    }
                }
                Check_Timer = 1000;
        }else Check_Timer -= diff;

        // If in Melee Range, Melee AI
        if(InMeleeRange)
        {
            me->GetMotionMaster()->MoveChase(me->getVictim());
        }
        else if(!InMeleeRange)
        {
            me->GetMotionMaster()->MoveIdle();
            if(Shoot_Timer < diff)
            {
                DoCast(me->getVictim(), SPELL_SHOOT);
                Shoot_Timer = 2000;
            }else Shoot_Timer -= diff;
        }

        DoMeleeAttackIfReady();
    }
};

2 (edited by desteny 2015-03-09 10:46:34)

Areradon wrote:

Hey,
i have heared that the most TC2 devs think that:

Although smartAI and eventAI is convenient to insert generic data via sniffer, but the readability of it is totaly #$%^, thats why TC2 (actually the developer of SmartAI) want to get away from it.

Thats true, and its not only (your / my) opinion, a lot of devs are thinking the same way.
For this reason i sugested long time ago a little bit different solution then yours, but its based on the same idea. (sorry can't share it cause of different collaborators and some want to keep it closed source)

but how ever, i explain:

your idea is to create "struct mob_archerAI : public ScriptedAI" as a generic prototype (template) for a type of AI, that can be used for several mobs, but the code needs to be copyed if spells or timing changes!

that results in much redundant code, hard to maintain and update.

my idea is to code a new "abstract AI struct" one in general like EventAI or one for each type (archer, caster, meele, ...)
this AI can be implemented by cpp script for various trashmobs with different spells and timings in a simple way (the basic concept is the same as eventAI and SmartAI has, but scripted in readable cpp)

the logic how a AI moves casts or follow the player (LOS, casting distance, mana management and many more) is implemneted only once in the "abstract"

example (only meta code!!):

//AI inherits from base AI for trashmobs

struct mob_archerAI : public AbstractTrashMobAI

//in method InitAI (or constructor) you can initialize the AI with all neccessary parameters

    void InitAI()
    {
        SetAIType(AiType.ARCHER_AI); //type of AI
        SetAISpell(SPELL_CAST1_ID, 3000, SpellFlags.IfHasMana); //cast spell 1 every 3 sec is has mana
        SetAISpell(SPELL_CAST2_ID, 5000, SpellFlags.IfHasDamageTaken, Target.Self); //cast spell 2 every 5 sec if has not full life (for example heal)
        UseAutoAttacks(true);
    }

in this way you define spells and logic, but no timers or AI code needed!

for more advanced usage you can even implements events (triggerd by update cycle) but don't use default AI events to implement logic, it should all work out of the box.

this is only a suggestion, feel free to do what you prefer, its only a type of input cause i know that this type of solution works well...