Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 52 additions & 34 deletions src/mod-ollama-chat_events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "Guild.h"
#include "PlayerbotAI.h"
#include "PlayerbotMgr.h"
#include "RandomPlayerbotMgr.h"
#include "ChannelMgr.h"
#include "Channel.h"
#include "AiFactory.h"
Expand All @@ -26,22 +27,25 @@
static OllamaBotEventChatter eventChatter;
static std::unordered_map<Player*, std::chrono::steady_clock::time_point> botEventCooldowns;

void OllamaBotEventChatter::DispatchGameEvent(Player* source, std::string type, std::string detail)
void OllamaBotEventChatter::DispatchGameEvent(Player* source, std::string type, std::string detail, Guild* guildOverride)
{
if (!g_Enable || !g_EnableEventChatter)
return;

if (!source)
{
return;
}

// Guild hooks pass the guild explicitly; everyone else derives it from the source.
Guild* eventGuild = guildOverride ? guildOverride : source->GetGuild();

bool isSourceBot = PlayerbotsMgr::instance().GetPlayerbotAI(source) != nullptr;
bool hasNearbyRealPlayer = false;
bool isGuildEvent = false;

// Only set isGuildEvent for specific event types
if (source->GetGuild() && g_EnableGuildEventChatter) {
if (eventGuild && g_EnableGuildEventChatter) {
// List of event types that are considered guild events
if (
type == g_GuildEventTypeGuildJoin ||
Expand All @@ -56,7 +60,7 @@ void OllamaBotEventChatter::DispatchGameEvent(Player* source, std::string type,
type == g_GuildEventTypeGuildLogin
) {
// Check if there are real players in the guild
Guild* guild = source->GetGuild();
Guild* guild = eventGuild;
for (auto const& pair : ObjectAccessor::GetPlayers()) {
Player* player = pair.second;
if (!player || !player->IsInWorld())
Expand Down Expand Up @@ -105,7 +109,7 @@ void OllamaBotEventChatter::DispatchGameEvent(Player* source, std::string type,
if (isGuildEvent)
{
// For guild events, get guild bots
Guild* guild = source->GetGuild();
Guild* guild = eventGuild;
for (auto const& pair : ObjectAccessor::GetPlayers())
{
Player* player = pair.second;
Expand Down Expand Up @@ -663,53 +667,67 @@ void ChatOnGameObjectUse::OnGameObjectUse(Player* player, GameObject* go)
eventChatter.DispatchGameEvent(player, g_EventTypeUsedObject, go->GetGOInfo()->name);
}

ChatOnGuildMemberChange::ChatOnGuildMemberChange() : PlayerScript("ChatOnGuildMemberChange") {}
ChatOnGuildEvent::ChatOnGuildEvent() : GuildScript("ChatOnGuildEvent") {}

void ChatOnGuildMemberChange::OnGuildMemberJoin(Player* player, Guild* /*guild*/)
void ChatOnGuildEvent::OnAddMember(Guild* guild, Player* player, uint8& /*plRank*/)
{
if (!player || !player->GetGuild() || !g_EnableGuildEventChatter)
if (!player || !guild || !g_EnableGuildEventChatter)
return;

if (!g_GuildEventTypeGuildJoin.empty())
eventChatter.DispatchGameEvent(player, g_GuildEventTypeGuildJoin, player->GetGuild()->GetName());
eventChatter.DispatchGameEvent(player, g_GuildEventTypeGuildJoin, guild->GetName(), guild);
}

void ChatOnGuildMemberChange::OnGuildMemberLeave(Player* player, Guild* guild)
void ChatOnGuildEvent::OnRemoveMember(Guild* guild, Player* player, bool /*isDisbanding*/, bool /*isKicked*/)
{
if (!player || !guild || !g_EnableGuildEventChatter)
return;


// player->GetGuild() may already be cleared at this point, so pass the guild explicitly.
if (!g_GuildEventTypeGuildLeave.empty())
eventChatter.DispatchGameEvent(player, g_GuildEventTypeGuildLeave, guild->GetName());
eventChatter.DispatchGameEvent(player, g_GuildEventTypeGuildLeave, guild->GetName(), guild);
}

void ChatOnGuildMemberChange::OnGuildMemberRankChange(Player* player, Guild* /*guild*/, uint8 /*oldRank*/, uint8 newRank)
void ChatOnGuildEvent::OnEvent(Guild* guild, uint8 eventType, ObjectGuid::LowType /*playerGuid1*/,
ObjectGuid::LowType playerGuid2, uint8 /*newRank*/)
{
if (!player || !player->GetGuild() || !g_EnableGuildEventChatter)
if (!guild || !g_EnableGuildEventChatter)
return;

if (newRank < player->GetGuild()->GetMember(player->GetGUID())->GetRankId())
{
// Promotion
if (!g_GuildEventTypeGuildPromotion.empty())
eventChatter.DispatchGameEvent(player, g_GuildEventTypeGuildPromotion, std::to_string(newRank));
}
else
{
// Demotion
if (!g_GuildEventTypeGuildDemotion.empty())
eventChatter.DispatchGameEvent(player, g_GuildEventTypeGuildDemotion, std::to_string(newRank));
}

// Only promote/demote are handled here; join/leave come from OnAddMember/OnRemoveMember.
bool isPromotion = (eventType == GUILD_EVENT_LOG_PROMOTE_PLAYER);
bool isDemotion = (eventType == GUILD_EVENT_LOG_DEMOTE_PLAYER);
if (!isPromotion && !isDemotion)
return;

// For promote/demote, playerGuid2 is the affected member.
Player* target = ObjectAccessor::FindPlayer(ObjectGuid::Create<HighGuid::Player>(playerGuid2));
if (!target)
return;

std::string const& eventName = isPromotion ? g_GuildEventTypeGuildPromotion : g_GuildEventTypeGuildDemotion;
if (eventName.empty())
return;

eventChatter.DispatchGameEvent(target, eventName, guild->GetName(), guild);
}

// Add new event for non-bot guild member login
void ChatOnGuildMemberChange::OnGuildMemberLogin(Player* player, Guild* guild)
ChatOnLogin::ChatOnLogin() : PlayerScript("ChatOnLogin") {}

void ChatOnLogin::OnPlayerLogin(Player* player)
{
if (!player || !guild || !g_EnableGuildEventChatter)
if (!player || !g_EnableGuildEventChatter)
return;
// Only real players trigger guild-login chatter. GetPlayerbotAI() is unreliable here because
// mod-playerbots attaches the bot AI in its own OnPlayerLogin, which can run after this hook;
// sRandomPlayerbotMgr.IsRandomBot() reads a registry populated before any bot logs in, so it
// catches the mass bot login at server startup that would otherwise flood the event queue.
if (PlayerbotsMgr::instance().GetPlayerbotAI(player) || sRandomPlayerbotMgr.IsRandomBot(player))
return;
Guild* guild = player->GetGuild();
if (!guild)
return;
if (PlayerbotsMgr::instance().GetPlayerbotAI(player))
return; // Only real players
if (!g_GuildEventTypeGuildLogin.empty())
eventChatter.DispatchGameEvent(player, g_GuildEventTypeGuildLogin, guild->GetName());
eventChatter.DispatchGameEvent(player, g_GuildEventTypeGuildLogin, guild->GetName(), guild);
}

27 changes: 20 additions & 7 deletions src/mod-ollama-chat_events.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
class OllamaBotEventChatter
{
public:
void DispatchGameEvent(Player* source, std::string type, std::string detail);
// guildOverride lets guild hooks supply the guild explicitly, so dispatch does not depend on
// source->GetGuild() (which is unreliable while a member is being added/removed).
void DispatchGameEvent(Player* source, std::string type, std::string detail, Guild* guildOverride = nullptr);
void QueueEvent(Player* bot, std::string type, std::string detail, std::string actorName, bool isGuildEvent = false);
std::string BuildPrompt(Player* bot, std::string promptTemplate, std::string eventType, std::string eventDetail, std::string actorName);
};
Expand Down Expand Up @@ -81,14 +83,25 @@ class ChatOnGameObjectUse : public PlayerScript
void OnGameObjectUse(Player* player, GameObject* go);
};

class ChatOnGuildMemberChange : public PlayerScript
// Guild membership events. AzerothCore exposes these on GuildScript (NOT PlayerScript):
// OnAddMember / OnRemoveMember cover join/leave (and hand us the Guild* + Player* directly),
// while OnEvent carries promote/demote via the guild event-log type.
class ChatOnGuildEvent : public GuildScript
{
public:
ChatOnGuildMemberChange();
void OnGuildMemberJoin(Player* player, Guild* guild);
void OnGuildMemberLeave(Player* player, Guild* guild);
void OnGuildMemberRankChange(Player* player, Guild* guild, uint8 oldRank, uint8 newRank);
void OnGuildMemberLogin(Player* player, Guild* guild);
ChatOnGuildEvent();
void OnAddMember(Guild* guild, Player* player, uint8& plRank) override;
void OnRemoveMember(Guild* guild, Player* player, bool isDisbanding, bool isKicked) override;
void OnEvent(Guild* guild, uint8 eventType, ObjectGuid::LowType playerGuid1, ObjectGuid::LowType playerGuid2,
uint8 newRank) override;
};

// There is no guild-login hook; catch logins on PlayerScript and filter to guilded real players.
class ChatOnLogin : public PlayerScript
{
public:
ChatOnLogin();
void OnPlayerLogin(Player* player) override;
};


Expand Down
2 changes: 2 additions & 0 deletions src/mod-ollama-chat_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@ void Addmod_ollama_chatScripts()
new ChatOnLevelUp();
new ChatOnAchievement();
new ChatOnGameObjectUse();
new ChatOnGuildEvent();
new ChatOnLogin();
new OllamaChatConfigCommand();
}