mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
The world side of guilds (docs/Guilds.md): - TMP_GUILD_CREATE, which the client's guild create box sends to its world, goes to the chat server as GUILD_CREATE. - The character component reads the character's guild from the database when it loads (instead of the old unused "gn" and "gid" charxml attributes) and takes GUILD_GET_STATUS from the chat server; a change is serialized once (the client redraws the name billboard every time it reads a guild name). A name waiting for moderation isn't shown. - Slash commands (the client has none for guilds): /g and /guild (guild chat; the client's guild tab sends /g, which goes through the chat filter and mute like zone chat, then to the chat server as channel 10), /guildcreate (opens the create box with DisplayGuildCreateBox), /gkick, /grank <name> <officer|veteran|recruit>, /gleader and /gdisband confirm. - The Guild Master script (LOT 3001, L_GUILD_CREATE.lua: using it opens the create box). Live never placed it; GMs can spawn it. Check in game (two accounts, a client with a FeatureGating row "guilds", 1, 0, 0 in its cdclient.fdb): /guildcreate, make a guild (the name shows under yours, the guild button appears on the status bar); invite the second character from the guild window, accept, both lists show both; guild chat tab; /grank, /gkick, /gleader; leave from the window; log the second character out and in (guildmate logged off/in lines). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
121 lines
4.0 KiB
C++
121 lines
4.0 KiB
C++
#include "GuildCommands.h"
|
|
|
|
#include <cctype>
|
|
|
|
#include "Character.h"
|
|
#include "ChatPackets.h"
|
|
#include "ChatServerLink.h"
|
|
#include "dChatFilter.h"
|
|
#include "Entity.h"
|
|
#include "eChatChannel.h"
|
|
#include "eGameMasterLevel.h"
|
|
#include "eGuildRank.h"
|
|
#include "Game.h"
|
|
#include "GeneralUtils.h"
|
|
#include "PlayerMessages.h"
|
|
#include "User.h"
|
|
|
|
namespace {
|
|
std::string Lower(std::string text) {
|
|
for (auto& c : text) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
|
return text;
|
|
}
|
|
|
|
std::string Trimmed(const std::string& text) {
|
|
const auto start = text.find_first_not_of(' ');
|
|
if (start == std::string::npos) return "";
|
|
return text.substr(start, text.find_last_not_of(' ') - start + 1);
|
|
}
|
|
|
|
LUWString PlayerName(const std::string& name) {
|
|
return LUWString(GeneralUtils::UTF8ToUTF16(name), 33);
|
|
}
|
|
|
|
void SendRank(Entity* entity, const std::string& name, const eGuildRank rank) {
|
|
ChatPackets::GuildSetRank request;
|
|
request.playerID = entity->GetObjectID();
|
|
request.targetPlayer = PlayerName(name);
|
|
request.rank = static_cast<uint8_t>(rank);
|
|
ChatServerLink::Send(request);
|
|
}
|
|
}
|
|
|
|
void GuildCommands::Chat(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
|
const auto message = Trimmed(args);
|
|
if (message.empty()) return;
|
|
auto* character = entity->GetCharacter();
|
|
if (!character) return;
|
|
auto* user = character->GetParentUser();
|
|
if (user && user->GetIsMuted()) {
|
|
character->SendMuteNotice();
|
|
return;
|
|
}
|
|
// The same filter as the zone's chat
|
|
if (!Game::chatFilter->IsSentenceOkay(message, character->GetGMLevel()).empty()) {
|
|
ChatPackets::SendSystemMessage(sysAddr, u"Your message was not sent to your guild.");
|
|
return;
|
|
}
|
|
|
|
auto text = GeneralUtils::UTF8ToUTF16(message);
|
|
ChatPackets::GeneralChatMessage chat;
|
|
chat.playerID = entity->GetObjectID();
|
|
chat.chatChannel = eChatChannel::GUILD;
|
|
chat.messageLength = static_cast<uint32_t>(text.size());
|
|
chat.senderName = PlayerName(character->GetName());
|
|
chat.senderID = entity->GetObjectID();
|
|
chat.senderGMLevel = static_cast<uint8_t>(character->GetGMLevel());
|
|
chat.message = LUWString(text, static_cast<uint32_t>(text.size()));
|
|
ChatServerLink::Send(chat);
|
|
}
|
|
|
|
void GuildCommands::OpenCreateBox(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
|
GameMessages::DisplayGuildCreateBox box;
|
|
box.target = entity->GetObjectID();
|
|
box.Send(sysAddr);
|
|
}
|
|
|
|
void GuildCommands::Kick(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
|
const auto name = Trimmed(args);
|
|
if (name.empty()) {
|
|
ChatPackets::SendSystemMessage(sysAddr, u"Usage: /gkick <name>");
|
|
return;
|
|
}
|
|
ChatPackets::GuildKick request;
|
|
request.playerID = entity->GetObjectID();
|
|
request.kickedPlayer = PlayerName(name);
|
|
ChatServerLink::Send(request);
|
|
}
|
|
|
|
void GuildCommands::Rank(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
|
const auto split = GeneralUtils::SplitString(Trimmed(args), ' ');
|
|
const std::string rankName = split.size() == 2 ? Lower(split[1]) : "";
|
|
eGuildRank rank = eGuildRank::NONE;
|
|
if (rankName == "officer") rank = eGuildRank::OFFICER;
|
|
else if (rankName == "veteran") rank = eGuildRank::VETERAN;
|
|
else if (rankName == "recruit") rank = eGuildRank::RECRUIT;
|
|
if (rank == eGuildRank::NONE) {
|
|
ChatPackets::SendSystemMessage(sysAddr, u"Usage: /grank <name> <officer|veteran|recruit>");
|
|
return;
|
|
}
|
|
SendRank(entity, split[0], rank);
|
|
}
|
|
|
|
void GuildCommands::Leader(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
|
const auto name = Trimmed(args);
|
|
if (name.empty()) {
|
|
ChatPackets::SendSystemMessage(sysAddr, u"Usage: /gleader <name>");
|
|
return;
|
|
}
|
|
SendRank(entity, name, eGuildRank::LEADER);
|
|
}
|
|
|
|
void GuildCommands::Disband(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
|
if (Trimmed(args) != "confirm") {
|
|
ChatPackets::SendSystemMessage(sysAddr, u"This removes every member and the guild itself. To do it, type /gdisband confirm");
|
|
return;
|
|
}
|
|
ChatPackets::GuildDisband request;
|
|
request.playerID = entity->GetObjectID();
|
|
ChatServerLink::Send(request);
|
|
}
|