When going about modifying something on TrinityCore, it's remarkably different from ArcEmu. Since a lot of it is based inside the DB, and it's nearly all C++, no LUA whatsoever. I'm going to write a few small linked-together guides on scripting and implementing the SQL, lay back, relax, and prepare to read.
The Shell of C++
Code:
#include "ScriptPCH.h"
enum eNums
{
};
class example : public CreatureScript
{
public:
example() : CreatureScript("example"){}
CreatureAI* GetAI_example(Creature* pCreature) const
{
return new exampleAI(pCreature);
}
struct exampleAI : public ScriptedAI
{
exampleAI(Creature *c) : ScriptedAI(c) {}
void Reset()
{
}
void KilledUnit(Unit * /*victim*/)
{
}
void JustDied(Unit * /*victim*/)
{
}
void EnterCombat(Unit * /*who*/)
{
}
void UpdateAI(const uint32 uiDiff)
{
if (!me->getVictim())
{
}
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
};
};
void AddSC_example()
{
new example();
}
The C++ example above is a basicskeleton of a C++ code for a basic NPC. I'm going to show you how to:
1. Cast Spells
- On Self
- On Target
- On Random Target(s)
2. Phases
- Health % Phases
- Timed Phases
3. Speaking
- Saying
- Yelling
Part I
[Casting Spells]
In {Part I} of the guide we'll work on all the magic of the scripting, literally. I'll show you how to make an NPC - buff themselves with Ice Armor, then cast Frostbolt on the attacking target AND Fireball on random target(s).
In your C++ shell, go back to the following:
Code:
enum eNums
{
};
Here we'll basically keep track of spells, and NPC's and such to be used in a script. Kind of like using the local function in a LUA script to define random stuff. So we are going to have Ice Armor, Frostbolt, and Fireball. So lets define them, shall we? Simply do this:
See how we just registered the spells right there? Simple, eh? Now, notice after each one, a comma follows. All of them will have to have a comma after them, except for the last one. This also means if they're only going to cast one spell, then they won't have to have a comma at the end.
Now, lets go down to this section:
Code:
exampleAI(Creature *c) : ScriptedAI(c) {}
Under there we'll add some more ”locals.” These will count as timers and variables, the script will call back on from time to time, when we tell it too.
We're simply going to follow this formula: uint32 SpellName_Timer;.
After this, we'll need to set times for the boss to use the spells, so go down to here:
Code:
void Reset()
{
}
Now, we add the times for them to first call upon it. I want the Ice Armor spell to be used the second they're spawned. Frostbolt, as soon as we enter combat. Fireball after being in combat for five seconds. So simply do this:
See what I did there? Those numbers in there, specify how long (in milliseconds) this monster will first call upon the spell, where it is in the script. Now, we'll put these in the appropriate locations, to make him cast these spells at the appropriate time. So, first go to here:
Code:
void UpdateAI(const uint32 uiDiff)
{
if (!me->getVictim())
{
}
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
To make them cast a spell outside of combat, what we'll put here is under:
Code:
if (!me->getVictim())
{
}
We'll simply follow this format for making the NPC cast the spell:
Code:
if (SpellName_Timer <= uiDiff)
{
<Targeting Info Goes Here>;
SpellName = <Time Till Want to Recast>;
}
else
SpellName -= uiDiff;
So, for out of combat, we will have this work under the if (!me->getVictim())
We will make him cast Ice Armor on Himself!
Code:
if (!me->getVictim())
{
if (Icearmor_Timer <= uiDiff)
{
DoCast(me, SPELL_ICEARMOR);
Icearmor_Timer = 18000000;
}
else
Icearmor_Timer -= uiDiff;
This will make them cast Ice Armor, then after thirty minutes, re-cast it. Since it is under the “if (!me->getVictim())” it'll be cast outside of combat. Now, to make him cast something inside of combat. We'll go down to the next area, and add the “If (SpellName_Timer <= uiDiff)” Go down to here:
Code:
if (!UpdateVictim())
return;
Underneath of that is where we'll add everything. We will make Frostbolt cast on the MT (Main Tank). Fireball is to be cast on random targets.
Code:
if (!UpdateVictim())
return;
if (Frostbolt_Timer <= uiDiff)
{
DoCast(me->getVictim(), SPELL_FROSTBOLT);
Frostbolt_Timer = 3000;
}
else
Frostbolt_Timer -= uiDiff;
if (Fireball_Timer <= uiDiff)
{
if (Unit *pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pTarget, SPELL_FIREBALL);
Fireball_Timer = 10000;
}
else
Fireball_Timer -= uiDiff;
See how I used each of those targeting for all three spells? Those are the different targeting types.
Main Tank = DoCast(me->getVictim(), SPELL_SPELLNAME);
On Self = DoCast(me, SPELL_SPELLNAME);
On Random = if (Unit *pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pTarget, SPELL_SPELLNAME);
The 0 in the Random Targeting, can be changed for which you want to target (Mana, Close Range, or Far Range) I believe it is the same as it is on LUA. But, I've only really used those three.
Part II
[Health Percentage Phasing]
Making phases in C++ is not terribly difficult. It does require making a new variable, and then a few more if's, but over all, it is more to type in, but it's straightforward, and it's obvious what it does.
First, go back up to the section you made all the “uint32 Spell_Timer;” Here you will make another one “uint32 Phase;”
This'll set the Phase to one. This is really useful for setting any variable, because you can set after a certain spell, or something else to change it to a different value. After a certain value, you can trigger something else. But for now, lets set up what's needed to Switch Phases, and how to set it up, to make spells only cast in certain phases. First, lets set up the mechanism we need to Switch spells with a Health % loss. We will need to add this after “if (!UpdateVictim()) return;”
Code:
if (!UpdateVictim())
return;
if (((me->GetHealth()*100 / me->GetMaxHealth()) < 55) && (Phase == 1))
{
Phase = 2;
}
Here is the thing needed to change phases, it'll make the phase set too 2, and what I will show in the following CODE box, will show you what to do to make the spells cast in the correct phases!
Code:
if (!UpdateVictim())
return;
if (((me->GetHealth()*100 / me->GetMaxHealth()) < 55) && (Phase == 1))
{
Phase = 2;
}
if (Phase == 1)
{
}
if (Phase == 2)
{
}
See the “If Phase = 1” and “if Phase = 2” thing? It does simply this: “If the phase equals one, it'll do what's in the brackets below it.” Here lets have Phase 1, be what we scripted earlier, and lets make phase two, only cast Fireball.
Now, for Timed Phases it's a bit different. But, at the same time its 100% the same as making a new spell. Lets just register another spell in “enum Enums” and call it “SPELL_ENRAGE”
Now, we'll set it up too work after ten minute(s).
Code:
void Reset()
{
Enrage_Timer = 600000;
}
Now, lets go down and make it work in combat. Do this:
Code:
if (!UpdateVictim())
return;
if (Enrage_Timer <= uiDiff)
{
DoCast(me, SPELL_ENRAGE);
}
That's all you need to do, no extra timers, no nothing else. (Hopefully, by now your script is an unstoppable killing machine.) - (Also, it should be able to be queried over and over.)
Part III
[Creature Speaking]
Let us go back up to the middle area that we didn't use. This is the area that we'll add ways to talk and / or yell.
I believe it's quite obvious what does what here. If by chance you can't figure it out... [KilledUnit: Is what the NPC will do or say when they kill something]. [JustDied: Is what they do or say when they die]. [EnterCombat: Is what they do or say upon entering combat]. Now, lets get to talking part...
Say!
Code:
me->MonsterSay(“TEXT”, LANG_UNIVERSAL, NULL);
Just replace “TEXT” with what you want to say, and “LANG_UNIVERSAL” with whatever language you want, make “UNIVERSAL” into which language you want. “LANG_COMMON”, or “LANG_ORCISH”, etc.
Yell!
Code:
me->MonsterYell(“TEXT”, LANG_UNIVERSAL, NULL);
Just replace “TEXT” with what you want to say, and “LANG_UNIVERSAL” with whatever language you want, make “UNIVERSAL” into which language you want. “LANG_COMMON”, or “LANG_ORCISH”, etc.
Thanks To
Ikizami: For the original post.
MattH: For revising the grammar and updating the whole post to work with the latest revision of TrinityCore.
Special Thanks To
[Only registered and activated users can see links. ] for allowing this post to be shared! :)
Bookmarks