Skip to content

c++ SCript for the Quest Zero Tolerance and spellfix spell_zuldrak_gymers_grab #31196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: 3.3.5
Choose a base branch
from

Conversation

Thor1423
Copy link

@Thor1423 Thor1423 commented Aug 5, 2025

based on this Commit #30286 (comment)

I personally think that the whole thing can be designed better with c++, so here is my c++ idea

Here's my link with the C++ code and two videos
#30286 (comment)

UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'npc_servant_of_drakuru' WHERE `entry` = 28802;

 -- Servant of Drakuru smart ai
SET @ENTRY := 28802;
DELETE FROM `smart_scripts` WHERE `source_type` = 0 AND `entryOrGuid` = @ENTRY;

-- Servant of Drakuru Text
DELETE FROM `creature_text` WHERE `CreatureID`=@ENTRY;
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
(@ENTRY, 0, 0, "Darmuk must die, mon!", 12, 0, 100, 0, 0, 0, 28884, 0, 'Servant of Drakuru - Say on Charm');

This is my first PR so please forgive me if I forgot something

based on this Commit TrinityCore#30286 (comment) 

I personally think that the whole thing can be designed better with c++, so here is my c++ idea

Here's my link with the C++ code and two videos
TrinityCore#30286 (comment)
@Thor1423 Thor1423 changed the title Update zone_zuldrak.cpp c++ SCript for the Quest Zero Tolerance Aug 5, 2025
Fix Register AI Class
}

enum Events
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this must be outside of class

npc_servant_of_drakuruAI(Creature* creature)
: ScriptedAI(creature), _controller(nullptr), _checkTimer(1000), _controlled(false)
{
_events.Reset();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't manipulate EventMap in ctor


void SpellHit(WorldObject* caster, SpellInfo const* spell) override
{
if (!spell)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SpellInfo cannot ever be null here

{
if (caster && caster->GetTypeId() == TYPEID_PLAYER)
{
Player* player = caster->ToPlayer();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is redundant, cast ToPlayer and then check for nullability in that case


void UpdateAI(uint32 diff) override
{
if (_controlled)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this checks per ms, use an event to delay the checks by 500ms-1s

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essentially, the script does exactly what it's supposed to, and therefore there were no compilation errors in the logs or in the game.

I assumed it was solid code that I had created with the help of a friend from back then. You can see in the video that it does what it's supposed to.

But okay, I'm not a professional programmer and I'm not afraid of corrections during the learning phase. So I'm sorry my first attempt isn't good enough for you.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just because a piece of code doesn't throw errors, exceptions, or cause crashes doesn't mean it's optimized to its best. You don't need to check that particular case every tick of Update, it's okay to delay it by 500-1000ms to optimize resources; it's not a deal of not being good enough for me, it's just a suggestion you can take or leave.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just because a piece of code doesn't throw errors, exceptions, or cause crashes doesn't mean it's optimized to its best. You don't need to check that particular case every tick of Update, it's okay to delay it by 500-1000ms to optimize resources; it's not a deal of not being good enough for me, it's just a suggestion you can take or leave.

That wasn't what I meant. I'll take it to heart and try to optimize it. As I said, I'm still learning and don't know everything yet, and this is my first attempt at a PR. When I get home, I'll try to implement it, so there's a one-time delay. You're right that once the action is completed, no further updates are necessary.

Copy link
Author

@Thor1423 Thor1423 Aug 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nyr97

I have now revised it and would like to submit it as is.

#include "ScriptMgr.h"
#include "Creature.h"
#include "SpellInfo.h"
#include "Unit.h"
#include "Player.h"
#include "ScriptedCreature.h"

enum
{
    SPELL_TRIGGER_ID = 52390,
    NPC_ENTRY_CONTROLLED = 28805,
    QUEST_ID = 12686,
    SPELL_CAST = 50361,

    EVENT_CAST_SPELL = 1,
    EVENT_CONTROL_CHECK = 2,
};

class npc_servant_of_drakuru : public CreatureScript
{
public:
    npc_servant_of_drakuru() : CreatureScript("npc_servant_of_drakuru") {}

    struct npc_servant_of_drakuruAI : public ScriptedAI
    {
        npc_servant_of_drakuruAI(Creature* creature): ScriptedAI(creature), _controller(nullptr), _controlled(false) { }

        void JustEngagedWith(Unit* /*who*/) override
        {
            _events.Reset();
            _events.ScheduleEvent(EVENT_CAST_SPELL, 4s);
            _events.ScheduleEvent(EVENT_CONTROL_CHECK, 1s);
        }

        void SpellHit(WorldObject* caster, SpellInfo const* spell) override
        {
            if (spell->Id == SPELL_TRIGGER_ID)
            {
                if (Player* player = caster->ToPlayer())
                {
                    if (player->GetQuestStatus(QUEST_ID) == QUEST_STATUS_INCOMPLETE)
                    {
                        _controller = player;
                        Talk(0);
                        me->UpdateEntry(NPC_ENTRY_CONTROLLED);
                        me->SetHealth(me->GetMaxHealth());
                        _controlled = true;
                    }
                }
            }
        }

        void UpdateAI(uint32 diff) override
        {
            _events.Update(diff);

            while (uint32 eventId = _events.ExecuteEvent())
            {
                switch (eventId)
                {
                case EVENT_CAST_SPELL:
                    if (!_controlled)
                    {
                        if (Unit* target = me->GetVictim())
                            me->CastSpell(target, SPELL_CAST, true);
                    }
                    _events.ScheduleEvent(EVENT_CAST_SPELL, 12s, 14s);
                    break;

                case EVENT_CONTROL_CHECK:
                    if (_controlled)
                    {
                        if (me->GetCharmerOrOwner() != _controller)
                        {
                            if (_controller && _controller->GetQuestStatus(QUEST_ID) == QUEST_STATUS_INCOMPLETE)
                                me->DespawnOrUnsummon(1s, 3s);

                            _controlled = false;
                            _controller = nullptr;
                            _events.Reset();
                            return;
                        }
                    }
                    _events.ScheduleEvent(EVENT_CONTROL_CHECK, 1s);
                    break;
                }
            }

            if (!_controlled)
                ScriptedAI::UpdateAI(diff);
        }

    private:
        EventMap _events;
        Player* _controller;
        bool _controlled;
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_servant_of_drakuruAI(creature);
    }
};

void AddSC_npc_servant_of_drakuru()
{
    new npc_servant_of_drakuru();
}

Code optimization and I hope it's ok this time ^^
@CraftedRO
Copy link
Contributor

you may want to put sql code in a file inside 3.3.5 update path too.

@Thor1423
Copy link
Author

Thor1423 commented Aug 6, 2025

you may want to put sql code in a file inside 3.3.5 update path too.

Do I have to send it separately as a PR?

@CraftedRO
Copy link
Contributor

separate file, in same PR, you can temporary name it 2025_99_99_99_world.sql

@Thor1423
Copy link
Author

Thor1423 commented Aug 6, 2025

separate file, in same PR, you can temporary name it 2025_99_99_99_world.sql

Okay, I'll give it a try ^^
Have you tested the script?

SQL for the PR Quest Zero Tolerance: TrinityCore#31196
@Thor1423
Copy link
Author

Thor1423 commented Aug 6, 2025

ok i have added the SQL File

Missing Healing Effect after grabbed the Storm Cloud with this fix  now works
Fix Spellscript spell_zuldrak_gymers_grab
@Thor1423 Thor1423 changed the title c++ SCript for the Quest Zero Tolerance c++ SCript for the Quest Zero Tolerance and spellfix spell_zuldrak_gymers_grab Aug 6, 2025
Comment on lines +1167 to +1168
if (GetHitCreature()->GetEntry() == NPC_STORM_CLOUD) // Storm Cloud
GetHitCreature()->CastSpell(GetCaster(), SPELL_HEALING_WINDS, true); // Healing Winds
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (GetHitCreature()->GetEntry() == NPC_STORM_CLOUD) // Storm Cloud
GetHitCreature()->CastSpell(GetCaster(), SPELL_HEALING_WINDS, true); // Healing Winds
if (GetHitCreature()->GetEntry() == NPC_STORM_CLOUD) // Storm Cloud
GetHitCreature()->CastSpell(GetCaster(), SPELL_HEALING_WINDS, true); // Healing Winds

Isnt here missing space ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah it is

@Thor1423
Copy link
Author

Thor1423 commented Aug 7, 2025

Apparently, reading the comments here, they don't want my fixes.

It was worth a try.

@Naddley
Copy link
Member

Naddley commented Aug 9, 2025

Apparently, reading the comments here, they don't want my fixes.

It was worth a try.

you get feedback for your code and now its "We dont want your code"?
Well...

@Nyr97
Copy link
Contributor

Nyr97 commented Aug 10, 2025

Yeah, I don't understand that, I gave a few good changes to apply 😢

@Thor1423
Copy link
Author

you get feedback for your code and now its "We dont want your code"?
Well...

I've made the improvement but it still doesn't seem to be good enough, so I assume there's no interest.

Yeah, I don't understand that, I gave a few good changes to apply 😢

I implemented this with the last update.

@Nyr97
Copy link
Contributor

Nyr97 commented Aug 10, 2025

Applying feedback that has been offered doesn't mean that it'll get reviewed the moment it happens. Merging takes time.

@offl
Copy link
Contributor

offl commented Aug 10, 2025

I've made the improvement but it still doesn't seem to be good enough, so I assume there's no interest.

The fact you're having reviews doesn't mean someone don't want your PR to be merged, it means there are improvements to be made so it can be merged (that's the whole purpose of making PRs)

-- Servant of Drakuru Text
DELETE FROM `creature_text` WHERE `CreatureID`=@ENTRY;
INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
(@ENTRY, 0, 0, "Darmuk must die, mon!", 12, 0, 100, 0, 0, 0, 28884, 0, 'Servant of Drakuru - Say on Charm');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing newline


enum
{
SPELL_TRIGGER_ID = 52390,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spells should have original name defined, in this case SPELL_CHARM_DRAKURU_SERVANT

{
SPELL_TRIGGER_ID = 52390,
NPC_ENTRY_CONTROLLED = 28805,
NPC_ENTRY_NORMAL = 28802,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with NPC entries, original names

SPELL_TRIGGER_ID = 52390,
NPC_ENTRY_CONTROLLED = 28805,
NPC_ENTRY_NORMAL = 28802,
QUEST_ID = 12686,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with quests, original names

QUEST_ID = 12686,
SPELL_CAST = 50361,

EVENT_CAST_SPELL = 1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Events should be named after spells used in events

if (player->GetQuestStatus(QUEST_ID) == QUEST_STATUS_INCOMPLETE)
{
_controller = player;
Talk(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic number, it should be defined in enum

if (!_controlled)
{
if (Unit* target = me->GetVictim())
me->CastSpell(target, SPELL_CAST, true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casting combat spells with triggered cast flag (true means triggered full mask) is a big no without a real reason. In this case creature will cast that spell even if polymorphed

if (Unit* target = me->GetVictim())
me->CastSpell(target, SPELL_CAST, true);
}
_events.ScheduleEvent(EVENT_CAST_SPELL, 12s, 14s);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use _events.Repeat(12s, 14s);

if (me->GetCharmerOrOwner() != _controller)
{
if (_controller && _controller->GetQuestStatus(QUEST_ID) == QUEST_STATUS_INCOMPLETE)
me->DespawnOrUnsummon(1s, 3s);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're using forceRespawnTimer but creature can't respawn (and shouldn't)
It should be just

me->DespawnOrUnsummon(1s);

return;
}
}
_events.ScheduleEvent(EVENT_CONTROL_CHECK, 1s);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_events.Repeat(1s);

@@ -1224,4 +1328,5 @@ void AddSC_zuldrak()
RegisterSpellScript(spell_zuldrak_zuldrak_rat);
RegisterSpellScript(spell_zuldrak_gymers_grab);
RegisterSpellScript(spell_zuldrak_gymers_throw);
new npc_servant_of_drakuru();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use new register model with RegisterCreatureAI, you can find examples in this file

@@ -0,0 +1,10 @@
UPDATE `creature_template` SET `AIName` = '', `ScriptName` = 'npc_servant_of_drakuru' WHERE `entry` = 28802;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SQL files should start with

--

@@ -1196,6 +1201,105 @@ class spell_zuldrak_gymers_throw : public SpellScript
}
};

//Quest 12686 Zero Tolerance

enum
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum ZeroTolerance

@@ -1196,6 +1201,105 @@ class spell_zuldrak_gymers_throw : public SpellScript
}
};

//Quest 12686 Zero Tolerance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/*######
## Quest 12686: Zero Tolerance
######*/

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one is not a standard and can be ignored most likely.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I use it every time
Now majority of new or updated zone scripts has it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants