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');