• This is a read only backup of the old Emudevs forum. If you want to have anything removed, please message me on Discord: KittyKaev

[C++] The Ultimate C++ Boss Script Tutorial

Jpp

Administrator
Boss Script
In this tutorial, I will teach you one of the ways to make a Boss Script using C++ & SQL for TrinityCore.

I will talk about the following points:
1. Casting Spells
2. Spawning Creatures
3. Phases
4. Talking and Emoting

Template for a Boss Script
This is how a basic template for a boss script would look.
Code:
#include "ScriptPCH.h"

enum Spells
{
};

enum Events
{
};

class example : public CreatureScript
{
    public:
        example() : CreatureScript("example") { }

        struct exampleAI : public BossAI
        {
            exampleAI(Creature* creature) : BossAI(creature, 0)
            {
            }

            void Reset() OVERRIDE
            {
                _Reset();
            }
			
            void EnterCombat(Unit* /*who*/) OVERRIDE
            {
            }
			
            void KilledUnit(Unit * /*victim*/) OVERRIDE
            {
            }
			
            void JustDied(Unit * /*victim*/) OVERRIDE
            {
            }

            void UpdateAI(uint32 diff) OVERRIDE
            {
                if (!UpdateVictim())
                    return;
					
                events.Update(diff);

                if (me->HasUnitState(UNIT_STATE_CASTING))
                    return;
					
                while (uint32 eventId = events.ExecuteEvent())
                {
                    switch (eventId)
                    {
                        default:
                            break;
                    }
                }
				
                DoMeleeAttackIfReady();
            }
        };

        CreatureAI* GetAI(Creature* creature) const OVERRIDE
        {
            return new exampleAI(creature);
        }
};

void AddSC_example()
{
	new example();
}

Spells - Part 1
I will show you how to make the creature cast spells, but first you will need to define the spells in the “enum Spells” showed in the basic Boss Template.
Code:
 enum Spells
{
};
You will be giving each spell a name, so we can use that later in the code instead of the numbers. This will make it easier for us to keep track of the spells. In this example, we are using the spells Lightning Bolt, Healing Wave and Flame Shock.
Code:
enum Spells
{
    SPELL_LIGHTNINGBOLT = 403, 
    SPELL_HEALINGWAVE = 331,
    SPELL_FLAMESHOCK = 8050,
};
“SPELL_” is not needed, but it is used to keep track of the spells easier.

Now we need to define some events that we will use later in the script. In addition to “enum Spells” I have created one called “enum Events” which is shown in the Boss Template.
Code:
 enum Events
{
};
We are using a switch later in the script and it will switch between numbers, but to keep track of it better, we will also give the numbers a name, similar to what we did with the spell ids.
Code:
 enum Events
{
    EVENT_LIGHTNINGBOLT = 1,
    EVENT_HEALINGWAVE = 2,
    EVENT_FLAMESHOCK = 3,
};
Again, “EVENT_” is not needed, but it makes it easier to keep track of them. The numbers should just start from 1 and count up for each event you add like shown.
We can now move on with the script.
Now go to “void EnterCombat” in the Boss Template.
Code:
void EnterCombat(Unit* /*who*/) OVERRIDE
{
}
We will now create a timed event for each of our spells.
Code:
void EnterCombat(Unit* /*who*/) OVERRIDE
{
	                events.ScheduleEvent(EVENT_LIGHTNINGBOLT, 8000);
                events.ScheduleEvent(EVENT_HEALINGWAVE, 12000);
                events.ScheduleEvent(EVENT_FLAMESHOCK , 10000);
}
This will execute our event after the defined time has passed. 8000 is how many milliseconds it will take before the event will be executed. 1000 milliseconds are equal to 1 second.
We will now go down to our switch shown in the Boss Template.
Code:
while (uint32 eventId = events.ExecuteEvent())
{
    switch (eventId)
    {
        default:
            break;
    }
}
I will now add the three cases for my events.
Code:
while (uint32 eventId = events.ExecuteEvent())
{
    switch (eventId)
    {
        case EVENT_LIGHTNINGBOLT:
            break;
        case EVENT_HEALINGWAVE:
            break;
        case EVENT_FLAMESHOCK:
            break;
        default:
            break;
    }
}
Right now, the events will not do anything when they are executed, so we will have to add that too.
Code:
while (uint32 eventId = events.ExecuteEvent())
{
    switch (eventId)
    {
        case EVENT_LIGHTNINGBOLT:
            DoCastVictim(SPELL_LIGHTNINGBOLT);
            events.ScheduleEvent(EVENT_LIGHTNINGBOLT, 8000);
            break;
        case EVENT_HEALINGWAVE:
            DoCast(me, SPELL_HEALINGWAVE);
            events.ScheduleEvent(EVENT_HEALINGWAVE, 12000);
            break;
        case EVENT_FLAMESHOCK:
            if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                DoCast(target, SPELL_FLAMESHOCK);
            events.ScheduleEvent(EVENT_FLAMESHOCK, 10000);
            break;
        default:
            break;
    }
}
It will now cast spells in the different cases and repeat the event after the set time.
In the EVENT_LIGHTNINGBOLT
Code:
DoCastVictim(SPELL_LIGHTNINGBOLT); //Will cast the spell on the victim (main tank.)
events.ScheduleEvent(EVENT_LIGHTNINGBOLT, 8000); //Will execute the event again after 8000 milliseconds (8 seconds.)
In the EVENT_HEALINGWAVE
Code:
DoCast(me, SPELL_HEALINGWAVE); //Will cast the spell on itself.
events.ScheduleEvent(EVENT_HEALINGWAVE, 12000); //Will execute the event again after 12000 milliseconds (12 seconds.)
In the EVENT_FLAMESHOCK
Code:
if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM, [COLOR="#FF0000"]0[/COLOR]))
    DoCast(target, SPELL_FLAMESHOCK);
events.ScheduleEvent(EVENT_FLAMESHOCK , 10000);
To target different groups of random players you will have to change the red number shown above in “EVENT_FLAMESHOCK”.
List of the different groups.
Code:
(SELECT_TARGET_RANDOM, 0)    //Any random target.
(SELECT_TARGET_RANDOM, 1)    //Random target in Short Range.
(SELECT_TARGET_RANDOM, 2)    //Random target in Mid Range.
(SELECT_TARGET_RANDOM, 3)    //Random target in Long Range.
(SELECT_TARGET_RANDOM, 4)    //Random target with Mana.
(SELECT_TARGET_RANDOM, 5)    //Random target with Rage.
(SELECT_TARGET_RANDOM, 6)    //Random target with Energy.

The Complete Boss Script Part 1
Code:
#include "ScriptPCH.h"

enum Spells
{
    SPELL_LIGHTNINGBOLT = 403, 
    SPELL_HEALINGWAVE = 331,
    SPELL_FLAMESHOCK = 8050,
};

enum Events
{
    EVENT_LIGHTNINGBOLT = 1,
    EVENT_HEALINGWAVE = 2,
    EVENT_FLAMESHOCK = 3,
};

class example : public CreatureScript
{
    public:
        example() : CreatureScript("example") { }

        struct exampleAI : public BossAI
        {
            exampleAI(Creature* creature) : BossAI(creature, 0)
            {
            }

            void Reset() OVERRIDE
            {
                _Reset();
            }
			
            void EnterCombat(Unit* /*who*/) OVERRIDE
            {
                events.ScheduleEvent(EVENT_LIGHTNINGBOLT, 8000);
                events.ScheduleEvent(EVENT_HEALINGWAVE, 12000);
                events.ScheduleEvent(EVENT_FLAMESHOCK , 10000);
            }
			
            void KilledUnit(Unit * /*victim*/) OVERRIDE
            {
            }
			
            void JustDied(Unit * /*victim*/) OVERRIDE
            {
            }

            void UpdateAI(uint32 diff) OVERRIDE
            {
                if (!UpdateVictim())
                    return;
					
                events.Update(diff);

                if (me->HasUnitState(UNIT_STATE_CASTING))
                    return;
					
            while (uint32 eventId = events.ExecuteEvent())
            {
                switch (eventId)
                {
                    case EVENT_LIGHTNINGBOLT:
                        DoCastVictim(SPELL_LIGHTNINGBOLT);
                        events.ScheduleEvent(EVENT_LIGHTNINGBOLT , 8000);
                        break;
                    case EVENT_HEALINGWAVE:
                        DoCast(me, SPELL_HEALINGWAVE);
                        events.ScheduleEvent(EVENT_HEALINGWAVE , 12000);
                        break;
                    case EVENT_FLAMESHOCK:
                        if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                            DoCast(target, SPELL_FLAMESHOCK);
                        events.ScheduleEvent(EVENT_FLAMESHOCK , 10000);
                        break;
                    default:
                        break;
                }
            }
				
                DoMeleeAttackIfReady();
            }
        };

        CreatureAI* GetAI(Creature* creature) const OVERRIDE
        {
            return new exampleAI(creature);
        }
};

void AddSC_example()
{
	new example();
}
 

Jpp

Administrator
Spawning Creatures - Part 2
Spawning a creature is an easy task, and does not require a lot of work.
We will add an event like the one we added earlier.
Code:
enum Events
{
    EVENT_LIGHTNINGBOLT = 1,
    EVENT_HEALINGWAVE = 2,
    EVENT_FLAMESHOCK = 3,
    EVENT_SPAWNCREATURE = 4,
};
Then we will give it a timer like the one we made earlier.
Code:
void EnterCombat(Unit* /*who*/) OVERRIDE
{
    events.ScheduleEvent(EVENT_LIGHTNINGBOLT, 8000);
    events.ScheduleEvent(EVENT_HEALINGWAVE, 12000);
    events.ScheduleEvent(EVENT_FLAMESHOCK, 10000);
    events.ScheduleEvent(EVENT_SPAWNCREATURE, 25000);
}
Once that is done, we will make a case for the event down in the switch.
Code:
while (uint32 eventId = events.ExecuteEvent())
{
    switch (eventId)
    {
        case EVENT_LIGHTNINGBOLT:
            DoCastVictim(SPELL_LIGHTNINGBOLT);
            events.ScheduleEvent(EVENT_LIGHTNINGBOLT, 8000);
            break;
        case EVENT_HEALINGWAVE:
            DoCast(me, SPELL_HEALINGWAVE);
            events.ScheduleEvent(EVENT_HEALINGWAVE, 12000);
            break;
        case EVENT_FLAMESHOCK:
            if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                DoCast(target, SPELL_FLAMESHOCK);
            events.ScheduleEvent(EVENT_FLAMESHOCK, 10000);
            break;
        case EVENT_SPAWNCREATURE:
					
        events.ScheduleEvent(EVENT_SPAWNCREATURE, 25000);
            break;
        default:
            break;
    }
}
To make it spawn a creature we will add a line, similar to how we make it cast a spell.
Code:
case EVENT_SPAWNCREATURE:
    me->SummonCreature(NPCID, xf, yf, zf, of, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000);
    events.ScheduleEvent(EVENT_SPAWNCREATURE, 25000);
    break;
Change the NPCID to the id of your creature.
Like this, where I use the id 1234.
Code:
me->SummonCreature(1234, xf, yf, zf, of, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000);
Change the x, y, z to the coords you want the creature to spawn on and the o to the orientation you want the creature to have. Do not remove the “f”, as that is needed for decimal coords.
It would look something like this. The coords I used were just some random ones.
Code:
me->SummonCreature(1234, -512.42f, 1234.56f, 56.21f, 2.1f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000);
As of now, the creature will despawn after 5000 milliseconds (5 seconds) have passed while it is out of combat.
Here is a list of other despawns you can use.
Code:
TEMPSUMMON_TIMED_OR_DEAD_DESPAWN                    //Despawns after a specified time OR when the creature disappears.
TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN                  //Despawns after a specified time OR when the creature dies.
TEMPSUMMON_TIMED_DESPAWN                            //Despawns after a specified time.
TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT              //Despawns after a specified time after the creature is out of combat.
TEMPSUMMON_CORPSE_DESPAWN                           //Despawns instantly after death.
TEMPSUMMON_CORPSE_TIMED_DESPAWN                     //Despawns after a specified time after death.
TEMPSUMMON_DEAD_DESPAWN                             //Despawns when the creature disappears.
TEMPSUMMON_MANUAL_DESPAWN                           //Despawns when UnSummon() is called.
If the despawn is not timed, do not include the last numbers, e.g.
Code:
me->SummonCreature(1234, -512.42f, 1234.56f, 56.21f, 2.1f, TEMPSUMMON_DEAD_DESPAWN);

The Complete Boss Script Part 2
Code:
 #include "ScriptPCH.h"

enum Spells
{
    SPELL_LIGHTNINGBOLT = 403, 
    SPELL_HEALINGWAVE = 331,
    SPELL_FLAMESHOCK = 8050,
};

enum Events
{
    EVENT_LIGHTNINGBOLT = 1,
    EVENT_HEALINGWAVE = 2,
    EVENT_FLAMESHOCK = 3,
    EVENT_SPAWNCREATURE = 4,
};

class example : public CreatureScript
{
    public:
        example() : CreatureScript("example") { }

        struct exampleAI : public BossAI
        {
            exampleAI(Creature* creature) : BossAI(creature, 0)
            {
            }

            void Reset() OVERRIDE
            {
                _Reset();
            }
			
            void EnterCombat(Unit* /*who*/) OVERRIDE
            {
                events.ScheduleEvent(EVENT_LIGHTNINGBOLT, 8000);
                events.ScheduleEvent(EVENT_HEALINGWAVE, 12000);
                events.ScheduleEvent(EVENT_FLAMESHOCK, 10000);
                events.ScheduleEvent(EVENT_SPAWNCREATURE, 25000);
            }
			
            void KilledUnit(Unit * /*victim*/) OVERRIDE
            {
            }
			
            void JustDied(Unit * /*victim*/) OVERRIDE
            {
            }

            void UpdateAI(uint32 diff) OVERRIDE
            {
                if (!UpdateVictim())
                    return;
					
                events.Update(diff);

                if (me->HasUnitState(UNIT_STATE_CASTING))
                    return;
					
                while (uint32 eventId = events.ExecuteEvent())
                {
                    switch (eventId)
                    {
                        case EVENT_LIGHTNINGBOLT:
                            DoCastVictim(SPELL_LIGHTNINGBOLT);
                            events.ScheduleEvent(EVENT_LIGHTNINGBOLT, 8000);
                            break;
                        case EVENT_HEALINGWAVE:
                            DoCast(me, SPELL_HEALINGWAVE);
                            events.ScheduleEvent(EVENT_HEALINGWAVE, 12000);
                            break;
                        case EVENT_FLAMESHOCK:
                            if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                                DoCast(target, SPELL_FLAMESHOCK);
                            events.ScheduleEvent(EVENT_FLAMESHOCK, 10000);
                            break;
                        case EVENT_SPAWNCREATURE:
                            me->SummonCreature(1234, -512.42f, 1234.56f, 56.21f, 2.1f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000);
                            events.ScheduleEvent(EVENT_SPAWNCREATURE, 25000);
                            break;
                        default:
                            break;
                    }
                }
				
                DoMeleeAttackIfReady();
            }
        };

        CreatureAI* GetAI(Creature* creature) const OVERRIDE
        {
            return new exampleAI(creature);
        }
};

void AddSC_example()
{
	new example();
}
 

Jpp

Administrator
Phases - Part 3
To demonstrate how to make health-based phases I will write a different script.

Template for a Boss Script Using Phases
Code:
#include "ScriptPCH.h"

enum Spells
{
};

enum Events
{
};

enum Phases
{
};

class example : public CreatureScript
{
    public:
        example() : CreatureScript("example") { }

        struct exampleAI : public BossAI
        {
            exampleAI(Creature* creature) : BossAI(creature, 0)
            {
            }

            void Reset() OVERRIDE
            {
                _Reset();
            }
			
            void EnterCombat(Unit* /*who*/) OVERRIDE
            {
            }
			
            void KilledUnit(Unit * /*victim*/) OVERRIDE
            {
            }
			
            void JustDied(Unit * /*victim*/) OVERRIDE
            {
            }

            void UpdateAI(uint32 diff) OVERRIDE
            {
                if (!UpdateVictim())
                    return;
					
                events.Update(diff);

                if (me->HasUnitState(UNIT_STATE_CASTING))
                    return;
					
                while (uint32 eventId = events.ExecuteEvent())
                {
                    switch (eventId)
                    {
                        default:
                            break;
                    }
                }
				
                DoMeleeAttackIfReady();
            }
        };

        CreatureAI* GetAI(Creature* creature) const OVERRIDE
        {
            return new exampleAI(creature);
        }
};

void AddSC_example()
{
	new example();
}

First, we will have to add the phases to the enum in the template. I will be using two phases.
Code:
enum Phases
{
    PHASE_ONE = 1,
    PHASE_TWO = 2,
};
Then we go down to EnterCombat in the template, and add this line, to make the boss start in phase 1.
Code:
void EnterCombat(Unit* /*who*/) OVERRIDE
{
    events.SetPhase(PHASE_ONE);
}
To make a health-based event you will have to go down to “void UpdateAI” in the template and add a couple of lines.
Code:
void UpdateAI(uint32 diff) OVERRIDE
{
    if (!UpdateVictim())
        return;
					
    events.Update(diff);

    if (me->HasUnitState(UNIT_STATE_CASTING))
        return;
	
    if (events.IsInPhase(PHASE_ONE) && HealthBelowPct(75))
    {
        events.SetPhase(PHASE_TWO);
    }
    while (uint32 eventId = events.ExecuteEvent())
    {
        switch (eventId)
        {
            default:
                break;
        }
    }
				
    DoMeleeAttackIfReady();
}
I will now explain the code I added.
Code:
if (events.IsInPhase(PHASE_ONE) && HealthBelowPct(75))
This checks if the boss is in PHASE_ONE and if the health is below 75%
Code:
 events.SetPhase(PHASE_TWO);
This sets the boss in a new phase, in this case PHASE_TWO.

I will now go down to EnterCombat and add some of the events, which I used earlier in the tutorial.
Code:
void EnterCombat(Unit* /*who*/) OVERRIDE
{
    events.SetPhase(PHASE_ONE);
	
    events.ScheduleEvent(EVENT_LIGHTNINGBOLT, 8000, 0, PHASE_ONE);
    events.ScheduleEvent(EVENT_HEALINGWAVE, 12000, 0, PHASE_ONE);
}
You might notice that they are not similar to the ones I used earlier in the tutorial, which is because I added “0, PHASE_ONE”. This will make the events only occur in phase 1.

Now go down to “if (events.IsInPhase(PHASE_ONE) && HealthBelowPct(75))”.
Code:
if (events.IsInPhase(PHASE_ONE) && HealthBelowPct(75))
{
    events.SetPhase(PHASE_TWO);
	
    events.ScheduleEvent(EVENT_FLAMESHOCK, 10000, 0, PHASE_TWO);
    events.ScheduleEvent(EVENT_SPAWNCREATURE, 25000, 0, PHASE_TWO);
}
This will schedule the two events above, when the boss enters phase 2.

The Complete Boss Script Part 3
Code:
#include "ScriptPCH.h"

enum Spells
{
    SPELL_LIGHTNINGBOLT = 403, 
    SPELL_HEALINGWAVE = 331,
    SPELL_FLAMESHOCK = 8050,
};

enum Events
{
    EVENT_LIGHTNINGBOLT = 1,
    EVENT_HEALINGWAVE = 2,
    EVENT_FLAMESHOCK = 3,
    EVENT_SPAWNCREATURE = 4,
};

enum Phases
{
    PHASE_ONE = 1,
    PHASE_TWO = 2,
};

class example : public CreatureScript
{
    public:
        example() : CreatureScript("example") { }

        struct exampleAI : public BossAI
        {
            exampleAI(Creature* creature) : BossAI(creature, 0)
            {
            }

            void Reset() OVERRIDE
            {
                _Reset();
            }
			
            void EnterCombat(Unit* /*who*/) OVERRIDE
            {
                events.SetPhase(PHASE_ONE);
				
                events.ScheduleEvent(EVENT_LIGHTNINGBOLT, 8000, 0, PHASE_ONE);
                events.ScheduleEvent(EVENT_HEALINGWAVE, 12000, 0, PHASE_ONE);
            }
			
            void KilledUnit(Unit * /*victim*/) OVERRIDE
            {
            }
			
            void JustDied(Unit * /*victim*/) OVERRIDE
            {
            }

            void UpdateAI(uint32 diff) OVERRIDE
            {
                if (!UpdateVictim())
                    return;
					
                events.Update(diff);

                if (me->HasUnitState(UNIT_STATE_CASTING))
                    return;
					
                if (events.IsInPhase(PHASE_ONE) && HealthBelowPct(75))
                {
                    events.SetPhase(PHASE_TWO);
					
                    events.ScheduleEvent(EVENT_FLAMESHOCK, 10000, 0, PHASE_TWO);
                    events.ScheduleEvent(EVENT_SPAWNCREATURE, 25000, 0, PHASE_TWO);
                }	

				
                while (uint32 eventId = events.ExecuteEvent())
                {
                    switch (eventId)
                    {
                        case EVENT_LIGHTNINGBOLT:
                            DoCastVictim(SPELL_LIGHTNINGBOLT);
                            events.ScheduleEvent(EVENT_LIGHTNINGBOLT, 8000, 0, PHASE_ONE);
                            break;
                        case EVENT_HEALINGWAVE:
                            DoCast(me, SPELL_HEALINGWAVE);
                            events.ScheduleEvent(EVENT_HEALINGWAVE, 12000, 0, PHASE_ONE);
                            break;
                        case EVENT_FLAMESHOCK:
                            if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                                DoCast(target, SPELL_FLAMESHOCK);
                            events.ScheduleEvent(EVENT_FLAMESHOCK, 10000, 0, PHASE_TWO);
                            break;
                        case EVENT_SPAWNCREATURE:
                            me->SummonCreature(1234, -512.42f, 1234.56f, 56.21f, 2.1f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000);
                            events.ScheduleEvent(EVENT_SPAWNCREATURE, 25000, 0, PHASE_TWO);
                            break;
                        default:
                            break;
                    }
                }
				
                DoMeleeAttackIfReady();
            }
        };

        CreatureAI* GetAI(Creature* creature) const OVERRIDE
        {
            return new exampleAI(creature);
        }
};

void AddSC_example()
{
	new example();
}
 

Jpp

Administrator
Talking & Emoting – Part 4

To make the boss talk or use emotes you will have to use the table creature_text in the world database.
creature_text
Code:
Entry – Entry of your boss.
Groupid – If there is more than one of the same entry (more than one text the creature says), this is the unique identifier that must be incremented for each new matching entry (ex. 0, 1, 2, 3...). If there is only one entry, this value should be 0.
Id – Entry for grouped texts and used in the script.
Text – The text the creature will say.
Type – Say(12), Yell(14), Emote(16), Boss Emote(41), Whisper(15), Boss Whisper(42).
Language – Universal is 0. Check Language.dbc for more languages.
Probability – A value from 1-100 that represents the percentage chance that this will be executed.
Emote – Emoteid.
Duration – Keep it 0, the core will handle this.
Sound – Sound id.
Comment – Any comment you may have.
I will be using the following queries for the tutorial as an example.
Code:
INSERT INTO `world`.`creature_text` (`entry`, `text`, `type`, `probability`, `emote`, `comment`) VALUES (43210, 'Time to dance', 14, 100, 400, 'example - Dance Emote + Text');
INSERT INTO `world`.`creature_text` (`entry`, `id`, `text`, `type`, `probability`, `sound`, `comment`) VALUES (43210, 1, 'And stay down!', 14, 100, 5781, 'example - Text + Sound');
This is basically how it is done.
Code:
 Talk(ID);
Where the id is taken from the table creature_text, which in my case is 0 and 1.

Go back to your script and find the place you want the creature to say/emote.
I will be using void EnterCombat and void KilledUnit as an example, but they could be added on spell cast, leave combat etc.

On EnterCombat
Code:
 void EnterCombat(Unit* /*who*/) OVERRIDE
{
    Talk(0); //Id from creature_text in world database
}
On KilledUnit
Code:
 void KilledUnit(Unit * /*victim*/) OVERRIDE
{
    Talk(1); //Id from creature_text in world database
}


Let me know if you should encounter any errors in this tutorial.
 

yuriy

Enthusiast
Deployment?

Great thread, mate!

I have two noob questions?

1. How to deploy the specified C++ files into WoW Server directories to make them work properly? Moreover, there are some kind of meta-commands (maybe defined by interface ScriptPCH.h). How the specified functions, commands works and interact with entire WoW.EXE logic? --I am familiar with universal C++ criteria

2. What ia a difference between C++ and LUA boss/NPC-scripting? How to deploy LUA files?
 

Tommy

Founder
Great thread, mate!

I have two noob questions?

1. How to deploy the specified C++ files into WoW Server directories to make them work properly? Moreover, there are some kind of meta-commands (maybe defined by interface ScriptPCH.h). How the specified functions, commands works and interact with entire WoW.EXE logic? --I am familiar with universal C++ criteria

2. What ia a difference between C++ and LUA boss/NPC-scripting? How to deploy LUA files?

1. What? Are you referring to injecting into the Wow.exe? Or are you wanting to know how to set the files up via server side so they work? I don't get the question at all.
2. C++ - Lua ~ In short, C++ requires you to compile every edit whilst Lua allows you to make scripts, reload the Lua Engine without restarting the server. Some Lua edits might need the server restarted but some don't. That's mainly what people want when it comes between C++ and Lua & that's their explanation. Lua is a more user-friendly library to learn and is a fast-pace language. C++ is a great language itself, not hard to learn once you get the hang of it. Generally, people choose Lua since they think for them that it is much more easy. For some people it is easy. It is a recommended language to start off of to work your way up in WoW emulation.

When you create a Lua file it will need go into the lua_scripts folder to load.
 

Skrbx

BETA Tester
I think i understand what he means. So on the question:

1. How to deploy the specified C++ files into WoW Server directories to make them work properly? Moreover, there are some kind of meta-commands (maybe defined by interface ScriptPCH.h). How the specified functions, commands works and interact with entire WoW.EXE logic? --I am familiar with universal C++ criteria

This is not single question so i will answer like this:

1. How to deploy the specified C++ files into WoW Server directories to make them work properly?

Official guide: http://collab.kpsn.org/display/tc/CustomScript

How the specified functions, commands works and interact with entire WoW.EXE logic?

In short the wow cient (wow.exe) and the wow server emulator (in this case trinitycore) are "talking" between each other in orther to interact. This is already implmented so all you need is to add your custom script to the solution and recompile. Then the new content will be deployed to all clients connected to the server.

Hope this helped, Regards!
 

darksoke

OnTop500
have you managed to make a mirror image phase ? To summon a mirror image of the attacker , i tried doing that before but it crashed the server at that phase and also i lost the script :(
 

Tommy42

Enthusiast
2. C++ - Lua ~ In short, C++ requires you to compile every edit whilst Lua allows you to make scripts, reload the Lua Engine without restarting the server. Some Lua edits might need the server restarted but some don't. That's mainly what people want when it comes between C++ and Lua & that's their explanation. Lua is a more user-friendly library to learn and is a fast-pace language. C++ is a great language itself, not hard to learn once you get the hang of it. Generally, people choose Lua since they think for them that it is much more easy. For some people it is easy. It is a recommended language to start off of to work your way up in WoW emulation.

so it's exactly the same, no one is more powerful than other, or have big avantage ? (not about learning the language itself),

thanks for the answer !
 

Seraphim

Noble Member
so it's exactly the same, no one is more powerful than other, or have big avantage ? (not about learning the language itself),

thanks for the answer !

It comes down to personal preference and comfort level. I prefer C++ not only because I edit the core and add scripts, but because I also started off using it.
 

Tommy42

Enthusiast
It comes down to personal preference and comfort level. I prefer C++ not only because I edit the core and add scripts, but because I also started off using it.

alright thanks you :),
when I have fixed my compiling problems ( cmake problems) and development environment ( Visual Studio 2013 has errors generation core)
i will do C++ haha !
 

Seraphim

Noble Member
alright thanks you :),
when I have fixed my compiling problems ( cmake problems) and development environment ( Visual Studio 2013 has errors generation core)
i will do C++ haha !

I've gotten fairly skilled at C++ and errors. So when I started Trinity, it didn't take long to figure out. Now it's just a manor of learning ALL of their functions and ways to make the structure of the script work best. As well as connect to the DB. (I hate that part).
 

doublefelix921

New member
Getting "unknown override specifier"

Hi there, I went through the first part of the guide and tried to compile. I am very new to Trinity Core, but everything was working well before this. I got the following error:

Code:
>C:\Trinity\src\server\scripts\Custom\examplefelix.cpp(29): error C3646: 'OVERRIDE': unknown override specifier
19>C:\Trinity\src\server\scripts\Custom\examplefelix.cpp(34): error C3646: 'OVERRIDE': unknown override specifier
19>C:\Trinity\src\server\scripts\Custom\examplefelix.cpp(41): error C3646: 'OVERRIDE': unknown override specifier
19>C:\Trinity\src\server\scripts\Custom\examplefelix.cpp(45): error C3646: 'OVERRIDE': unknown override specifier
19>C:\Trinity\src\server\scripts\Custom\examplefelix.cpp(49): error C3646: 'OVERRIDE': unknown override specifier
19>C:\Trinity\src\server\scripts\Custom\examplefelix.cpp(85): error C3646: 'OVERRIDE': unknown override specifier

and then later, when building worldserver, it quit:

Code:
21>LINK : fatal error LNK1181: cannot open input file '..\scripts\Release\scripts.lib'

As a test, I tried just copy-pasting the code from the guide ("The Complete Boss Script Part 1", bottom of the first post). I still got the same error (all I did was find and replace to change the name of the script).

My custom_script_loader.cpp in Trinity/src/server/scripts has

Code:
// This is where scripts' loading functions should be declared:
void AddSC_examplefelix();

// The name of this function should match:
// void Add${NameOfDirectory}Scripts()
void AddCustomScripts()
{
    AddSC_examplefelix();
}

My custom script is exactly the same but with "examplefelix" instead of "example":

Code:
examplefelix() : CreatureScript("examplefelix") { }

(Most of) the offending lines are:
Code:
void Reset() OVERRIDE
            {
                _Reset();
            }
            
            void EnterCombat(Unit* /*who*/) OVERRIDE
            {
                events.ScheduleEvent(EVENT_LIGHTNINGBOLT, 8000);
                events.ScheduleEvent(EVENT_HEALINGWAVE, 12000);
                events.ScheduleEvent(EVENT_FLAMESHOCK , 10000);
            }
            
            void KilledUnit(Unit * /*victim*/) OVERRIDE
            {
            }
            
            void JustDied(Unit * /*victim*/) OVERRIDE
            {
            }

            void UpdateAI(uint32 diff) OVERRIDE

Help would be appreciated :)
 
Top