mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
* [Saylinks] Convert all GM Command Saylinks to Silent Saylinks. - This cleans up all non-silent GM Command Saylinks that we had before due to the way they worked before. All saylinks like this should be silent now. - Add source short hand capability for say links with same link as text. * Defaults to r anyway. * Spacing.
695 lines
18 KiB
C++
695 lines
18 KiB
C++
#include "door_manipulation.h"
|
|
#include "../doors.h"
|
|
#include "../../common/misc_functions.h"
|
|
|
|
#define MAX_CLIENT_MESSAGE_LENGTH 2000
|
|
|
|
void DoorManipulation::CommandHandler(Client *c, const Seperator *sep)
|
|
{
|
|
// this should never happen
|
|
if (!c) {
|
|
return;
|
|
}
|
|
|
|
// args
|
|
std::string arg1(sep->arg[1]);
|
|
std::string arg2(sep->arg[2]);
|
|
std::string arg3(sep->arg[3]);
|
|
|
|
// table check
|
|
std::string table_name = "tool_game_objects";
|
|
std::string url = "https://raw.githubusercontent.com/EQEmu/database-tool-sqls/main/tool_game_objects.sql";
|
|
if (!database.DoesTableExist(table_name)) {
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"Table [{}] does not exist. Downloading from [{}] and installing locally",
|
|
table_name,
|
|
url
|
|
).c_str()
|
|
);
|
|
database.SourceDatabaseTableFromUrl(
|
|
table_name,
|
|
url
|
|
);
|
|
}
|
|
|
|
// edit menu
|
|
if (arg1 == "edit") {
|
|
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
|
|
if (door) {
|
|
|
|
const std::string move_x_action = "move_x";
|
|
const std::string move_y_action = "move_y";
|
|
const std::string move_z_action = "move_z";
|
|
const std::string move_h_action = "move_h";
|
|
const std::string set_size_action = "set_size";
|
|
|
|
// we're passing a move action here
|
|
if (!arg3.empty() && Strings::IsNumber(arg3)) {
|
|
float x_move = 0.0f;
|
|
float y_move = 0.0f;
|
|
float z_move = 0.0f;
|
|
float h_move = 0.0f;
|
|
float set_size = 0.0f;
|
|
|
|
if (arg2 == move_x_action) {
|
|
x_move = std::atof(arg3.c_str());
|
|
}
|
|
|
|
if (arg2 == move_y_action) {
|
|
y_move = std::atof(arg3.c_str());
|
|
}
|
|
|
|
if (arg2 == move_z_action) {
|
|
z_move = std::atof(arg3.c_str());
|
|
}
|
|
|
|
if (arg2 == move_h_action) {
|
|
h_move = std::atof(arg3.c_str());
|
|
}
|
|
|
|
if (arg2 == set_size_action) {
|
|
set_size = std::atof(arg3.c_str());
|
|
}
|
|
|
|
door->SetLocation(
|
|
door->GetX() + x_move,
|
|
door->GetY() + y_move,
|
|
door->GetZ() + z_move
|
|
);
|
|
|
|
glm::vec4 door_position = door->GetPosition();
|
|
door_position.w = door_position.w + h_move;
|
|
door->SetPosition(door_position);
|
|
door->SetSize(door->GetSize() + set_size);
|
|
|
|
// spawn and move helpers
|
|
uint16 helper_mob_x_negative = 0;
|
|
uint16 helper_mob_x_positive = 0;
|
|
uint16 helper_mob_y_positive = 0;
|
|
uint16 helper_mob_y_negative = 0;
|
|
|
|
for (auto &n: entity_list.GetNPCList()) {
|
|
NPC *npc = n.second;
|
|
std::string npc_name = npc->GetName();
|
|
if (npc_name.find("-X") != std::string::npos) {
|
|
helper_mob_x_negative = npc->GetID();
|
|
}
|
|
if (npc_name.find("-Y") != std::string::npos) {
|
|
helper_mob_y_negative = npc->GetID();
|
|
}
|
|
if (npc_name.find("+X") != std::string::npos) {
|
|
helper_mob_x_positive = npc->GetID();
|
|
}
|
|
if (npc_name.find("+Y") != std::string::npos) {
|
|
helper_mob_y_positive = npc->GetID();
|
|
}
|
|
}
|
|
|
|
// -X
|
|
door_position = door->GetPosition();
|
|
if (helper_mob_x_negative == 0) {
|
|
door_position.x = door_position.x - 15;
|
|
helper_mob_x_negative = NPC::SpawnNodeNPC("-X", "", door_position)->GetID();
|
|
}
|
|
else {
|
|
auto n = entity_list.GetNPCByID(helper_mob_x_negative);
|
|
n->GMMove(door->GetX() - 15, door->GetY(), door->GetZ(), n->GetHeading());
|
|
}
|
|
|
|
// +X
|
|
door_position = door->GetPosition();
|
|
if (helper_mob_x_positive == 0) {
|
|
door_position.x = door_position.x + 15;
|
|
helper_mob_x_positive = NPC::SpawnNodeNPC("+X", "", door_position)->GetID();
|
|
}
|
|
else {
|
|
auto n = entity_list.GetNPCByID(helper_mob_x_positive);
|
|
n->GMMove(door->GetX() + 15, door->GetY(), door->GetZ(), n->GetHeading());
|
|
}
|
|
|
|
// -Y
|
|
door_position = door->GetPosition();
|
|
if (helper_mob_y_negative == 0) {
|
|
door_position.y = door_position.y - 15;
|
|
helper_mob_y_negative = NPC::SpawnNodeNPC("-Y", "", door_position)->GetID();
|
|
}
|
|
else {
|
|
auto n = entity_list.GetNPCByID(helper_mob_y_negative);
|
|
n->GMMove(door->GetX(), door->GetY() - 15, door->GetZ(), n->GetHeading());
|
|
}
|
|
|
|
// +Y
|
|
door_position = door->GetPosition();
|
|
if (helper_mob_y_positive == 0) {
|
|
door_position.y = door_position.y + 15;
|
|
helper_mob_y_positive = NPC::SpawnNodeNPC("+Y", "", door_position)->GetID();
|
|
}
|
|
else {
|
|
auto n = entity_list.GetNPCByID(helper_mob_y_positive);
|
|
n->GMMove(door->GetX(), door->GetY() + 15, door->GetZ(), n->GetHeading());
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"Door Selected ID [{}] Name [{}] OpenType [{}] Invertstate [{} | {}/{}] ",
|
|
c->GetDoorToolEntityId(),
|
|
door->GetDoorName(),
|
|
door->GetOpenType(),
|
|
door->GetInvertState(),
|
|
Saylink::Silent("#door setinvertstate 0", "0"),
|
|
Saylink::Silent("#door setinvertstate 1", "1")
|
|
).c_str()
|
|
);
|
|
|
|
|
|
std::vector<std::string> move_options = {
|
|
move_x_action,
|
|
move_y_action,
|
|
move_z_action,
|
|
move_h_action,
|
|
set_size_action
|
|
};
|
|
std::vector<std::string> move_x_options_positive;
|
|
std::vector<std::string> move_x_options_negative;
|
|
std::vector<std::string> move_y_options_positive;
|
|
std::vector<std::string> move_y_options_negative;
|
|
std::vector<std::string> move_z_options_positive;
|
|
std::vector<std::string> move_z_options_negative;
|
|
std::vector<std::string> move_h_options_positive;
|
|
std::vector<std::string> move_h_options_negative;
|
|
std::vector<std::string> set_size_options_positive;
|
|
std::vector<std::string> set_size_options_negative;
|
|
|
|
std::vector<std::string> xyz_values = {
|
|
".1", "1", "5", "10", "25", "50", "100"
|
|
};
|
|
|
|
// build positive options x/y/z
|
|
for (const auto &v: xyz_values) {
|
|
for (const auto &o: move_options) {
|
|
if (o == move_x_action) {
|
|
move_x_options_positive.emplace_back(
|
|
Saylink::Silent(fmt::format("#door edit {} {}", o, v), v)
|
|
);
|
|
}
|
|
else if (o == move_y_action) {
|
|
move_y_options_positive.emplace_back(
|
|
Saylink::Silent(fmt::format("#door edit {} {}", o, v), v)
|
|
);
|
|
}
|
|
else if (o == move_z_action) {
|
|
move_z_options_positive.emplace_back(
|
|
Saylink::Silent(fmt::format("#door edit {} {}", o, v), v)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// loop through vector in reverse order
|
|
// build negative options x/y/z
|
|
for (auto v = xyz_values.rbegin(); v != xyz_values.rend(); ++v) {
|
|
for (const auto &o: move_options) {
|
|
if (o == move_x_action) {
|
|
move_x_options_negative.emplace_back(
|
|
Saylink::Silent(fmt::format("#door edit {} -{}", o, *v), *v)
|
|
);
|
|
}
|
|
else if (o == move_y_action) {
|
|
move_y_options_negative.emplace_back(
|
|
Saylink::Silent(fmt::format("#door edit {} -{}", o, *v), *v)
|
|
);
|
|
}
|
|
else if (o == move_z_action) {
|
|
move_z_options_negative.emplace_back(
|
|
Saylink::Silent(fmt::format("#door edit {} -{}", o, *v), *v)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> heading_values = {
|
|
"1", "5", "32.5", "63.75", "130",
|
|
};
|
|
|
|
// build positive options h
|
|
for (const auto &v: heading_values) {
|
|
move_h_options_positive.emplace_back(
|
|
Saylink::Silent(fmt::format("#door edit {} {}", move_h_action, v), v)
|
|
);
|
|
}
|
|
|
|
// loop through vector in reverse order
|
|
// build negative options h
|
|
for (auto v = heading_values.rbegin(); v != heading_values.rend(); ++v) {
|
|
move_h_options_negative.emplace_back(
|
|
Saylink::Silent(fmt::format("#door edit {} -{}", move_h_action, *v), *v)
|
|
);
|
|
}
|
|
|
|
std::vector<std::string> size_values = {
|
|
"1", "5", "10", "25", "50", "100", "1000"
|
|
};
|
|
|
|
// build positive options size
|
|
for (const auto &v: size_values) {
|
|
set_size_options_positive.emplace_back(
|
|
Saylink::Silent(fmt::format("#door edit {} {}", set_size_action, v), v)
|
|
);
|
|
}
|
|
|
|
// loop through vector in reverse order
|
|
// build negative options size
|
|
for (auto v = size_values.rbegin(); v != size_values.rend(); ++v) {
|
|
set_size_options_negative.emplace_back(
|
|
Saylink::Silent(fmt::format("#door edit {} -{}", set_size_action, *v), *v)
|
|
);
|
|
}
|
|
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"Name [{}] [{}] [{}] [{}]",
|
|
door->GetDoorName(),
|
|
Saylink::Silent("#door save", "Save"),
|
|
Saylink::Silent("#door changemodelqueue", "Change Model"),
|
|
Saylink::Silent("#door setinclineinc", "Incline")
|
|
).c_str()
|
|
);
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"[{}] - [X] + [{}]",
|
|
Strings::Implode(" | ", move_x_options_negative),
|
|
Strings::Implode(" | ", move_x_options_positive)
|
|
).c_str()
|
|
);
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"[{}] - [Y] + [{}]",
|
|
Strings::Implode(" | ", move_y_options_negative),
|
|
Strings::Implode(" | ", move_y_options_positive)
|
|
).c_str()
|
|
);
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"[{}] - [Z] + [{}]",
|
|
Strings::Implode(" | ", move_z_options_negative),
|
|
Strings::Implode(" | ", move_z_options_positive)
|
|
).c_str()
|
|
);
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"[{}] - [H] + [{}]",
|
|
Strings::Implode(" | ", move_h_options_negative),
|
|
Strings::Implode(" | ", move_h_options_positive)
|
|
).c_str()
|
|
);
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"[{}] - [Size] + [{}]",
|
|
Strings::Implode(" | ", set_size_options_negative),
|
|
Strings::Implode(" | ", set_size_options_positive)
|
|
).c_str()
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
c->Message(Chat::Red, "Door selection invalid...");
|
|
}
|
|
|
|
// create
|
|
if (arg1 == "create") {
|
|
std::string model = Strings::ToUpper(arg2);
|
|
uint16 entity_id = entity_list.CreateDoor(
|
|
model.c_str(),
|
|
c->GetPosition(),
|
|
58,
|
|
100
|
|
);
|
|
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format("Creating door entity_id [{}] with model [{}]", entity_id, model).c_str());
|
|
c->SetDoorToolEntityId(entity_id);
|
|
}
|
|
|
|
// set model
|
|
if (arg1 == "model") {
|
|
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
|
|
std::string model = Strings::ToUpper(arg2);
|
|
if (door) {
|
|
door->SetDoorName(model.c_str());
|
|
}
|
|
}
|
|
|
|
// change model queue
|
|
if (arg1 == "changemodelqueue") {
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"#door model <modelname> | Changes door model for selected door or select from [{}] or [{}]",
|
|
Saylink::Silent("#door showmodelszone", "local zone"),
|
|
Saylink::Silent("#door showmodelsglobal", "global")
|
|
).c_str()
|
|
);
|
|
}
|
|
|
|
// open type
|
|
if (arg1 == "opentype" && !arg2.empty() && Strings::IsNumber(arg2)) {
|
|
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
|
|
if (door) {
|
|
door->SetOpenType(std::atoi(arg2.c_str()));
|
|
}
|
|
}
|
|
|
|
// incline
|
|
if (arg1 == "setincline" && !arg2.empty() && Strings::IsNumber(arg2)) {
|
|
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
|
|
if (door) {
|
|
door->SetIncline(std::atoi(arg2.c_str()));
|
|
}
|
|
}
|
|
|
|
// invertstate
|
|
if (arg1 == "setinvertstate" && !arg2.empty() && Strings::IsNumber(arg2)) {
|
|
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
|
|
if (door) {
|
|
door->SetInvertState(std::atoi(arg2.c_str()));
|
|
}
|
|
}
|
|
|
|
// save
|
|
if (arg1 == "save") {
|
|
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
|
|
if (door) {
|
|
door->CreateDatabaseEntry();
|
|
c->Message(Chat::White, "Door saved");
|
|
}
|
|
}
|
|
|
|
// incline incremental
|
|
if (arg1 == "setinclineinc" && !arg2.empty() && Strings::IsNumber(arg2)) {
|
|
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
|
|
if (door) {
|
|
door->SetIncline(door->GetIncline() + std::atoi(arg2.c_str()));
|
|
}
|
|
}
|
|
|
|
if (arg1 == "setinclineinc") {
|
|
std::map<float, std::string> incline_values = {
|
|
{.01, "Upright"},
|
|
{63.75, "45 Degrees",},
|
|
{130, "90 Degrees"},
|
|
{192.5, "135 Degrees"},
|
|
{255, "180 Degrees"},
|
|
{321.25, "225 Degrees"},
|
|
{385, "270 Degrees"},
|
|
{448.75, "315 Degrees"},
|
|
{512.5, "360 Degrees"}
|
|
};
|
|
|
|
std::vector<std::string> incline_normal_options;
|
|
std::vector<std::string> incline_positive_options;
|
|
std::vector<std::string> incline_negative_options;
|
|
for (auto incline_value: incline_values) {
|
|
incline_normal_options.emplace_back(
|
|
Saylink::Silent(
|
|
fmt::format(
|
|
"#door setincline {}",
|
|
incline_value.first
|
|
),
|
|
incline_value.second
|
|
)
|
|
);
|
|
}
|
|
|
|
for (int incline_index = 0; incline_index <= 100; incline_index += 10) {
|
|
int incline_value = (incline_index == 0 ? 1 : incline_index);
|
|
incline_positive_options.emplace_back(
|
|
Saylink::Silent(
|
|
fmt::format(
|
|
"#door setinclineinc {}",
|
|
incline_value
|
|
),
|
|
itoa(std::abs(incline_value))
|
|
)
|
|
);
|
|
}
|
|
|
|
for (int incline_index = -100; incline_index <= 1; incline_index += 10) {
|
|
int incline_value = (incline_index == 0 ? -1 : incline_index);
|
|
incline_negative_options.emplace_back(
|
|
Saylink::Silent(
|
|
fmt::format(
|
|
"#door setinclineinc {}",
|
|
incline_value
|
|
),
|
|
itoa(std::abs(incline_value))
|
|
)
|
|
);
|
|
}
|
|
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"[Incline] [{}]",
|
|
Strings::Implode(" | ", incline_normal_options)
|
|
).c_str()
|
|
);
|
|
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"[Incline Increments] [{}] - | + [{}]",
|
|
Strings::Implode(" | ", incline_negative_options),
|
|
Strings::Implode(" | ", incline_positive_options)
|
|
).c_str()
|
|
);
|
|
}
|
|
|
|
// show models in zone
|
|
if (arg1 == "showmodelsglobal") {
|
|
auto game_objects = ToolGameObjectsRepository::GetWhere(
|
|
database,
|
|
"object_name LIKE '%IT%' AND zoneid = 0 AND object_name NOT LIKE '%OBJ%' GROUP by file_from"
|
|
);
|
|
|
|
if (game_objects.empty()) {
|
|
c->Message(Chat::White, "There are no models to display...");
|
|
}
|
|
|
|
c->Message(Chat::White, "------------------------------------------------");
|
|
c->Message(Chat::White, "# Models (Global)");
|
|
c->Message(Chat::White, "------------------------------------------------");
|
|
|
|
DisplayModelsFromFileResults(c, game_objects);
|
|
}
|
|
|
|
// show models in zone
|
|
if (arg1 == "showmodelszone") {
|
|
auto game_objects = ToolGameObjectsRepository::GetWhere(
|
|
database,
|
|
fmt::format("zoneid = {}", zone->GetZoneID())
|
|
);
|
|
|
|
if (game_objects.empty()) {
|
|
c->Message(Chat::White, "There are no models for this zone...");
|
|
}
|
|
|
|
c->Message(Chat::White, "------------------------------------------------");
|
|
c->Message(Chat::White, "# Models from zone");
|
|
c->Message(Chat::White, "------------------------------------------------");
|
|
|
|
DisplayObjectResultToClient(c, game_objects);
|
|
}
|
|
|
|
// show models from file name
|
|
if (arg1 == "showmodelsfromfile" && !arg2.empty()) {
|
|
const std::string &file_name = arg2;
|
|
auto game_objects = ToolGameObjectsRepository::GetWhere(
|
|
database,
|
|
fmt::format("file_from = '{}'", file_name)
|
|
);
|
|
|
|
if (game_objects.empty()) {
|
|
c->Message(Chat::White, "There are no models for this zone...");
|
|
}
|
|
|
|
c->Message(Chat::White, "------------------------------------------------");
|
|
c->Message(Chat::White, fmt::format("# Models from file name [{}]", file_name).c_str());
|
|
c->Message(Chat::White, "------------------------------------------------");
|
|
|
|
DisplayObjectResultToClient(c, game_objects);
|
|
}
|
|
|
|
// help menu
|
|
if (arg1.empty()) {
|
|
DoorManipulation::CommandHeader(c);
|
|
c->Message(
|
|
Chat::White,
|
|
"#door create <modelname> | Creates a door from a model. (Example IT78 creates a campfire)"
|
|
);
|
|
c->Message(Chat::White, "#door setinvertstate [0|1] | Sets selected door invert state");
|
|
c->Message(Chat::White, "#door setincline <incline> | Sets selected door incline");
|
|
c->Message(Chat::White, "#door opentype <opentype> | Sets selected door opentype");
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"#door model <modelname> | Changes door model for selected door or select from [{}] or [{}]",
|
|
Saylink::Silent("#door showmodelszone", "local zone"),
|
|
Saylink::Silent("#door showmodelsglobal", "global")
|
|
).c_str()
|
|
);
|
|
c->Message(
|
|
Chat::White,
|
|
"#door showmodelsfromfile <file.eqg|file.s3d> | Shows models from s3d or eqg file. Example tssequip.eqg or wallet01.eqg"
|
|
);
|
|
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"{} | Shows available models in the current zone that you are in",
|
|
Saylink::Silent("#door showmodelszone")
|
|
).c_str()
|
|
);
|
|
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"{} | Shows available models globally by first listing all global model files",
|
|
Saylink::Silent("#door showmodelsglobal")
|
|
).c_str()
|
|
);
|
|
|
|
c->Message(Chat::White, "#door save | Creates database entry for selected door");
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"{} - Brings up editing interface for selected door",
|
|
Saylink::Silent("#door edit")
|
|
).c_str()
|
|
);
|
|
c->Message(
|
|
Chat::White,
|
|
fmt::format(
|
|
"{} - lists doors in zone",
|
|
Saylink::Silent("#list doors")
|
|
).c_str()
|
|
);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
void DoorManipulation::CommandHeader(Client *c)
|
|
{
|
|
c->Message(Chat::White, "------------------------------------------------");
|
|
c->Message(Chat::White, "# Door Commands");
|
|
c->Message(Chat::White, "------------------------------------------------");
|
|
}
|
|
|
|
void DoorManipulation::DisplayObjectResultToClient(
|
|
Client *c,
|
|
std::vector<ToolGameObjectsRepository::ToolGameObjects> game_objects
|
|
)
|
|
{
|
|
std::vector<std::string> say_links;
|
|
|
|
for (auto &g: game_objects) {
|
|
say_links.emplace_back(
|
|
fmt::format(
|
|
"[{}] ",
|
|
Saylink::Silent(
|
|
fmt::format("#door model {}", g.object_name),
|
|
g.object_name
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
int character_length = 0;
|
|
std::vector<std::string> buffered_links;
|
|
|
|
for (auto &links: say_links) {
|
|
buffered_links.emplace_back(links);
|
|
character_length += links.length();
|
|
|
|
// empty buffer
|
|
if (character_length > MAX_CLIENT_MESSAGE_LENGTH) {
|
|
std::string message_buffer;
|
|
|
|
for (auto &buffered_link: buffered_links) {
|
|
message_buffer += buffered_link;
|
|
}
|
|
|
|
c->Message(Chat::White, message_buffer.c_str());
|
|
|
|
// reset
|
|
character_length = 0;
|
|
buffered_links = {};
|
|
}
|
|
}
|
|
|
|
if (!buffered_links.empty()) {
|
|
c->Message(Chat::White, Strings::Implode(" ", buffered_links).c_str());
|
|
}
|
|
}
|
|
|
|
void DoorManipulation::DisplayModelsFromFileResults(
|
|
Client *c,
|
|
std::vector<ToolGameObjectsRepository::ToolGameObjects> game_objects
|
|
)
|
|
{
|
|
std::vector<std::string> say_links;
|
|
|
|
for (auto &g: game_objects) {
|
|
say_links.emplace_back(
|
|
fmt::format(
|
|
"[{}] ",
|
|
Saylink::Silent(
|
|
fmt::format("#door showmodelsfromfile {}", g.file_from),
|
|
g.file_from
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
int character_length = 0;
|
|
std::vector<std::string> buffered_links;
|
|
|
|
for (auto &links: say_links) {
|
|
buffered_links.emplace_back(links);
|
|
character_length += links.length();
|
|
|
|
// empty buffer
|
|
if (character_length > MAX_CLIENT_MESSAGE_LENGTH) {
|
|
std::string message_buffer;
|
|
|
|
for (auto &buffered_link: buffered_links) {
|
|
message_buffer += buffered_link;
|
|
}
|
|
|
|
c->Message(Chat::White, message_buffer.c_str());
|
|
|
|
// reset
|
|
character_length = 0;
|
|
buffered_links = {};
|
|
}
|
|
}
|
|
|
|
if (!buffered_links.empty()) {
|
|
c->Message(Chat::White, Strings::Implode(" ", buffered_links).c_str());
|
|
}
|
|
}
|