Jump to content

[Scripting] HowTo: Add emote or text to creature


Corsol

Recommended Posts

Hi to everyone! I want to explain what i've learned to you all about ScriptDev2 script and dbScripts used for add emotes and said text to creature, npc etc...

English is not my main language so, if my text is not clear but you can understand what i want to say, please correct me!

First of all, what we need:

- Text Editor (Ex: Notepad++, gEdit, or other IDE to edit text files)

- SQL Editor (Ex: MySql Workbench, Squirrel or other MySQL IDE editor)

- A little knowledge on C++ and SQL languages

- A dbc file editor (Ex: mydbceditor)

- A MPQ file editor (Ex: Ladik's MPQ Editor)

The today's goal:

Add to npc "Prince Liam Greymane" his text and voice.

How to do it:

We have 3 ways to do it (thanks to Olion for his int):

- One way is by database scripts (dbscript_on_*) (SQL script)

- Second way is by EventAI scripts (creature_ai_*) (SQL script)

- Third way is by ScriptDev2/core script (C++ script);

We start with last way (ScriptDev2/core script):

As we can read about "Prince Liam Greymane" he is an NPC inside Gilneas City, so we need to edit the proper .cpp files.

In server\src\bindings\scripts\scripts\eastern_kingdoms folder we can find the "gilneas_city.cpp" file. We need to edit it.

We need 3 basic structures for script a creature or npc:

1) the enumeration structure that will contain global variables used inside script:

enum {
...
};

2) the main structure of creature or npc object:

struct MANGOS_DLL_DECL npc_prince_liam_greymane_phase1AI : public ScriptedAI
{
...
};

3) the main function called when his event were fired by ScriptDev core. This function simply call the previus struct declared

CreatureAI* GetAI_npc_prince_liam_greymane_phase1(Creature* pCreature)
{
   return new npc_prince_liam_greymane_phase1AI(pCreature);
}

As we can see i've named every object with a particular name to avoid duplicate names inside the same .cpp file. So is not important if it will be a long name, just use usefull name and don't use short or compressed names.

Now before we can start to put the variables with text ID inside enumeration, we need to write the SQL query to store text inside database.

We want to use first three phrases. So we need to build a SQL query.

The target table is inside scriptDev database (that name depend on what you choose during installation) and it's name is "scripts_text".

So the query we have to build and use will be something like this:

INSERT INTO script_texts (entry, content_default, sound, type, language, emote, comment) VALUES 
(-1654001, 'I want the perimeter secured and the gates manned by two guards at all times. No one gets in, no one gets out.', 19615, 0, 7, 424, 'npc_prince_liam_greymane_phase1'),
(-1654002, 'We protected Gilneas from the Scourge. We protected Gilneas during the Northgate rebellion. We will protect Gilneas from whatever this new threat may be.', 19616, 0, 7, 424, 'npc_prince_liam_greymane_phase1'),
(-1654003, 'Stand ready, guards! We don\'t know how many intruders we\'re dealing with, but the Headlands are overrun and we\'re cut off from the harbor towns. Expect to be outnumbered.', 19614, 0, 7, 424, 'npc_prince_liam_greymane_phase1');

Now look on table's column meaning (you can read the full infos at mangosDB pdf):

  • Entry: this column contains the NEGATIVE ID that identify the npc or creature text. The number is build in 3 block from left to right. First block is fixed to -1. Second block (of 3 digit) must be equals to map ID (in example 654 is the Gilneas Map ID). Last block (last 3 digit) is the incremental number of texts used in current Map ID. So we have the number: -1 654 001 for first text.
  • Content_default: this column contains the said text from npc or creature. You can copy it from npc or creature info page.
  • Sound: this column contains the ID of speech played from npc or creature. To get this ID you need to look inside "soundEntries.dbc" (extracted from WoW client during MaNGOS installation) and all "speech.MPQ" (located in Data and Data/EnGB or other locales folder of WoW client) files with tools linked on top. With the combination of both tools you can find in MPQ files the correct audio and than look for his name inside DBC file to get the proper ID.
  • Type: this column contains the way the text will be displayed: say, yell, text emote so on. (The value has to match with a language identifier defined in Languages.dbc).
  • Language: this column contains the typology value of in which language text is displayed (Ex: common, draenei, dwarf, tauren, etc...) (look on mangosDB pdf for avaiable values).
  • Emote: this column contains the typology value of which gesture npc or creature use when text is displayed (The value has to match with a emote identifier defined in Emotes.dbc).
  • Comment: this column contains a description of text. I've put the function's name to remember the relationship between text and his c++ script.

Now we have texts stored into database and we can start to write c++ code. So in enumeration structure we put the variable with texts ID defined before. The enum will be like this:

enum {
SAY_STORYLINE1		= -1654001,
SAY_STORYLINE2		= -1654002,
SAY_STORYLINE3		= -1654003
};

Always remember to use usefull variable names and don't be lazy :P.

Let's look on second code block, the main structure. To have text working you need something like this:

struct MANGOS_DLL_DECL npc_prince_liam_greymane_phase1AI : public ScriptedAI
{
   npc_prince_liam_greymane_phase1AI(Creature* pCreature) : ScriptedAI(pCreature) 	{Reset();}

uint32 m_uiSayStoryDelay;
   uint32 m_uiSayStoryTimer;
int m_uiSayStoryLast;

   bool m_bCanSayStory;

   void Reset() override
   {
	m_uiSayStoryDelay = 15000;
       m_uiSayStoryTimer = m_uiSayStoryDelay;
	m_uiSayStoryLast = 0;

       m_bCanSayStory = true;
   }

   void UpdateAI(const uint32 uiDiff) override
   {
	if (m_bCanSayStory)
	{
		m_uiSayStoryTimer = m_uiSayStoryDelay;

		// Random switch between 3 texts
		switch (m_uiSayStoryLast)
		{
			case 0: DoScriptText(SAY_STORYLINE1, m_creature); m_uiSayStoryLast++; break;
			case 1: DoScriptText(SAY_STORYLINE2, m_creature); m_uiSayStoryLast++; break;
			case 2: DoScriptText(SAY_STORYLINE3, m_creature); m_uiSayStoryLast = 0; m_uiSayStoryTimer = 60000; break;
		}

		m_bCanSayStory = false;
	}

	if (m_uiSayStoryTimer < uiDiff)
	{
		m_bCanSayStory = true;
		m_uiSayStoryTimer = m_uiSayStoryDelay;
	}
	else m_uiSayStoryTimer -= uiDiff;
   }
};

The main function "UpdateAI" is where you can setup all actions of your npc or creature. The function used to display in game text is "DoScriptText(text_id, creature)". This function require 2 parameter:

1) the text ID of which text will be displayed (stored inside the variable)

2) the creature object that will say the text (m_creature is the object of current creature)

Other parts of code are used to control when and how the text will be displayed.

Now we are close to the end of c++ part of scripting. We need last step.

In each cpp files there is one important function at the end of file. In gilneas_city.cpp file is called "AddSC_gilneas_city()". this function contains every script related to creature or object scripted inside files.

This step is absolutely necessary to make all thinks working.

To make our script working we had to have something like this:

void AddSC_gilneas_city()
{
   Script* pNewScript;

   pNewScript = new Script;
   pNewScript->Name = "npc_prince_liam_greymane_phase1";
   pNewScript->GetAI = &GetAI_npc_prince_liam_greymane_phase1;
   pNewScript->RegisterSelf();
}

As you can see we have created a Script object with struct defined before.

Now last step to make all things working.

We have to put the name assigned to Script object ("npc_prince_liam_greymane_phase1" in our script) into "creature_template" table of world database. For do this we have to build an UPDATE query like this:

UPDATE creature_template SET ScriptName='npc_prince_liam_greymane_phase1' WHERE entry='34850';

The query set "ScriptName" column value to name used on Script object and now our work is done.

Just recompile the server and run it. Our NPC is scripted!

Link to comment
Share on other sites

The second way to do our goal is by EventAI script:

First of all you need to create the texts inside the "creature_ai_texts" table to define what text will be displayed.

INSERT INTO creature_ai_texts (entry, content_default, sound, type, language, emote, comment) VALUES 
(-348501, 'I want the perimeter secured and the gates manned by two guards at all times. No one gets in, no one gets out.', 19615, 0, 7, 424, 'npc_prince_liam_greymane_phase1'),
(-348502, 'We protected Gilneas from the Scourge. We protected Gilneas during the Northgate rebellion. We will protect Gilneas from whatever this new threat may be.', 19616, 0, 7, 424, 'npc_prince_liam_greymane_phase1'),
(-348503, 'Stand ready, guards! We don\'t know how many intruders we\'re dealing with, but the Headlands are overrun and we\'re cut off from the harbor towns. Expect to be outnumbered.', 19614, 0, 7, 424, 'npc_prince_liam_greymane_phase1');

Query columns are equals to column of same query in last method explained before, except for first. This time the "Entry" value need to be a negativ value between -1 and -999999. To better identify texts for a creature raccomandations is to use the creature "id" as prefix, and add a counter to the end like 1, 2, 3, etc. So in our cas the creature "id" is 34850 and first entry will be "-348501" and go on with other.

Now that texts are defined this you have to create a new row inside "creature_ai_scripts" table in world database with a query like this:

INSERT INTO creature_ai_scripts (id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, event_param1, event_param2, event_param3, event_param4, action1_type, action1_param1, action1_param2, action1_param3, action2_type, action2_param1, action2_param2, action2_param3, action3_type, action3_param1, action3_param2, action3_param3, comment) VALUES 
('3485001', '34850', '29', '0', '100', '1', '15000', '15000', '60000', '60000', '1', '-348501', '-348502', '-348503', '0', '0', '0', '0', '0', '0', '0', '0', 'Prince Liam Greymane - Say in Phase 1')

Let's look on column meaning:

  • ID: this column contains the ID that identify the script. The number is equal to "creature_id"*100 + 1. So we have the number 3485 (creature_id) * 100 + 1 = 3485001 for first script. For more scripts related to the same creature just add 1 to last id.
  • creature_id: this column contains the creatureID taken from "creature_template" table.
  • event_type: this column contains the number that identify the event triggered for this script (look on mangosDB pdf for avaiable values).
  • event_flags: this column contains special flags for event (ex: repeatable event or other. Look on mangosDB pdf for avaiable values).
  • event_param1-4: this columns contains parameters related to event specified (look on mangosDB pdf for avaiable values).
  • action1-3_type: this columns contains the actions that you want to be executed by creature. For display text action code is equal to 1 (look on mangosDB pdf for avaiable values).
  • action1-3_param1-3: this columns contains the id of parameters needed by action specified. To say text you need to specify the id of text row contained in table "creature_ai_texts". (look on mangosDB pdf for avaiable values).

With this script we have a random text betwee three declared. First will start after 15 seconds, and other will be displayer each one after 60 seconds.

Now you need to activate the EventAI script adding last database row:

UPDATE creature_template SET `AIName` = 'EventAI' WHERE `entry` = 34850;

With this modify you tell to EventAI manager that this creature has scripts to be executed and all will start.

That's all!

I've not tested yet the first method so i can't explain how to work.

Thanks for reading!

Tell me if something is not clear or wrong!

Cheers :)

Link to comment
Share on other sites

The problem is:

i've configured the "creature_ai_scripts" table and "creature_ai_texts" table but npc won't display text.... probably i forgot some steps to make all work, or probably my configuration had some errors...

can you suggest me the solution to use "creature_ai_scripts" table to make the npc speaking?

Thanks

Link to comment
Share on other sites

  • 6 months later...

Excellent :) Just adding a few notes.

Antz was correct above in that the task should be set up first. The presented SD2 script will work, but on what condition should it start to? Now the NPC will behave like trained parrot, repeating the text infinitely. It is barely a soluiton.

How to do it:

We have 2 ways to do it:

- One way is by ScriptDev2 script (C++ script);

- the second way is by dbScripts (SQL script).

Actually, the Three core has 3 ways to solve the task. In the order of increasing power and complexity, these are:

  • database scripts (dbscript_on_*)
  • EventAI scripts (creature_ai_*)
  • SD2/core scripts

Also, in the Three branch Rel20_Newbuild the 4th way is enabled: Eluna scripts with power just a bit under the SD2.

So, if the action should start at a gossip/quest accept so on, all 3 ways are available. If this would be "at player near the mob" condition, the simplest way (dbscript_on_*) will not be available. An even more involved condition will stick us to SD2 only.

Now look on table's column meaning (you can read the full infos at mangosDB pdf):


  • [skipped]
  • Type: this column contains the typology value of how the text is displayed (The value has to match with a language identifier defined in Languages.dbc).

More exactly, this defines the way the text will be displayed: say, yell, text emote so on.

The function used to display in game text is "DoScriptText(text_id, creature)". This function require 2 parameter:

1) the text ID of which text will be displayed (stored inside the variable)

2) the creature object that will say the text (m_creature is the object of current creature)

Here the things are even more funny but usually not known widely :)

The method discussed takes 3 parameters actually. The second one ("source", i.e. something talking) has WorldObject* type. This is anything present in the World so having coordinates, in particular, a GameObject. So it looks like we can in principle make a GO to speak. The third parameter is even more interesting: Unit*, defining "target". In what way the talk is "targeted" to someone? Under proper database support using special text placeholders.

Consider an example: a NPC talks to the group of druid BigBearAss and paladin TinCan, the replica said is written in DB as "Follow me, $N." Call without the 3rd parameter will lead to the substitution of the player name at the client level. So druid will hear "Follow me, BigBearAss.", while paladin - "Follow me, TinCan." Now change the replica to "You are unworthy to wee on my shoes, $N!" and allow this harsh action only to druids but to no other class. To set both characters hearing the same name, define the 3rd parameter as Player* to the paladin char. Now, both characters will hear the same "You are unworthy to wee on my shoes, TinCan!", i.e. $N on all clients will be substituted by the name of the "target". It is a quite helpful feature. Note also that 1) there are other placeholders besides $N; 2) there are many places in the DB textes where actual name, class so on were substituted erroneously instead of corresponding $ placeholders (well, the wowhead replicas never/rarely contain placeholders).

The second way to do our goal is by dbScripts:

Let's reserve this term for event-driven DB scripts defined in the tables dbscript_on_*. The way above must be named "EventAI script". Also, when you realize the condition for the NPC to speak, choosing correct event type will become evident.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. Privacy Policy Terms of Use