[C++] Gossip Menü

Guten Abend!

Ich habe ein Gossip Übungsbeispiel geschrieben und es in den Custom Ordner gepackt (FILENAME: gossip_test.cpp). Nun in der CMakeLists.txt rein und neu Configuriert und compiler gestartet. Doch dann kommt leider die Fehlermeldung


/home/trinity/source/335a/TrinityCore/src/server/scripts/Custom/gossip_test.cpp:4: error: expected initializer before ‘:’ token

make[2]: *** [src/server/scripts/CMakeFiles/scripts.dir/Custom/gossip_test.cpp.o] Fehler 1

make[1]: *** [src/server/scripts/CMakeFiles/scripts.dir/all] Fehler 2

Was meint er genau mit Initializer? Habe doch den ‘:’ gesetzt!

Hier nochmal mein Script!


#include "ScriptPCH.h"


// Klasse erstellen

class SCRIPT_DECL Mein_Gossip : public GossipScript

{

public:

	// Fähigkeiten (Methoden) erstellen!

	void GossipHello( Object* pObject, Player* Plr, bool AutoSend )

	{

		// GossipMenu erstellen (MainFrame)

		GossipMenu* Menu; // Nur in der Funktion verfügbar


		// Dem ObjectManager mitteilen das wir ein neues GossipMenu erstellen.

		// ErstelleGossipMenüFürSpieler

		// &Menu <- Inhalt => Adresse von GossipMenu* Menu;

		// pObject->GetGUID() -> Die Einzigartige ID des NPCs

		// Plr => Zeiger auf den aktiven Spieler!

		objmgr.CreateGossipMenuForPlayer( &Menu, pObject->GetGUID(), 1, Plr);


		// Erstellen eines Selectors (Zeile | Auswahl)

		// ( icon, "Message", optionId )

		Menu->AddItem( 1, "Menue1", 1);

		Menu->AddItem( 1, "Test Selector 2", 2);


		// Menü nun zum Spieler auf den Bildschirm senden!

		if(AutoSend)

		Menu->SendTo(Plr); // an -> Plr (Player)

	}


	void GossipSelectOption( Object* pObject, Player* Plr, uint32 Id, uint32 intid, const char* Code )

	{

		Creature* pCreature = ( pObject->GetTypeId() == TYPEID_UNIT ) ? (( Creature*) pObject ): NULL;

		// Existiert bereits der Zeiger auf die Creature?

		if( pCreature == NULL )

		return // Ausgang!


		GossipMenu* Menu;


		switch(intid)

		{

			// Hier wird die Auswahl überprüft!

			case 0:

				{

					GossipHello(pObject, Plr, true);

				}

				break;


			case 1:

				{

					Plr->GossipComplete();

				}

			break;


			case 2:

				{

					Plr->EventTeleport( 1, 1395, -4367, 26 );

				}

			break;

		}

	}


	void GossipEnd( Object* pObject, Player* Plr )

	{

		// Beenden des Gossip Menü's

		GossipScript::GossipEnd( pObject, Plr );

	}


	void Destroy()

	{

		delete this;

	}

};


void SetupMein_Gossip( ScriptMgr* mgr )

{

	// Gossip Menü im Speicher registrieren!

	GossipScript* gs = ( GossipScript* ) new Mein_Gossip();

	mgr->register_gossip_script( 713 , gs );

}

das geht so auch nicht

so mus es ausehen


#include "ScriptPCH.h"


#define GOSSIP_TEXT_npc_xxx     "dein text"

#define GOSSIP_TEXT_npc_xxxx     "dein text"


class npc_xxx : public CreatureScript

{

   public: npc_xxx() : CreatureScript("npc_xxx") { }


    bool OnGossipHello(Player* pPlayer, Creature* pCreature)

   {

      pPlayer->ADD_GOSSIP_ITEM( 2, GOSSIP_TEXT_npc_xxx , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF +1);

      pPlayer->ADD_GOSSIP_ITEM( 2, GOSSIP_TEXT_npc_xxxx , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF +2);

      pPlayer->SEND_GOSSIP_MENU(0,pCreature->GetGUID());

    return true;

   }


   void SendDefaultMenu_ACTION(Player* pPlayer, Creature* pCreature, uint32 uiAction)   

  {

    switch (uiAction)

   {

      case GOSSIP_ACTION_INFO_DEF +1: 

      pPlayer->TeleportTo(m, x, y, z, o);

      pPlayer->CLOSE_GOSSIP_MENU();

     break;

     case GOSSIP_ACTION_INFO_DEF +2: 


     uint32 itemId = 0000; 

     ItemPosCountVec dest;

     InventoryResult msg = pPlayer->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, 1);


     if (msg == EQUIP_ERR_OK)

     {

	if (Item* item = pPlayer->StoreNewItem(dest,itemId,true))

       {

	   pPlayer->SendNewItem(item,1,false,true);

       } 

     }

     else

        {

          pPlayer->SendEquipError(msg, NULL, NULL, itemId);


       }

     break;		

  }


  bool OnGossipSelect(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)

  {

   if (uiSender == GOSSIP_SENDER_MAIN)

	SendDefaultMenu_ACTION(pPlayer, pCreature, uiAction);


   return true;

  }	 


};


void AddSC_npc_xxx()

{

    new npc_xxx();

}

Das Script sieht ein wenig aus wie das damalige aus der alten TC1. Das wurde ja nicht umsonst irgendwann rausgeworfen. Aber Robox hat schön gezeigt wie es richtig geht.

Guten Morgen!

Danke schön… ich habe viel aus alten Scripten etc abgeleitet und dann hier zusammen gebastelt. Also SCRIPT_DECL wird nicht mehr benötigt wie ich sehe… das gefällt mir^^

Doch da ich das lerne, wäre es super nett wenn mir jemand die Wichtigen Stellen etwas Kommentieren könnte. Oder gibt es eine Seite auf der alles wie z.B.

ADD_GOSSIP_ITEM()

SEND_GOSSIP_MENU()

etc

Referenziert (erklärt) wird?

Wenn nicht wäre es super nett wenn mir einer ein Paar Kommentare in das Script von Robox einfügen könnte. So haben alle die es noch nicht so gut können die Möglichkeit aus den Kommentaren zu lernen /emoticons/default_smile.png

Wäre super nett /emoticons/default_smile.png

Liebe Grüße

Yellow

Ich habs mal korrigiert und ein paar Kommentare hinzugefügt.


#include "ScriptPCH.h"


#define GOSSIP_TEXT_npc_xxx     "dein text"

#define GOSSIP_TEXT_npc_xxxx     "dein text"


class npc_xxx : public CreatureScript

{

   public: npc_xxx() : CreatureScript("npc_xxx") { }

    // xxx steht hier jeweils für den Namen des NPC

    // in der regel erhällt er im Namen ein Attribut, um ihn später besser klassifizieren zu können

    // etwa npc_xxx  oder boss_xxx   oder mob_xxx   usw.

    // OnGossipHello wird aufgerufen, wenn der Spieler auf den NPC rechtsklickt

    bool OnGossipHello(Player* pPlayer, Creature* pCreature)

    {

        pPlayer->ADD_GOSSIP_ITEM( 2, GOSSIP_TEXT_npc_xxx , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);

        // ADD_GOSSIP_ITEM fügt einen anwählbaren Menüpunkt hinzu

        // GOSSIP_TEXT_npc_xxx enthällt den anklickbaren Text

        // GOSSIP_ACTION_INFO_DEF +1 ist der Wert action, der an OnGossipSelect übergeben wird

        pPlayer->ADD_GOSSIP_ITEM( 2, GOSSIP_TEXT_npc_xxxx , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);

        // SEND_GOSSIP_MENU schickt das Menü los, fehlt der Punkt, wird zwar ein Menü generiert,

        // landet aber nicht im Fenster, welches dem Spieler angezeigt wird

        pPlayer->SEND_GOSSIP_MENU(0,pCreature->GetGUID());

        return true;

    }


    void SendDefaultMenu_ACTION(Player* pPlayer, Creature* pCreature, uint32 uiAction)   

    {

        // uiAction ist der Menüpunkt, den der Spieler gewählt hat

        // in unserem Beispiel etwa GOSSIP_ACTION_INFO_DEF + 1

        switch (uiAction)

        {

            // hier wird nun konkret entsprechend des gewählten Menüpunktes

            // ein bestimmter Code durchlaufen

            // Achtung! Niemals das break; am Ende von case *: vergessen

            // innerhalb dieser action's kann alles mögliche gemacht werden wir

            // Teleportieren, Gold adden, Spells lernen...im Grunde kann man alles machen

            case GOSSIP_ACTION_INFO_DEF + 1: 

                pPlayer->TeleportTo(m, x, y, z, o);

                // CLOSE_GOSSIP_MENU() schließt das Fenster, welches der Spieler vom NPC sieht

                pPlayer->CLOSE_GOSSIP_MENU();

                break;

            case GOSSIP_ACTION_INFO_DEF + 2: 

                uint32 itemId = 0000; 

                ItemPosCountVec dest;

                InventoryResult msg = pPlayer->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, 1);


                if (msg == EQUIP_ERR_OK)

                {

                    if (Item* item = pPlayer->StoreNewItem(dest,itemId,true))

                    {

                        pPlayer->SendNewItem(item,1,false,true);

                    } 

                }

                else

                {

                    pPlayer->SendEquipError(msg, NULL, NULL, itemId);


                }

                break;             

        }

    }


    // man kann den Code aus endDefaultMenu_ACTION auch direkt in OnGossipSelect

    // die hier gewählt Variante ist lediglich ein wenig sicherer, was aber kaum noch genutzt wird

    bool OnGossipSelect(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)

    {

        // die Abfrage vergleicht, ob der im Code gespeicherte Nutzer des Menüs immernoch

        // gleich dem Nutzer des Menüs ist...klingt bissi komisch, gehört aber so

        if (uiSender == GOSSIP_SENDER_MAIN)

            SendDefaultMenu_ACTION(pPlayer, pCreature, uiAction);


        return true;

    }      

};


void AddSC_npc_xxx()

{

    new npc_xxx();

}

Mich herzlichen dank für deine Kommentierung… Genau an sowas habe ich gedacht. Nur schade du du nicht genauer auf das mit dem Item eingegangen bist, da bei mir dies mit den Items nicht von der Core ungesetzt wird. Ich habe auch den Code etwas anpassen müssen da Sachen mit dem {} nicht funktionierten.

Leider mußte ich den Item Block auskommentieren da es als Probleme mit der Variable msg gegeben hat.


#include "ScriptPCH.h"


#define GOSSIP_TEXT_npc_xxx     "Teleport Eisklammtal"

#define GOSSIP_TEXT_npc_xxxx     "Teleport Durotar"


class npc_xxx : public CreatureScript

{

public: npc_xxx() : CreatureScript("npc_xxx") { }


    bool OnGossipHello(Player* pPlayer, Creature* pCreature)

   {

		pPlayer->ADD_GOSSIP_ITEM( 2, GOSSIP_TEXT_npc_xxx , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF +1);

		pPlayer->ADD_GOSSIP_ITEM( 2, GOSSIP_TEXT_npc_xxxx , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF +2);

		pPlayer->SEND_GOSSIP_MENU(0,pCreature->GetGUID());

		return true;

   }


	void SendDefaultMenu_ACTION(Player* pPlayer, Creature* pCreature, uint32 uiAction)   

	{

		switch (uiAction)

		{

			case GOSSIP_ACTION_INFO_DEF +1: 

			pPlayer->TeleportTo( 0, -6239.38, 331.78, 382.87, 6.05 );

			pPlayer->CLOSE_GOSSIP_MENU();

			break;


			case GOSSIP_ACTION_INFO_DEF +2:


			pPlayer->TeleportTo( 1, -616.84, -4256.11, 38.94, 0.40 );

			pPlayer->CLOSE_GOSSIP_MENU();

			break;


			/* Item vergabe mal ausgeklammert! 

			uint32 itemId = 5967; 

			ItemPosCountVec dest;

			InventoryResult msg;

			msg = pPlayer->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, 1);


			if (msg == EQUIP_ERR_OK)

			{

				if (Item* item = pPlayer->StoreNewItem(dest,itemId,true))

				{

					pPlayer->SendNewItem(item,1,false,true);

				}

			} 

			else

			{

				pPlayer->SendEquipError(msg, NULL, NULL, itemId);


			}

			break;

			*/

		}

	}


	bool OnGossipSelect(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)

	{

		if (uiSender == GOSSIP_SENDER_MAIN)

		{

			SendDefaultMenu_ACTION(pPlayer, pCreature, uiAction);

        }        

		return true;

	}      

};


void AddSC_npc_xxx()

{

    new npc_xxx();

};

Ich habe dies in die CMakeLists.txt eingetragen. Und wird auch mit Kompiliert. Da ich hier nirgends eine NPC-ID eingeben kann^^ gehe ich mal davon aus das es ein Script ist was ich einfach nur bei CREATURE_TEMPLATE (DB) bei der Creature bei Script eingeben muß oder? Dort gab ich in meinem fall ‘gossip_test’ ein!

Die Flag ist auf 2 aber leider Zeigt mir der NPC kein Menü an. Was mache ich falsch?

Liebe grüße an alle

Yellow

PS: Muß ich immer die Komplette Core neu kompilieren ?^^

Ich habe mich bei der Prüfung nicht um den Item Code gekümmert, da der an sich erst einmal nix mit dem Gossip zu tun hat. Wie erwähnt, kann man mit einem Gossip im Grunde genommen alles machen. Wirklich alles. Unmöglich, da auf irgendwas konkret einzugehen.

Das solltest du dann lieber nochmal gesondert Fragen. Man weis an der Stelle ja nichtmal, was du da eigentlich vor hast. Das solltest du erst mal beschreiben, dann kann man dein Beispiel überhaupt erst mal analysieren.

Was den Gossip an sich angeht:

public: npc_xxx() : CreatureScript("npc_xxx") { }

Was dort in " " steht, ist der Scriptname, welchen du in das entsprechende Feld in der creature_template einträgst.

Kompiliert muss grundsätzlich nur dann etwas neu, wenn sich darin etwas geändert hat. Es sei denn, es handelt sich um eine Header (.h) Datei. Da diese in vielen Dateien importiert sind, müssen all jene Dateien neu kompiliert werden. Da hier aber keiner weis, womit und worauf du kompilierst, kann dir keiner sagen, ob bei dir alles richtig läuft.

Hast du jedenfalls neu kompiliert und installiert, die Änderung in der DB gemacht und den NPC gespawnt, solltest du ihn auch ansprechen können. Wenn nicht, solltest du mal genau beschreiben, was denn passiert, wenn du ihn anklickst und hier auch das genaue Script eingeben, das du kompiliert hast. Sonst wirds ein Ratespiel.

p.s. Die Flag für Gossip ist 1. Zwei ist ein Questgeber.

Ok das du das mit dem Item nicht Kommentiert hast, leuchtet nun ein!

Hatte im Kopf die Flags komplett verhauen und 2 statt 1 eingetragen.

npcFlag = 1

Script = npc_xxx

Creature wird angezeigt mit Gossip Icon an der Maus. Aber leider kein Menü!

Cache gelöscht Server neugestartet und dennoch zeigt er kein Gossip an. Beim Compilieren null Fehler.

Ich nutze Debian 6 Squeeze (32 Bit)!

Was das kompilieren angeht, ich meinte wenn ich einfach nur ein CPP File in den Custom Ordner ablege und in die Cmake liste eintrage. Muß ich dann Server und Core neu kompilieren oder reicht es nur die Scripts neu zu kompilieren? /emoticons/default_smile.png

Danke für eure Gedult und Hilfe!

Yellow

PS: Ich mache das alles nur um zu verstehen wie ein Gossip als CPP-Script funktioniert… /emoticons/default_wink.png

Es sollte genügen, wenn du nur das entsprechende File neu kompilierst.

Du erhällst also kein Gossip, hast aber das Zeichen. Dann stimmt es wenigstens schonmal in der DB. Schau doch mal ans Ende deiner WorldLog, ob dort beim Laden der Scripte gemeldet wird, ob ein Script nicht geladen wird. Wenn ja, dann poste mal den entsprechenden Abschnitt.

Und was hast du genau im Scriptloader geändert?

hast du dein Script auch in die 2 x ScriptLoader.cpp eingetragen.

Ich versuch das mal in verständliches Deutsch zu übersetzen:

Hast du auch daran gedacht, dass du im ScriptLoader nicht nur einmalig das ganze schreiben musst. Das war es auch, was ich mit meinem letzten Satz im letzten Post andeuten wollte. Gemeinerweise muss das nicht zwangsläufig einen Fehler beim Kompilieren geben. Du deklarierst das Script, rufst es aber nicht auf. Drum fehlt das Gossip. Jedenfalls ist das so, wenn du es nur einmal drin stehen hast (also deklariert hast).

Ok es liegt wohl am ScriptLoader. Wo ist der ?^^

Scriptloader gefunden und folgendes am Ende gemacht!


#ifdef SCRIPTS

/* This is where custom scripts' loading functions should be declared. */

   void AddSC_npc_xxx(); // Funktion deklarieren!


#endif


void AddCustomScripts()

{

#ifdef SCRIPTS

    /* This is where custom scripts should be added. */

   AddSC_npc_xxx();     // Test Gossip-Script loading!


#endif

Und was das Compilieren angeht. Wie compiliere ich das script für den Server ohne die Core?

: Nun teste ich das wieder mit


cmake ../ -DPREFIX=/home/trinity/server/335a/ 

make -j 2 && make install

Omg hör mich an wie ein Blutiger Anfänger… aber leider bin ich das bei Trinity auch^^ /emoticons/default_smile.png

PS: Ich habe garkeine World.log oder meinst du die Server.log?

Lass das cmake weg und schon ists okay. Sollte dann nur wie beschrieben geänderte Dateien kompilieren. Du musst auch nicht make und make install gesondert ausführen.

Nutz einfach make install -j2 (2 steht für die Anzahl an Prozesse, die für den compile genutzt werden…nimm hier die Anzahl an CPU Kernen, die du zur Verfügung hast)

Ok super danke schön!

Jetzt läuft aber imo noch der Ganze Vorgang^^ mal schauen was gleich mit dem Gossip ist /emoticons/default_smile.png


Hm ok nun fertig compiliert ohne Fehler. Mein Cache gelöscht und den Server neu gestarter. Nun NPC neu spawnen lassen und es passiert folgendes.

Sprechblase ist da.

Rechtsklick drauf → Redet mit mir und mein Character macht die Handbewegung zum sprechen.

Kein Menü nix!

Laufe ich nun weg, dann kommt nach ca 3-5 metern “Auf Wiedersehen”.

Also es scheint etwas anzukommen aber das Menü wird nicht an mich übergeben /emoticons/default_sad.png

Was läuft da falsch? /emoticons/default_sad.png

EDIT: Lösung im Forum gefunden!


vorher

pPlayer->SEND_GOSSIP_MENU(0,pCreature->GetGUID());


nachher

pPlayer->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE,pCreature->GetGUID());

Danke an alle die mir geholfen haben! Und für die Gedult /emoticons/default_wink.png

Schade das kein Spoiler geht!


#include "ScriptPCH.h"


#define GOSSIP_TEXT_npc_xxx     "Teleport Eisklammtal"

#define GOSSIP_TEXT_npc_xxxx     "Teleport Durotar"


class npc_xxx : public CreatureScript

{

public: npc_xxx() : CreatureScript("npc_xxx") { }


    bool OnGossipHello(Player* pPlayer, Creature* pCreature)

   {

		pPlayer->ADD_GOSSIP_ITEM( 2, GOSSIP_TEXT_npc_xxx , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF +1);

		pPlayer->ADD_GOSSIP_ITEM( 2, GOSSIP_TEXT_npc_xxxx , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF +2);

		pPlayer->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE,pCreature->GetGUID());

		return true;

   }


	void SendDefaultMenu_ACTION(Player* pPlayer, Creature* pCreature, uint32 uiAction)   

	{

		switch (uiAction)

		{

			case GOSSIP_ACTION_INFO_DEF +1: 

			pPlayer->TeleportTo( 0, -6239.38, 331.78, 382.87, 6.05 );

			pPlayer->CLOSE_GOSSIP_MENU();

			break;


			case GOSSIP_ACTION_INFO_DEF +2:


			pPlayer->TeleportTo( 1, -616.84, -4256.11, 38.94, 0.40 );

			pPlayer->CLOSE_GOSSIP_MENU();

			break;


			/* Item vergabe mal ausgeklammert! 

			uint32 itemId = 5967; 

			ItemPosCountVec dest;

			InventoryResult msg;

			msg = pPlayer->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, 1);


			if (msg == EQUIP_ERR_OK)

			{

				if (Item* item = pPlayer->StoreNewItem(dest,itemId,true))

				{

					pPlayer->SendNewItem(item,1,false,true);

				}

			} 

			else

			{

				pPlayer->SendEquipError(msg, NULL, NULL, itemId);


			}

			break;

			*/

		}

	}


	bool OnGossipSelect(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)

	{

		if (uiSender == GOSSIP_SENDER_MAIN)

		{

			SendDefaultMenu_ACTION(pPlayer, pCreature, uiAction);

        }        

		return true;

	}      

};


void AddSC_npc_xxx()

{

    new npc_xxx();

};

Huhu, wollte mal fragen, ob der NPC dann, wenn man so ein Gossip gestartet hat, auch auf gesendete Nachrichten also im Sinne von /say entgegen nehmen kann.

Wenn ja wäre ich sehr erfreut, wenn mir das einer erläutern kann.

Du meinst sowas wie

Plr sagt zum NPC /say Hi und NPC Portet z.B. einen wo hin?

Ja sowas meinte ich /emoticons/default_wink.png Ist das möglich ?

Also erst wird der NPC angesprochen, dieser startet dann sein Script und an einer bestimmten stelle erwartet er dann eine antwort.

Diese Antwort sollte möglichst gegeben werden können ohne diesen erneut anzusprechen und die Gossip Option auszuwählen, einfach um es ein bisschen “Interessanter” zu gestalten.

hm ok… also ein rechtsklick auf einen NPC ist ja ein Ereignis. Doch leider bin ich noch nicht gut genug um dir zu sagen welches Ereignis da angesprochen wird!

Hoffen wir mal das es einer von unseren Codern im Forum weis /emoticons/default_smile.png

Naja. Das mit dem Chat könnte man schon machen über nen Hook oder direkt in den ChatHandler gehackt. Erscheint mir aber wenig sinnvoll sowas. Möchtest du ein Frage Antwort Spielchen machen, kannst du das auch über Eingabefenster machen. Um dahinter zu steigen, wie das klappt, kannst du dir zum Beispiel das Erstellen einer Gilde anschaun, oder eines Arenateams. Dort werden Eingabefenster gehandelt.

Da das dann aber nix mehr mit nem Gossip zu tun hat, solltest du dafür nen neuen Thread aufmachen.

Achso ok, dann wird das ganze ja doch etwas schwieriger als gedacht, dann mach ich doch das ganze lieber über das Gossip Script.

Aber danke für die Hilfe /emoticons/default_smile.png