Scene system

Scene scripts

Scene scripts are a way to provide a means of implementing more complicated behavior, which couldn't be placed in db. 

Generic structure

// headers (self-explaining)
#include "ScriptMgr.h"
#include "Player.h"


// enums to prevent magic numbers
enum EnumName
{
    GIVE_ME_A_NAME = 123456,
};


// naming scheme: scene_[spellname/description], for example scene_demon_hunter_start, scene_enter_the_illidari_ashtongue
class scene_your_name_here : public SceneScript
{
public:
    // should be the same as class name - name in "" is the one used by db in scene_template table
    scene_your_name_here () : SceneScript("scene_your_name_here ") { }
    
    // hook section starts here (see below for description)
    void OnSceneStart(Player* player, uint32 /*sceneInstanceID*/, SceneTemplate const* /*sceneTemplate*/) override
    {        
    }
    void OnSceneTriggerEvent(Player* player, uint32 /*sceneInstanceID*/, SceneTemplate const* /*sceneTemplate*/, std::string const& triggerName) override
    {        
    }
    void OnSceneCancel(Player* player, uint32 sceneInstanceID, SceneTemplate const* sceneTemplate) override
    {
    }
    void OnSceneComplete(Player* player, uint32 sceneInstanceID, SceneTemplate const* sceneTemplate) override
    {
    }
    
};

// this function call creates script loader object for us - seek that function at the end of the script file you're adding to
// if there's no such function see http://collab.kpsn.org/display/tc/How-to_CustomScript
void AddSC_scene_scripts()
{
    // an entry for our script loader, there may be other entries aswell
	/* ... */    
    new scene_your_name_here ();
}

SceneScript hooks

There are total 4 hooks you can use. 

    // As the name says code inside brackets is executed when scene starts, e.g. by spell or by "player->GetSceneMgr().PlayScene(SceneId);"
    void OnSceneStart(Player* player, uint32 /*sceneInstanceID*/, SceneTemplate const* /*sceneTemplate*/) override
    {        
    }
    // Code inside brackets is executed when CMSG_SCENE_TRIGGER_EVENT with similar sceneInstanceID as scene is sent
    void OnSceneTriggerEvent(Player* player, uint32 /*sceneInstanceID*/, SceneTemplate const* /*sceneTemplate*/, std::string const& triggerName) override
    {
        // IMPORTANT: you need to select correct triggerName otherwise your code is executed all the time if some triggerName is sent by client
        // triggerName is sent by CMSG_SCENE_TRIGGER_EVENT (check PacketStruct), you can find it also in parsed sniff or inside SceneScript.db2 (lua)
        if (triggerName == "IKILLEDIT")
            player->KilledMonsterCredit(KILLED_MOB);
        if (triggerName == "UPDATEPHASE")
            player->UpdateAreaAndZonePhase();        
    }
    // As the name says code inside brackets is executed when scene is canceled, e.g. player presses ESC and confirms
    void OnSceneCancel(Player* player, uint32 sceneInstanceID, SceneTemplate const* sceneTemplate) override
    {
    }
    // As the name says code inside brackets is executed when scene is properly completed
    void OnSceneComplete(Player* player, uint32 sceneInstanceID, SceneTemplate const* sceneTemplate) override
    {
    }

Note: triggerNames are fixed names by Blizzard.


Example Script

Core part:

enum SceneCreatures
{
    SEE_FELSABER_QUEST_KILL_CREDIT = 101534,
    SEVIS_BRIGHTFLAME_ASHTONGUE    = 99916
};


class scene_enter_the_illidari_ashtongue : public SceneScript
{
public:
    scene_enter_the_illidari_ashtongue() : SceneScript("scene_enter_the_illidari_ashtongue") { }

    void OnSceneStart(Player* player, uint32 /*sceneInstanceID*/, SceneTemplate const* /*sceneTemplate*/) override
    {
        //This must be Personal Spawn (NYI)
        Creature* sevis = GetClosestCreatureWithEntry(player, SEVIS_BRIGHTFLAME_ASHTONGUE, 5);
        if (sevis == nullptr)
            return;

        player->SummonCreature(SEVIS_BRIGHTFLAME_ASHTONGUE, sevis->GetPosition(), TEMPSUMMON_TIMED_DESPAWN, 18000);
        player->UpdateAreaAndZonePhase();
    }
    void OnSceneTriggerEvent(Player* player, uint32 /*sceneInstanceID*/, SceneTemplate const* /*sceneTemplate*/, std::string const& triggerName) override
    {
        if (triggerName == "SEEFELSABERCREDIT")
            player->KilledMonsterCredit(SEE_FELSABER_QUEST_KILL_CREDIT);
        if (triggerName == "UPDATEPHASE")
            player->UpdateAreaAndZonePhase();
    }
};


void AddSC_scene_scripts()
{
    new scene_enter_the_illidari_ashtongue();
}

DB part:

-- If scene_template entry exists
UPDATE `scene_template` SET `ScriptName` = 'scene_enter_the_illidari_ashtongue' WHERE  `SceneId` = 1053;

-- If scene_template entry is missing
DELETE FROM `scene_template` WHERE `SceneId` = 1053;
INSERT INTO `scene_template` (`SceneId`, `Flags`, `ScriptPackageID`, `ScriptName`) VALUES 
(1053, 20, 1451, 'scene_enter_the_illidari_ashtongue');