The TaskScheduler was introduced in commit TrinityCore/TrinityCore@da77a90 and TrinityCore/TrinityCore@151a0f5 as a better alternative to eventmap.
It is adapted to the current needs of scripting which means scheduling of event between a random range, schedulling with readable std::chrono constants, repeating of events and more.
See this script for a short usage example
The Basis usage is like EventMap
#include "TaskScheduler.h"
TaskScheduler scheduler;
void UpdateAI(uint32 diff) override
{
// Update the scheduler with the given difftime in ms.
scheduler.Update(diff);
}
To prevent spells casts while another one is still casted it is recommended to use task validators and callbacks:
#include "TaskScheduler.h"
TaskScheduler scheduler;
// Don't invoke any task if the condition isn't true
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff, [this]
{
// Post actions after the scheduler has successfully finished.
// (Wasn't interrupted through a false validation).
DoMeleeAttackIfReady();
});
}
Scheduling tasks is very easy with lamdas, if you are not familar with c++ lamdas check it out, you can also use static methods, member methods or std::bind (everything which convertible into std::function<void(TaskContext)>
).
void EnterCombat(Unit* /*who*/) override
{
scheduler.Schedule(Seconds(3), [this](TaskContext context)
{
me->Yell("Hey, this is a simple task scheduler test", LANG_UNIVERSAL);
});
// Schedule with a random time between 5 and 8 seconds.
scheduler.Schedule(Seconds(5), Seconds(8), [this](TaskContext context)
{
// ... Do something
});
// Schedule an async task std::function<void()>
scheduler.Async([this]
{
// ... Do something
});
}
Its also possible to assign group id's to tasks
enum Groups
{
GROUP_ONE,
GROUP_TWO,
GROUP_THREE
}
// Schedule with a random time between 5 and 8 seconds.
scheduler.Schedule(Seconds(5), GROUP_ONE, [this](TaskContext context)
{
// ... Do something
});
scheduler.CancelGroup(GROUP_ONE);
scheduler.CancelAll();
// More methods are available, just check it out, the TaskScheduler is well documented.
scheduler.DelayGroup(GROUP_ONE, Seconds(3), Seconds(5));
scheduler.DelayAll(Seconds(3));
scheduler.RescheduleGroup(GROUP_ONE, Seconds(3), Seconds(5));
Every method which requires a time duration is overloaded several times and to accept a static time or a random time between min and max (equal to EventMap
usage with urand()
).
Nearly every method call returns this as reference
which makes it possible to concat multiple calls together:
enum Group
{
GROUP_FIRST,
GROUP_SECOND,
GROUP_THIRD
};
scheduler
.Schedule(Milliseconds(1000), GROUP_FIRST, [](TaskContext context)
{
std::cout << "Delay: Second" << std::endl;
})
.Schedule(Milliseconds(500), GROUP_SECOND, [](TaskContext context)
{
std::cout << "Delay: First" << std::endl;
})
.Schedule(Milliseconds(1500), GROUP_THIRD, [](TaskContext context)
{
std::cout << "Delay: Third" << std::endl;
});
The TaskContext provides the ability the modify or to repeat the current event. You can also store the TaskContext in the script for further usage.
Also the TaskContext provides the ability to modify the TaskScheduler safe (never access the TaskScheduler directly from a scheduled task).
scheduler.Schedule(Seconds(5), [this](TaskContext context)
{
// Modify the TaskScheduler from the called task.
context.CancelAll();
// Schedule a new task from within the task.
context.Schedule(Seconds(7), GROUP_ONE, [this](TaskContext context)
{
// ... Do something
});
// Repeat with the same duration
context.Repeat();
// Repeat with 4s
context.Repeat(Seconds(4));
// Repeat with a random duration between 6s and 10s.
context.Repeat(Seconds(6), Seconds(10));
});
A task counts how often it was repeated, you can access this counter by using TaskContext::GetRepeatCounter()
.
The following example shows how to write a simple spoken event using the repeat counter:
void EnterCombat(Unit* /*who*/) override
{
scheduler.Schedule(Seconds(5), [this](TaskContext context)
{
// The repeat counter increases every time the event was repeated.
switch (context.GetRepeatCounter())
{
case 0:
me->Yell("O shit!", LANG_UNIVERSAL);
break;
case 1:
me->Yell("I see it was a mistake to fight you.", LANG_UNIVERSAL);
break;
case 2:
me->Yell("There is no loot you can get today, sorry!", LANG_UNIVERSAL);
break;
case 3:
me->Yell("See YA!", LANG_UNIVERSAL);
break;
default:
me->DisappearAndDie();
// Return here without repeating the event.
return;
}
// Repeat the event for the next step.
context.Repeat(Seconds(4), Seconds(6));
});
}
For more usage examples check out my development repositoy or my TrinityCore fork which contains a sample script.