Tutorial de Script C++ World of Warcraft 1

Iniciado por Nuzak, Abr 20, 2025, 02:29 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Todos los créditos a mariodanny91 por su publicación original en WoWCreador

Tutorial de Script C++ World of Warcraft 1:

En esta guía, enseñaré una de las maneras de hacer un script C++ para el emulador JadeCore  que está basado en TrinityCore antiguo.  Les servirá de conocimiento base para otros emuladores más actualizados y motivarlos a investigar mas sobre el tema. Vale aclarar que todo lo planteado aquí es de manera autodidacta no soy programador, solo comparto lo que he aprendido.

Para esta guia deben tener instalado Visual Studio, Cmake y otras dependencias que exija el core ya sea OpenSSL,Boost,Mysql en sus versiones x86 o x64.


Punto sobre los cuales hablaré:
-Comenzare con el link del source  You are not allowed to view links. Register or Login

-Como agregar el script C++ al source.
-Estructura del Script.

-Casteo de magias.
-Textos.

-Invocación de Npc.


Como agregar el script C++ al source.
Creamos un archivo en blanco ya sea con bloc de notas o con notepad++, en la carpeta \JadeCore548-patched\src\server\scripts\Custom, con extension .cpp

Yo voy a crear tutorial.cpp.
En el archivo CMakeLists.txt agregamos el script, en caso de no tenerlo lo pueden crear, debe quedar asi:

 
 
      C++:
 
# Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
#
# This file is free software; as a special exception the author gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
set(scripts_STAT_SRCS
  ${scripts_STAT_SRCS}
  Custom/tutorial.cpp
)
message("  -> Prepared: Custom")


Pasamos el source por Cmake.


En Visual Studio buscamos en archivo ScriptLoader.cpp (\JadeCore548-patched\src\server\game\Scripting\ScriptLoader.cpp)
Se dede declarar en dos lugares:

 
 
      C++:
 
1:
// Customs
void AddSC_npc_tutorial();
2:
void AddCustomScripts()
{
#ifdef SCRIPTS
    AddSC_npc_tutorial();
#endif
}


Estructura del Script.
Esta es una plantilla de Script de la cual partiremos:

 
 
      C++:
 
/*
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptMgr.h"
#include "ScriptPCH.h"
enum Npc
{
};
enum Texts
{
};
enum Spells
{
};
enum Events
{
};
class npc_tutorial : public CreatureScript
{
    public:
        npc_tutorial() : CreatureScript("npc_tutorial") { }
        struct npc_tutorialAI : public ScriptedAI
        {
            npc_tutorialAI(Creature* creature) : ScriptedAI(creature)
            {
            }
            void Reset()
            {            
            }
         
            void EnterCombat(Unit* who)
            {
            }
         
            void KilledUnit(Unit * victim)
            {
            }
         
            void JustDied(Unit * victim)
            {
            }
            void UpdateAI(uint32 const diff)
            {
                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
        {
            return new npc_tutorialAI(creature);
        }
};
void AddSC_tutorial()
{
    new npc_tutorial();
}


1-Enumerar los datos que vamos a utilizar:
(pueden utilizar la palabra que deseen (xxxxx) pero debe quedar asi)

 
 
      C++:
 
enum xxxxx
{
};


Para los npc:+
 
 
      C++:
 
enum Npc
{
    NPC_SUMMON = 32467,
};


Para los textos:
 
 
      C++:
 
enum Texts
{
    SAY_AGGRO       = 0,
    SAY_SUMMON      = 1,
    SAY_SLAY        = 2,
    SAY_DEATH       = 3,
};


Para las magias:
 
 
      C++:
 
enum Spells
{    
    SPELL_NUBE_ENFERMISA    = 28156,
    SPELL_DESGARRAR         = 59239,
    SPELL_RAJAR                = 40504,
    SPELL_TORBELLINO         = 24236,
};


Para los eventos:
 
 
      C++:
 
enum Events
{
    EVENT_DESGARRAR     = 1,
    EVENT_RAJAR         = 2,
    EVENT_TORBELLINO     = 3,
    EVENT_SUMMON         = 4,
};


2-Explicar cada funcion:

-Cuando la criatura se resetee es decir respanee.

 
 
      C++:
 
void Reset()
    {
 
    }


-Cuando la criatura entre en combate.

 
 
      C++:
 
void EnterCombat(Unit* who)
    {
     
    }


-Cuando la criatura mate, ya sea player o npc.
 
 
      C++:
 
void KilledUnit(Unit * victim)
    {
     
    }


-Cuando la criatura muera.
 
 
      C++:
 
void JustDied(Unit * victim)
    {
    }
   


3-Agregar los eventos con sus respectivos tiempos:
Al declararse en la funcion void EnterCombat empezara a contar el tiempo en el momento que empiece el combate.

 
 
      C++:
 
void EnterCombat(Unit* who)
    {
        Talk(SAY_AGGRO);
     
        me->CastSpell(me, SPELL_NUBE_ENFERMISA, true);
     
        events.ScheduleEvent(EVENT_DESGARRAR, 15000, 0);
        events.ScheduleEvent(EVENT_RAJAR, 5000, 0);
        events.ScheduleEvent(EVENT_TORBELLINO, 10000, 0);
        events.ScheduleEvent(EVENT_SUMMON, 20000, 0);
 
    }
   


4-Crear los eventos con sus respectivos spell, summon, textos, etc:
 
 
      C++:
 
void UpdateAI(uint32 const diff)
            {
                if (!UpdateVictim())
                    return;
                 
                events.Update(diff);
                if (me->HasUnitState(UNIT_STATE_CASTING))
                    return;
                 
                while (uint32 eventId = events.ExecuteEvent())
                {
                    switch (eventId)
                    {
                        case EVENT_DESGARRAR:
                        if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 20.0f, false))
                            DoCast(target, (SPELL_DESGARRAR));
                        events.ScheduleEvent(EVENT_DESGARRAR, 15000, 0);
                            break;
                    case EVENT_RAJAR:
                        DoCastVictim(SPELL_RAJAR);
                        events.ScheduleEvent(EVENT_RAJAR, 5000, 0);
                            break;
                    case EVENT_TORBELLINO:
                        DoCast(me, SPELL_TORBELLINO);
                        events.ScheduleEvent(EVENT_TORBELLINO, 10000, 0);
                            break;
                    case EVENT_SUMMON:
                        me->SummonCreature(NPC_SUMMON, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN);
                        events.ScheduleEvent(EVENT_SUMMON, 20000, 0);
                            break;
                         
                        default:
                            break;
                    }
                }
             
                DoMeleeAttackIfReady();
            }
        };
       


 
 
      Codigo:
 
   SELECT_TARGET_RANDOM                     //Selecciona objetivo aleatorios.
    SELECT_TARGET_TOPAGGRO                   //Selecciona objetivo desde mayor a menos amenaza.
    SELECT_TARGET_BOTTOMAGGRO                 //Selecciona objetivo desde menor a mayor amenaza.
    SELECT_TARGET_NEAREST                     //Selecciona objetivo mas cerca.
    SELECT_TARGET_FARTHEST                     //Selecciona objetivo mas lejos.
    TEMPSUMMON_TIMED_OR_DEAD_DESPAWN                    // despawns despues de un tiempo especificado o cuando la criatura desaparesca.
    TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN                  // despawns despues de un tiempo especificado o cuando la criatura muera.
    TEMPSUMMON_TIMED_DESPAWN                            // despawns despues de un tiempo especificado.
    TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT              // despawns despues de un tiempo cuando la criatura salga de combate.
    TEMPSUMMON_CORPSE_DESPAWN                           // despawns instantaneamente despues de morir.
    TEMPSUMMON_CORPSE_TIMED_DESPAWN                     // despawns despues de un tiempo especificado cuando muere.
    TEMPSUMMON_DEAD_DESPAWN                             // despawns cuando la criatura desaparece.
    TEMPSUMMON_MANUAL_DESPAWN
   


El Script deberia ir quedando asi:

 
 
      C++:
 
/*
* Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptMgr.h"
#include "ScriptPCH.h"
enum Npc
{
    NPC_SUMMON = 32467,
};
enum Texts
{
    SAY_AGGRO        = 0,
    SAY_SUMMON        = 1,
    SAY_SLAY        = 2,
    SAY_DEATH        = 3,
};
enum Spells
{
    SPELL_NUBE_ENFERMISA    = 28156,
    SPELL_DESGARRAR            = 59239,
    SPELL_RAJAR                = 40504,
    SPELL_TORBELLINO        = 24236,
};
enum Events
{
    EVENT_DESGARRAR        = 1,
    EVENT_RAJAR            = 2,
    EVENT_TORBELLINO    = 3,
    EVENT_SUMMON        = 4,
};
class npc_tutorial : public CreatureScript
{
    public:
        npc_tutorial() : CreatureScript("npc_tutorial") { }
        struct npc_tutorialAI : public ScriptedAI
        {
            npc_tutorialAI(Creature* creature) : ScriptedAI(creature)
            {
            }
            void Reset()
            {
            }
         
            void EnterCombat(Unit* who)
            {
                Talk(SAY_AGGRO);
             
                me->CastSpell(me, SPELL_NUBE_ENFERMISA, true);
             
                events.ScheduleEvent(EVENT_DESGARRAR, 15000, 0);
                events.ScheduleEvent(EVENT_RAJAR, 5000, 0);
                events.ScheduleEvent(EVENT_TORBELLINO, 10000, 0);
                events.ScheduleEvent(EVENT_SUMMON, 20000, 0);        
            }
         
            void KilledUnit(Unit * victim)
            {            
                Talk(SAY_SLAY);            
            }
            void JustDied(Unit * victim)
            {
                Talk(SAY_DEATH);
            }
            void UpdateAI(uint32 const diff)
            {
                if (!UpdateVictim())
                    return;
                 
                events.Update(diff);
                if (me->HasUnitState(UNIT_STATE_CASTING))
                    return;
                 
                while (uint32 eventId = events.ExecuteEvent())
                {
                    switch (eventId)
                    {
                    case EVENT_DESGARRAR:
                        if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 20.0f, false))
                            DoCast(target, (SPELL_DESGARRAR));
                        events.ScheduleEvent(EVENT_DESGARRAR, 15000, 0);
                            break;
                    case EVENT_RAJAR:
                        DoCastVictim(SPELL_RAJAR);
                        events.ScheduleEvent(EVENT_RAJAR, 5000, 0);
                            break;
                    case EVENT_TORBELLINO:
                        DoCast(me, SPELL_TORBELLINO);
                        events.ScheduleEvent(EVENT_TORBELLINO, 10000, 0);
                            break;
                    case EVENT_SUMMON:
                        Talk(SAY_SUMMON);
                        me->SummonCreature(NPC_SUMMON, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN);
                        events.ScheduleEvent(EVENT_SUMMON, 20000, 0);
                            break;
                 
                        default:
                            break;
                    }
                }
             
                DoMeleeAttackIfReady();
            }
        };
        CreatureAI* GetAI(Creature* creature) const
        {
            return new npc_tutorialAI(creature);
        }
};
void AddSC_tutorial()
{
    new npc_tutorial();
}


Explico que hace el npc sencillo:


Cuando entre en combate dirá un texto, se casteara una magia y :
cada 15 segundos casteara desgarrar a un objetivo aleatorio que este en un rango de 20 metros.

cada 10 segundos casteara rajar al objetivo con mas amenaza.
cada 5 segundos casteara torbellino en el lugar.

cada 20 segundos invocara un NPC en su posicion y dirá un texto.


Cuando mate dirá un texto.


Cuando muera dirá un texto.


Ahora vamos a la parte de SQL:

Crear el NPC con el nombre del script en la columna `ScriptName`:
 
 
      SQL:
 
INSERT INTO `creature_template`(`entry`, `difficulty_entry_1`, `difficulty_entry_2`, `difficulty_entry_3`, `difficulty_entry_4`, `difficulty_entry_5`, `difficulty_entry_6`, `difficulty_entry_7`, `difficulty_entry_8`, `difficulty_entry_9`, `difficulty_entry_10`, `difficulty_entry_11`, `difficulty_entry_12`, `difficulty_entry_13`, `difficulty_entry_14`, `difficulty_entry_15`, `KillCredit1`, `KillCredit2`, `modelid1`, `modelid2`, `modelid3`, `modelid4`, `name`, `subname`, `IconName`, `gossip_menu_id`, `minlevel`, `maxlevel`, `exp`, `exp_unk`, `faction_A`, `faction_H`, `npcflag`, `npcflag2`, `speed_walk`, `speed_run`, `speed_fly`, `scale`, `rank`, `mindmg`, `maxdmg`, `dmgschool`, `attackpower`, `dmg_multiplier`, `baseattacktime`, `rangeattacktime`, `unit_class`, `unit_flags`, `unit_flags2`, `dynamicflags`, `family`, `trainer_type`, `trainer_spell`, `trainer_class`, `trainer_race`, `minrangedmg`, `maxrangedmg`, `rangedattackpower`, `type`, `type_flags`, `type_flags2`, `lootid`, `pickpocketloot`, `skinloot`, `resistance1`, `resistance2`, `resistance3`, `resistance4`, `resistance5`, `resistance6`, `spell1`, `spell2`, `spell3`, `spell4`, `spell5`, `spell6`, `spell7`, `spell8`, `PetSpellDataId`, `VehicleId`, `mingold`, `maxgold`, `AIName`, `MovementType`, `InhabitType`, `HoverHeight`, `Health_mod`, `Mana_mod`, `Mana_mod_extra`, `Armor_mod`, `RacialLeader`, `questItem1`, `questItem2`, `questItem3`, `questItem4`, `questItem5`, `questItem6`, `movementId`, `RegenHealth`, `equipment_id`, `mechanic_immune_mask`, `flags_extra`, `ScriptName`, `WDBVerified`) VALUES
(1000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30721, 0, 0, 0, 'Tutorial', NULL, NULL, 0, 90, 90, 4, 0, 14, 14, 0, 0, 1, 1.14286, 1.14286, 1, 1, 1000, 2000, 0, 1000, 20, 2000, 2000, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1000, 2000, 1000, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 3, 1, 100, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 'npc_tutorial', 1);


Textos del NPC:
 
 
      SQL:
 
INSERT INTO `creature_text`(`entry`, `groupid`, `id`, `text`, `type`, `language`, `probability`, `emote`, `duration`, `sound`, `comment`) VALUES
(1000000, 0, 0, 'SAY AGGRO', 14, 0, 100, 0, 0, 17223, ''),
(1000000, 1, 0, 'SAY_SUMMON', 14, 0, 100, 25, 0, 17222, ''),
(1000000, 2, 0, 'SAY_SLAY', 14, 0, 100, 21, 0, 17214, ''),
(1000000, 3, 0, 'SAY_DEATH', 14, 0, 100, 16, 0, 17374, '');


Compilamos y que lo disfruten.
  •