From 6823830ed7a6e28953336a50169a691b4896fc5f Mon Sep 17 00:00:00 2001 From: James Huston Date: Sun, 7 Dec 2025 22:32:37 -0500 Subject: [PATCH] feat(dragonriding): Add .flight toggle command and MCP gm_command extensions - Add .flight toggle/sky/steady commands for players to switch flight styles - Default to Steady Flight on login (TC behavior), opt-in to Skyriding - Grant dragonriding spells (376777, 372608, 377920) when enabling Skyriding - Prevent both flight style auras from being saved (managed by command) - Add FlightCapability.SpellID application in SetFlightCapabilityID - Extend MCP gm_command with aura/unaura/learn/unlearn support - Add .gm advfly command for advanced flying toggle Note: Vigor UI still requires UI Widget packet implementation (not in TC) --- src/araxiaonline/mcp/ServerTools.cpp | 147 +++++++++++++++++- src/server/game/Entities/Player/Player.cpp | 1 + src/server/game/Entities/Unit/Unit.cpp | 14 ++ src/server/game/Spells/SpellMgr.cpp | 3 +- src/server/scripts/Commands/cs_flight.cpp | 117 ++++++++++++++ src/server/scripts/Commands/cs_gm.cpp | 30 ++++ .../scripts/Commands/cs_script_loader.cpp | 2 + 7 files changed, 312 insertions(+), 2 deletions(-) create mode 100644 src/server/scripts/Commands/cs_flight.cpp diff --git a/src/araxiaonline/mcp/ServerTools.cpp b/src/araxiaonline/mcp/ServerTools.cpp index d9ba582ec4..1c3fa106ce 100644 --- a/src/araxiaonline/mcp/ServerTools.cpp +++ b/src/araxiaonline/mcp/ServerTools.cpp @@ -14,6 +14,13 @@ #include "GitRevision.h" #include "GameTime.h" #include "LuaEngine/ElunaSharedData.h" +#include "Cell.h" +#include "CellImpl.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "Creature.h" +#include "SpellMgr.h" +#include "SpellInfo.h" #include namespace Araxia @@ -301,13 +308,151 @@ void RegisterServerTools() {"player", player->GetName()} }; } + // Handle: aura - Add aura to player + else if (cmd == "aura") + { + uint32 spellId; + if (!(iss >> spellId)) + return {{"success", false}, {"error", "Usage: aura "}}; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE); + if (!spellInfo) + return {{"success", false}, {"error", "Invalid spell ID"}, {"spellId", spellId}}; + + player->AddAura(spellId, player); + + return { + {"success", true}, + {"command", "aura"}, + {"player", player->GetName()}, + {"spellId", spellId}, + {"spellName", spellInfo->SpellName->Str[LOCALE_enUS]} + }; + } + // Handle: unaura - Remove aura from player + else if (cmd == "unaura") + { + uint32 spellId; + if (!(iss >> spellId)) + return {{"success", false}, {"error", "Usage: unaura "}}; + + if (!player->HasAura(spellId)) + return {{"success", false}, {"error", "Player does not have this aura"}, {"spellId", spellId}}; + + player->RemoveAurasDueToSpell(spellId); + + return { + {"success", true}, + {"command", "unaura"}, + {"player", player->GetName()}, + {"spellId", spellId} + }; + } + // Handle: learn - Teach spell to player + else if (cmd == "learn") + { + uint32 spellId; + if (!(iss >> spellId)) + return {{"success", false}, {"error", "Usage: learn "}}; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, DIFFICULTY_NONE); + if (!spellInfo) + return {{"success", false}, {"error", "Invalid spell ID"}, {"spellId", spellId}}; + + player->LearnSpell(spellId, false); + + return { + {"success", true}, + {"command", "learn"}, + {"player", player->GetName()}, + {"spellId", spellId}, + {"spellName", spellInfo->SpellName->Str[LOCALE_enUS]} + }; + } + // Handle: unlearn - Remove spell from player + else if (cmd == "unlearn") + { + uint32 spellId; + if (!(iss >> spellId)) + return {{"success", false}, {"error", "Usage: unlearn "}}; + + if (!player->HasSpell(spellId)) + return {{"success", false}, {"error", "Player does not have this spell"}, {"spellId", spellId}}; + + player->RemoveSpell(spellId, false, false); + + return { + {"success", true}, + {"command", "unlearn"}, + {"player", player->GetName()}, + {"spellId", spellId} + }; + } + // Handle: respawn - Respawn creatures around player or targeted creature + else if (cmd == "respawn") + { + Map* map = player->GetMap(); + if (!map) + return {{"success", false}, {"error", "Player not on valid map"}}; + + // Check if player has a creature selected + Unit* target = player->GetSelectedUnit(); + Creature* targetCreature = target ? target->ToCreature() : nullptr; + + if (targetCreature) + { + // Respawn the specific targeted creature + if (targetCreature->isDead()) + { + targetCreature->Respawn(); + return { + {"success", true}, + {"command", "respawn"}, + {"mode", "targeted"}, + {"creature", targetCreature->GetName()}, + {"entry", targetCreature->GetEntry()} + }; + } + else + { + // Force despawn and respawn for living creature + targetCreature->DespawnOrUnsummon(0ms, 1s); + return { + {"success", true}, + {"command", "respawn"}, + {"mode", "force_respawn"}, + {"creature", targetCreature->GetName()}, + {"entry", targetCreature->GetEntry()}, + {"message", "Creature will despawn and respawn in 1 second"} + }; + } + } + else + { + // No target - respawn all creatures in area (like .respawn command) + CellCoord p(Trinity::ComputeCellCoord(player->GetPositionX(), player->GetPositionY())); + Cell cell(p); + cell.SetNoCreate(); + + Trinity::RespawnDo u; + Trinity::WorldObjectWorker worker(player, u); + Cell::VisitGridObjects(player, worker, player->GetGridActivationRange()); + + return { + {"success", true}, + {"command", "respawn"}, + {"mode", "area"}, + {"message", "Respawned all creatures in area"} + }; + } + } // Unknown command return { {"success", false}, {"error", "Unknown or unimplemented command"}, {"command", cmd}, - {"supported", {"go xyz", "tele", "gps", "additem", "die", "revive"}} + {"supported", {"go xyz", "tele", "gps", "additem", "die", "revive", "aura", "unaura", "learn", "unlearn", "respawn"}} }; } ); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 3014b1b19f..d115dcdb7c 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -18825,6 +18825,7 @@ void Player::_LoadAuras(PreparedQueryResult auraResult, PreparedQueryResult effe } // TODO: finish dragonriding - this forces old flight mode + // Araxia: Players can use .flight toggle to enable Skyriding AddAura(404468, this); } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 153d1136dd..885705db72 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -8864,6 +8864,20 @@ void Unit::SetFlightCapabilityID(int32 flightCapabilityId, bool clientUpdate) SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::FlightCapabilityID), flightCapabilityId); + // Araxia: Apply the FlightCapability's associated spell (may enable Vigor UI) + if (flightCapabilityId > 0) + { + if (FlightCapabilityEntry const* flightCapability = sFlightCapabilityStore.LookupEntry(flightCapabilityId)) + { + TC_LOG_INFO("entities.unit", "SetFlightCapabilityID: FlightCapability %d has SpellID %d", + flightCapabilityId, flightCapability->SpellID); + if (flightCapability->SpellID > 0) + { + CastSpell(this, flightCapability->SpellID, true); + } + } + } + UpdateAdvFlyingSpeed(ADV_FLYING_AIR_FRICTION, clientUpdate); UpdateAdvFlyingSpeed(ADV_FLYING_MAX_VEL, clientUpdate); UpdateAdvFlyingSpeed(ADV_FLYING_LIFT_COEFFICIENT, clientUpdate); diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 0dc27c8092..659f8f76fc 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -5163,7 +5163,8 @@ void SpellMgr::LoadSpellInfoCorrections() }); // TODO: temporary, remove with dragonriding - ApplySpellFix({ 404468 }, [](SpellInfo* spellInfo) + // Araxia: Also prevent Skyriding aura from saving (applied via .flight command) + ApplySpellFix({ 404464, 404468 }, [](SpellInfo* spellInfo) { spellInfo->AttributesCu |= SPELL_ATTR0_CU_AURA_CANNOT_BE_SAVED; }); diff --git a/src/server/scripts/Commands/cs_flight.cpp b/src/server/scripts/Commands/cs_flight.cpp new file mode 100644 index 0000000000..c2f462ee91 --- /dev/null +++ b/src/server/scripts/Commands/cs_flight.cpp @@ -0,0 +1,117 @@ +/* + * Araxia Online - Flight Style Toggle Command + * + * Allows players to toggle between Skyriding and Steady flight modes. + */ + +#include "ScriptMgr.h" +#include "Chat.h" +#include "ChatCommand.h" +#include "Player.h" +#include "SpellAuras.h" +#include "WorldSession.h" + +using namespace Trinity::ChatCommands; + +// Flight style spell IDs +constexpr uint32 SPELL_FLIGHT_STYLE_SKYRIDING = 404464; +constexpr uint32 SPELL_FLIGHT_STYLE_STEADY = 404468; + +// Dragonriding spells +constexpr uint32 SPELL_SKYRIDING_BASICS = 376777; +constexpr uint32 SPELL_SURGE_FORWARD = 372608; +constexpr uint32 SPELL_TAKE_TO_THE_SKIES = 377920; + +// Helper function to grant dragonriding spells +static void GrantDragonridingSpells(Player* player) +{ + if (!player->HasSpell(SPELL_SKYRIDING_BASICS)) + player->LearnSpell(SPELL_SKYRIDING_BASICS, false); + if (!player->HasSpell(SPELL_SURGE_FORWARD)) + player->LearnSpell(SPELL_SURGE_FORWARD, false); + if (!player->HasSpell(SPELL_TAKE_TO_THE_SKIES)) + player->LearnSpell(SPELL_TAKE_TO_THE_SKIES, false); +} + +class flight_commandscript : public CommandScript +{ +public: + flight_commandscript() : CommandScript("flight_commandscript") { } + + ChatCommandTable GetCommands() const override + { + static ChatCommandTable flightCommandTable = + { + { "toggle", HandleFlightToggleCommand, rbac::RBAC_PERM_COMMAND_HELP, Console::No }, + { "sky", HandleFlightSkyCommand, rbac::RBAC_PERM_COMMAND_HELP, Console::No }, + { "steady", HandleFlightSteadyCommand, rbac::RBAC_PERM_COMMAND_HELP, Console::No }, + }; + + static ChatCommandTable commandTable = + { + { "flight", flightCommandTable }, + }; + + return commandTable; + } + + // Toggle between Skyriding and Steady flight + static bool HandleFlightToggleCommand(ChatHandler* handler) + { + Player* player = handler->GetSession()->GetPlayer(); + if (!player) + return false; + + if (player->HasAura(SPELL_FLIGHT_STYLE_SKYRIDING)) + { + // Switch to Steady + player->RemoveAura(SPELL_FLIGHT_STYLE_SKYRIDING); + player->AddAura(SPELL_FLIGHT_STYLE_STEADY, player); + handler->SendSysMessage("Flight style changed to |cff00ff00Steady Flight|r."); + } + else + { + // Switch to Skyriding - grant dragonriding spells + player->RemoveAura(SPELL_FLIGHT_STYLE_STEADY); + player->AddAura(SPELL_FLIGHT_STYLE_SKYRIDING, player); + GrantDragonridingSpells(player); + handler->SendSysMessage("Flight style changed to |cff00ffffSkyriding|r (Dragonriding). Dragonriding spells learned!"); + } + + return true; + } + + // Force Skyriding mode + static bool HandleFlightSkyCommand(ChatHandler* handler) + { + Player* player = handler->GetSession()->GetPlayer(); + if (!player) + return false; + + player->RemoveAura(SPELL_FLIGHT_STYLE_STEADY); + player->AddAura(SPELL_FLIGHT_STYLE_SKYRIDING, player); + GrantDragonridingSpells(player); + handler->SendSysMessage("Flight style set to |cff00ffffSkyriding|r (Dragonriding). Dragonriding spells learned!"); + + return true; + } + + // Force Steady flight mode + static bool HandleFlightSteadyCommand(ChatHandler* handler) + { + Player* player = handler->GetSession()->GetPlayer(); + if (!player) + return false; + + player->RemoveAura(SPELL_FLIGHT_STYLE_SKYRIDING); + player->AddAura(SPELL_FLIGHT_STYLE_STEADY, player); + handler->SendSysMessage("Flight style set to |cff00ff00Steady Flight|r."); + + return true; + } +}; + +void AddSC_flight_commandscript() +{ + new flight_commandscript(); +} diff --git a/src/server/scripts/Commands/cs_gm.cpp b/src/server/scripts/Commands/cs_gm.cpp index d4302e4bb0..09fc70a182 100644 --- a/src/server/scripts/Commands/cs_gm.cpp +++ b/src/server/scripts/Commands/cs_gm.cpp @@ -47,6 +47,7 @@ public: { { "chat", HandleGMChatCommand, rbac::RBAC_PERM_COMMAND_GM_CHAT, Console::No }, { "fly", HandleGMFlyCommand, rbac::RBAC_PERM_COMMAND_GM_FLY, Console::No }, + { "advfly", HandleGMAdvFlyCommand, rbac::RBAC_PERM_COMMAND_GM_FLY, Console::No }, { "ingame", HandleGMListIngameCommand, rbac::RBAC_PERM_COMMAND_GM_INGAME, Console::Yes }, { "list", HandleGMListFullCommand, rbac::RBAC_PERM_COMMAND_GM_LIST, Console::Yes }, { "visible", HandleGMVisibleCommand, rbac::RBAC_PERM_COMMAND_GM_VISIBLE, Console::No }, @@ -114,6 +115,35 @@ public: return true; } + // Araxia: Enable dragonriding/skyriding (advanced flight) + static bool HandleGMAdvFlyCommand(ChatHandler* handler, bool enable) + { + Player* target = handler->getSelectedPlayer(); + if (!target) + target = handler->GetSession()->GetPlayer(); + + if (enable) + { + target->SetCanFly(true); + target->SetCanAdvFly(true); + target->SetFlightCapabilityID(1, true); // ID 1 = default FlightCapability + target->SetCanTransitionBetweenSwimAndFly(true); + handler->PSendSysMessage("Advanced flight (dragonriding) enabled for %s. FlightCapabilityID=1", + handler->GetNameLink(target).c_str()); + } + else + { + target->SetCanAdvFly(false); + target->SetFlightCapabilityID(0, true); + target->SetCanFly(false); + target->SetCanTransitionBetweenSwimAndFly(false); + handler->PSendSysMessage("Advanced flight (dragonriding) disabled for %s", + handler->GetNameLink(target).c_str()); + } + + return true; + } + static bool HandleGMListIngameCommand(ChatHandler* handler) { bool first = true; diff --git a/src/server/scripts/Commands/cs_script_loader.cpp b/src/server/scripts/Commands/cs_script_loader.cpp index 9a50f93335..677e51cf1b 100644 --- a/src/server/scripts/Commands/cs_script_loader.cpp +++ b/src/server/scripts/Commands/cs_script_loader.cpp @@ -30,6 +30,7 @@ void AddSC_debug_commandscript(); void AddSC_deserter_commandscript(); void AddSC_disable_commandscript(); void AddSC_event_commandscript(); +void AddSC_flight_commandscript(); void AddSC_gm_commandscript(); void AddSC_go_commandscript(); void AddSC_gobject_commandscript(); @@ -78,6 +79,7 @@ void AddCommandsScripts() AddSC_deserter_commandscript(); AddSC_disable_commandscript(); AddSC_event_commandscript(); + AddSC_flight_commandscript(); AddSC_gm_commandscript(); AddSC_go_commandscript(); AddSC_gobject_commandscript();