1
0

Added CreateChoiceWindow, ClearChoice and GetChoiceSpawnID lua functions, added base of tracking for min, max, avg, first level for raid/group into instances... more work to complete tomorrow

This commit is contained in:
Emagi 2024-12-04 21:53:57 -05:00
parent 48b2b1cc4d
commit c17b4d5588
19 changed files with 613 additions and 174 deletions

View File

@ -5595,7 +5595,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
case COMMAND_SET_GUILD_MEMBER_NOTE : { Command_SetGuildMemberNote(client, sep); break; }
case COMMAND_SET_GUILD_OFFICER_NOTE : { Command_SetGuildOfficerNote(client, sep); break; }
case COMMAND_GUILD : { Command_Guild(client, sep); break; }
case COMMAND_CREATE_GUILD : { Command_CreateGuild(client); break; }
case COMMAND_CREATE_GUILD : { Command_CreateGuild(client, sep); break; }
case COMMAND_GUILDS : { Command_Guilds(client); break; }
case COMMAND_GUILDS_ADD : { Command_GuildsAdd(client, sep); break; }
case COMMAND_GUILDS_CREATE : { Command_GuildsCreate(client, sep); break; }
@ -6541,9 +6541,9 @@ void Commands::Command_Guild(Client* client, Seperator* sep)
Purpose : Display's in-game Guild Creation window
Dev : Scatman
*/
void Commands::Command_CreateGuild(Client* client)
void Commands::Command_CreateGuild(Client* client, Seperator* sep)
{
client->SendGuildCreateWindow();
Command_GuildsCreate(client, sep, true);
}
/*
@ -6692,11 +6692,33 @@ void Commands::Command_GuildsAdd(Client* client, Seperator* sep)
Dev : Scatman
Example : /guilds create [guild name] (player name)
*/
void Commands::Command_GuildsCreate(Client* client, Seperator* sep)
void Commands::Command_GuildsCreate(Client* client, Seperator* sep, bool prompted_dialog)
{
Spawn* npc = nullptr;
if(prompted_dialog) {
auto target_npc = client->dialog_manager.getAcceptValue("create guild");
if(!target_npc) {
// well this is not acceptable!! CHEATER! :D
return;
}
else {
if(!client->GetPlayer()->GetZone()){
// player isn't in a zone? eh..
return;
}
npc = client->GetPlayer()->GetZone()->GetSpawnByID(target_npc);
if(!npc) {
client->Message(CHANNEL_COLOR_RED, "Did not find guild registrar, please re-initiate dialog with them.");
return;
}
}
}
if (sep && sep->arg[0])
{
const char* guild_name = sep->arg[0];
if(prompted_dialog)
guild_name = sep->argplus[0];
int8 resp = database.CheckNameFilter(guild_name, 4, 41);
if(!guild_name || resp == BADNAMELENGTH_REPLY) {
client->Message(CHANNEL_COLOR_YELLOW, "Guild name is too short.");
@ -6708,7 +6730,7 @@ void Commands::Command_GuildsCreate(Client* client, Seperator* sep)
{
bool ret = false;
if (sep->arg[1] && strlen(sep->arg[1]) > 0 && client->GetAdminStatus() > 0)
if (!prompted_dialog && sep->arg[1] && strlen(sep->arg[1]) > 0 && client->GetAdminStatus() > 0)
{
Client* to_client = zone_list.GetClientByCharName(string(sep->arg[1]));
@ -6728,7 +6750,7 @@ void Commands::Command_GuildsCreate(Client* client, Seperator* sep)
client->Message(CHANNEL_COLOR_YELLOW, "Could not find target %s or target is already in a guild.", sep->arg[1]);
}
}
else if (client->GetAdminStatus() > 0 && client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsPlayer())
else if (!prompted_dialog && client->GetAdminStatus() > 0 && client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsPlayer())
{
Client* to_client = ((Player*)client->GetPlayer()->GetTarget())->GetClient();
@ -6752,27 +6774,38 @@ void Commands::Command_GuildsCreate(Client* client, Seperator* sep)
{
if(client->GetPlayer()->GetGuild()) {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are already in a guild.");
if(client->GetAdminStatus() < 1)
return;
}
else if(net.is_primary) {
int32 guildID = world.CreateGuild(guild_name);
if(guildID > 0)
Client* leader = nullptr;
if(prompted_dialog || client->GetAdminStatus() < 1)
leader = client;
int32 guildID = world.CreateGuild(guild_name, leader);
if(guildID > 0) {
peer_manager.sendPeersCreateGuild(guildID);
}
else {
peer_manager.sendPrimaryCreateGuildRequest(std::string(guild_name), "");
}
ret = true;
}
}
else {
peer_manager.sendPrimaryCreateGuildRequest(std::string(guild_name), "", prompted_dialog, npc->GetID());
return;
}
}
if (ret)
if (ret) {
client->Message(CHANNEL_COLOR_YELLOW, "Guild '%s' was successfully created.", guild_name);
if(prompted_dialog) { // prompted dialog requires npc be defined
client->GetCurrentZone()->CallSpawnScript(npc, SPAWN_SCRIPT_CASTED_ON, client->GetPlayer(), guild_name);
}
}
else
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "There was an error creating the guild.");
}
else
client->Message(CHANNEL_COLOR_YELLOW, "Guild '%s' already exists.", guild_name);
}
else
else if(!prompted_dialog)
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage: /guilds create [guild name] (player name). If no player is specified, the player's target will be used. If the player has no target, a guild with no members will be created.");
}

View File

@ -311,14 +311,14 @@ public:
void Command_StopFollow(Client* client, Seperator* sep);
void Command_Grid(Client* client, Seperator* sep);
void Command_Guild(Client* client, Seperator* sep);
void Command_CreateGuild(Client* client);
void Command_CreateGuild(Client* client, Seperator* sep);
void Command_SetGuildOfficerNote(Client* client, Seperator* sep);
void Command_SetGuildMemberNote(Client* client, Seperator* sep);
void Command_OfficerSay(Client* client, Seperator* sep);
void Command_GuildSay(Client* client, Seperator* sep);
void Command_Guilds(Client* client);
void Command_GuildsAdd(Client* client, Seperator* sep);
void Command_GuildsCreate(Client* client, Seperator* sep);
void Command_GuildsCreate(Client* client, Seperator* sep, bool prompted_dialog = false);
void Command_GuildsDelete(Client* client, Seperator* sep);
void Command_GuildsList(Client* client);
void Command_GuildsRemove(Client* client, Seperator* sep);

View File

@ -14313,3 +14313,104 @@ int EQ2Emu_lua_CreatePersistedRespawn(lua_State* state) {
return 1;
}
int EQ2Emu_lua_CreateChoiceWindow(lua_State* state) {
if (!lua_interface)
return 0;
Spawn* npc = lua_interface->GetSpawn(state);
Spawn* spawn = lua_interface->GetSpawn(state, 2);
std::string windowTextPrompt = lua_interface->GetStringValue(state, 3);
std::string acceptText = lua_interface->GetStringValue(state, 4);
std::string acceptCommand = lua_interface->GetStringValue(state, 5);
std::string declineText = lua_interface->GetStringValue(state, 6);
std::string declineCommand = lua_interface->GetStringValue(state, 7);
int32 time = lua_interface->GetInt32Value(state, 8);
int8 textBox = lua_interface->GetInt8Value(state, 9);
int8 textBoxRequired = lua_interface->GetInt8Value(state, 10);
int32 maxLength = lua_interface->GetInt32Value(state, 11);
lua_interface->ResetFunctionStack(state);
if(!npc || !npc->IsNPC())
{
lua_interface->LogError("%s: LUA CreateChoiceWindow command error: npc is not valid, either does not exist or is not a npc", lua_interface->GetScriptName(state));
lua_interface->SetBooleanValue(state, false);
return 1;
}
if(!spawn || !spawn->IsPlayer())
{
lua_interface->LogError("%s: LUA CreateChoiceWindow command error: spawn is not valid, either does not exist or is not a player", lua_interface->GetScriptName(state));
lua_interface->SetBooleanValue(state, false);
return 1;
}
Client* client = ((Player*)spawn)->GetClient();
if(client) {
bool success = client->SendDialogChoice(npc->GetID(), windowTextPrompt, acceptText, acceptCommand, declineText, declineCommand, time, textBox, textBoxRequired, maxLength);
lua_interface->SetBooleanValue(state, success);
return 1;
}
lua_interface->SetBooleanValue(state, false);
return 1;
}
int EQ2Emu_lua_ClearChoice(lua_State* state) {
if (!lua_interface)
return 0;
Spawn* spawn = lua_interface->GetSpawn(state);
std::string commandToClear = lua_interface->GetStringValue(state, 2);
int8 clearDecline = lua_interface->GetInt8Value(state, 3);
lua_interface->ResetFunctionStack(state);
if(!spawn || !spawn->IsPlayer())
{
lua_interface->LogError("%s: LUA ClearChoice command error: spawn is not valid, either does not exist or is not a player", lua_interface->GetScriptName(state));
lua_interface->SetBooleanValue(state, false);
return 1;
}
Client* client = ((Player*)spawn)->GetClient();
if(client) {
bool success = false;
if(clearDecline) {
success = client->dialog_manager.clearDecline(commandToClear);
}
else {
success = client->dialog_manager.clearAccept(commandToClear);
}
lua_interface->SetBooleanValue(state, success);
return 1;
}
lua_interface->SetBooleanValue(state, false);
return 1;
}
int EQ2Emu_lua_GetChoiceSpawnID(lua_State* state) {
if (!lua_interface)
return 0;
Spawn* spawn = lua_interface->GetSpawn(state);
std::string commandMatch = lua_interface->GetStringValue(state, 2);
int8 declineValue = lua_interface->GetInt8Value(state, 3);
lua_interface->ResetFunctionStack(state);
if(!spawn || !spawn->IsPlayer())
{
lua_interface->LogError("%s: LUA GetChoiceSpawnID command error: spawn is not valid, either does not exist or is not a player", lua_interface->GetScriptName(state));
lua_interface->SetInt32Value(state, 0);
return 1;
}
Client* client = ((Player*)spawn)->GetClient();
int32 spawn_id = 0;
if(client) {
if(declineValue) {
spawn_id = client->dialog_manager.getDeclineValue(commandMatch);
}
else {
spawn_id = client->dialog_manager.getAcceptValue(commandMatch);
}
}
lua_interface->SetInt32Value(state, spawn_id);
return 1;
}

View File

@ -663,4 +663,8 @@ int EQ2Emu_lua_DespawnByLocationID(lua_State* state);
int EQ2Emu_lua_AddRespawn(lua_State* state);
int EQ2Emu_lua_CreatePersistedRespawn(lua_State* state);
int EQ2Emu_lua_CreateChoiceWindow(lua_State* state);
int EQ2Emu_lua_ClearChoice(lua_State* state);
int EQ2Emu_lua_GetChoiceSpawnID(lua_State* state);
#endif

View File

@ -1568,6 +1568,10 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
lua_register(state,"AddRespawn", EQ2Emu_lua_AddRespawn);
lua_register(state,"CreatePersistedRespawn", EQ2Emu_lua_CreatePersistedRespawn);
lua_register(state,"CreateChoiceWindow", EQ2Emu_lua_CreateChoiceWindow);
lua_register(state,"ClearChoice", EQ2Emu_lua_ClearChoice);
lua_register(state,"GetChoiceSpawnID", EQ2Emu_lua_GetChoiceSpawnID);
}
void LuaInterface::LogError(const char* error, ...) {

View File

@ -1977,7 +1977,81 @@ void PlayerGroupManager::ClearGroupRaidLooterFlag(int32 groupID) {
}
}
void PlayerGroupManager::EstablishRaidLevelRange(Client* client, int32* min_level, int32* max_level, int32* avg_level, int32* first_level) {
std::shared_lock lock(MGroups);
if(!client)
return;
if (!client->GetPlayer()->GetGroupMemberInfo()) {
*min_level = *max_level = *avg_level = *first_level = client->GetPlayer()->GetLevel();
return;
}
int32 groupID = client->GetPlayer()->GetGroupMemberInfo()->group_id;
if (m_groups.count(groupID) < 1) {
*min_level = *max_level = *avg_level = *first_level = client->GetPlayer()->GetLevel();
return;
}
PlayerGroup* group = m_groups[groupID];
if (group) {
bool isInRaid = group->IsInRaidGroup(group->GetID());
std::vector<int32> raidGroups;
group->GetRaidGroups(&raidGroups);
if (!isInRaid && raidGroups.size() < 1) {
raidGroups.push_back(group->GetID());
}
// Initialize tracking variables
int32 local_min_level = INT32_MAX;
int32 local_max_level = INT32_MIN;
int32 total_levels = 0;
int8 level_count = 0;
// Get the first player's level
*first_level = client->GetPlayer()->GetLevel();
for (auto& groupID : raidGroups) {
group = m_groups[groupID];
if (!group)
continue;
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>* members = group->GetMembers();
for (auto& memberInfo : *members) {
Entity* member = memberInfo->member;
if (!member || !member->IsPlayer())
continue;
if (member->GetZone() != client->GetPlayer()->GetZone())
continue;
int32 member_level = member->GetLevel();
local_min_level = std::min(local_min_level, member_level);
local_max_level = std::max(local_max_level, member_level);
total_levels += member_level;
level_count++;
}
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
// Finalize values with divide-by-zero protection
if (level_count > 0) {
*min_level = local_min_level;
*max_level = local_max_level;
*avg_level = total_levels / level_count;
}
else {
*min_level = *max_level = *avg_level = *first_level = client->GetPlayer()->GetLevel();
}
}
else {
*min_level = *max_level = *avg_level = *first_level = client->GetPlayer()->GetLevel();
}
}
Entity* PlayerGroup::GetGroupMemberByPosition(Entity* seeker, int32 mapped_position) {
Entity* ret = nullptr;

View File

@ -255,6 +255,7 @@ public:
void SplitWithGroupOrRaid(Client* client, int32 coin_plat, int32 coin_gold, int32 coin_silver, int32 coin_copper);
bool IdentifyMemberInGroupOrRaid(ZoneChangeDetails* details, Client* client, int32 zoneID, int32 instanceID = 0);
void ClearGroupRaidLooterFlag(int32 groupID);
void EstablishRaidLevelRange(Client* client, int32* min_level, int32* max_level, int32* avg_level, int32* first_level);
private:
int32 m_nextGroupID; // Used to generate a new unique id for new groups

View File

@ -295,6 +295,8 @@ void HandleCreateGuild(boost::property_tree::ptree tree) {
bool success = false;
int32 guildID = 0;
std::string leaderName("");
bool promptedDialog = false;
int32 spawnID = 0;
if (auto successful = tree.get_optional<bool>("success")) {
success = successful.get();
}
@ -307,6 +309,14 @@ void HandleCreateGuild(boost::property_tree::ptree tree) {
leaderName = name.get();
}
if (auto prompt = tree.get_optional<bool>("prompted_dialog")) {
promptedDialog = prompt.get();
}
if (auto spawnid = tree.get_optional<int32>("spawn_id")) {
spawnID = spawnid.get();
}
if (net.is_primary) {
// we send out to peers
}
@ -315,6 +325,12 @@ void HandleCreateGuild(boost::property_tree::ptree tree) {
Guild* guild = guild_list.GetGuild(guildID);
Client* leader = zone_list.GetClientByCharName(leaderName.c_str());
if (leader && guild && !leader->GetPlayer()->GetGuild()) {
if(spawnID) {
Spawn* npc = leader->GetPlayer()->GetZone()->GetSpawnByID(spawnID);
if(promptedDialog && npc && npc->IsNPC()) {
leader->GetCurrentZone()->CallSpawnScript(npc, SPAWN_SCRIPT_CASTED_ON, leader->GetPlayer(), guild->GetName());
}
}
guild->AddNewGuildMember(leader, 0, GUILD_RANK_LEADER);
database.SaveGuildMembers(guild);
if (leader && leader->GetPlayer()->GetGroupMemberInfo()) {

View File

@ -619,7 +619,7 @@ void PeerManager::sendPeersDisbandGroup(int32 group_id) {
}
}
bool PeerManager::sendPrimaryCreateGuildRequest(std::string guild_name, std::string leader_name) {
bool PeerManager::sendPrimaryCreateGuildRequest(std::string guild_name, std::string leader_name, bool prompted_dialog, int32 spawnID) {
std::shared_ptr<Peer> primary = getHealthyPrimaryPeerPtr();
if (primary) {
boost::property_tree::ptree root;
@ -628,6 +628,8 @@ bool PeerManager::sendPrimaryCreateGuildRequest(std::string guild_name, std::str
root.put("peer_web_port", std::to_string(net.GetWebWorldPort()));
root.put("guild_name", guild_name);
root.put("leader_name", leader_name);
root.put("prompted_dialog", prompted_dialog);
root.put("spawn_id", spawnID);
std::ostringstream jsonStream;
boost::property_tree::write_json(jsonStream, root);

View File

@ -206,7 +206,7 @@ public:
std::string peerId = "", std::vector<int32>* raidGroups = nullptr, bool is_update = false);
void sendPeersDisbandGroup(int32 group_id);
bool sendPrimaryCreateGuildRequest(std::string guild_name, std::string leader_name);
bool sendPrimaryCreateGuildRequest(std::string guild_name, std::string leader_name, bool prompted_dialog = false, int32 spawnID = 0);
void sendPeersAddGuildMember(int32 character_id, int32 guild_id, std::string invited_by, int32 join_timestamp, int8 rank);
void sendPeersRemoveGuildMember(int32 character_id, int32 guild_id, std::string removed_by);
void sendPeersCreateGuild(int32 guild_id);

View File

@ -618,6 +618,7 @@ void World::Web_worldhandle_startzone(const http::request<http::string_body>& re
int32 zoneId = 0;
std::string zoneName("");
bool alwaysLoaded = false;
int32 minLevel = 0, maxLevel = 0, avgLevel = 0, firstLevel = 0;
if (auto inst_id = json_tree.get_optional<int32>("instance_id")) {
instanceId = inst_id.get();
}
@ -634,6 +635,22 @@ void World::Web_worldhandle_startzone(const http::request<http::string_body>& re
alwaysLoaded = always_loaded.get();
}
if (auto level = json_tree.get_optional<int32>("min_level")) {
minLevel = level.get();
}
if (auto level = json_tree.get_optional<int32>("max_level")) {
maxLevel = level.get();
}
if (auto level = json_tree.get_optional<int32>("avg_level")) {
avgLevel = level.get();
}
if (auto level = json_tree.get_optional<int32>("first_level")) {
firstLevel = level.get();
}
sint32 success = 0;
ZoneChangeDetails details;
if (instanceId || zoneId || zoneName.length() > 0) {
@ -642,7 +659,7 @@ void World::Web_worldhandle_startzone(const http::request<http::string_body>& re
success = 1;
}
else {
if ((zone_list.GetZoneByInstance(&details, instanceId, zoneId, true, false, false, false)))
if ((zone_list.GetZoneByInstance(&details, instanceId, zoneId, true, false, false, false, minLevel, maxLevel, avgLevel, firstLevel)))
success = 1;
}
}

View File

@ -684,7 +684,7 @@ void ZoneList::Remove(ZoneServer* zone) {
}
}
bool ZoneList::GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::string opt_zone_name, bool loadZone, bool skip_existing_zones, bool increment_zone, bool check_peers, bool check_instances, bool only_always_loaded, bool skip_self) {
bool ZoneList::GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::string opt_zone_name, bool loadZone, bool skip_existing_zones, bool increment_zone, bool check_peers, bool check_instances, bool only_always_loaded, bool skip_self, int32 minLevel, int32 maxLevel, int32 avgLevel, int32 firstLevel) {
list<ZoneServer*>::iterator zone_iter;
ZoneServer* tmp = 0;
ZoneServer* ret = 0;
@ -736,6 +736,11 @@ bool ZoneList::GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::
root.put("zone_name", opt_zone_name);
root.put("zone_id", std::to_string(opt_zone_id));
root.put("always_loaded", only_always_loaded);
root.put("min_level", minLevel);
root.put("max_level", maxLevel);
root.put("avg_level", avgLevel);
root.put("first_level", firstLevel);
std::ostringstream jsonStream;
boost::property_tree::write_json(jsonStream, root);
std::string jsonPayload = jsonStream.str();
@ -751,7 +756,7 @@ bool ZoneList::GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::
}
else {
tmp = new ZoneServer(opt_zone_name.c_str());
database.LoadZoneInfo(tmp);
database.LoadZoneInfo(tmp, minLevel, maxLevel, avgLevel, firstLevel);
tmp->Init();
tmp->SetAlwaysLoaded(only_always_loaded);
}
@ -771,7 +776,8 @@ bool ZoneList::GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::
return (tmp != nullptr) ? true : false;
}
bool ZoneList::GetZoneByInstance(ZoneChangeDetails* zone_details, int32 instance_id, int32 zone_id, bool loadZone, bool skip_existing_zones, bool increment_zone, bool check_peers) {
bool ZoneList::GetZoneByInstance(ZoneChangeDetails* zone_details, int32 instance_id, int32 zone_id, bool loadZone, bool skip_existing_zones, bool increment_zone, bool check_peers,
int32 minLevel, int32 maxLevel, int32 avgLevel, int32 firstLevel) {
list<ZoneServer*>::iterator zone_iter;
ZoneServer* tmp = 0;
ZoneServer* ret = 0;
@ -814,6 +820,12 @@ bool ZoneList::GetZoneByInstance(ZoneChangeDetails* zone_details, int32 instance
root.put("zone_name", zonename);
root.put("zone_id", std::to_string(zone_id));
root.put("always_loaded", false);
root.put("min_level", minLevel);
root.put("max_level", maxLevel);
root.put("avg_level", avgLevel);
root.put("first_level", firstLevel);
std::ostringstream jsonStream;
boost::property_tree::write_json(jsonStream, root);
std::string jsonPayload = jsonStream.str();
@ -834,7 +846,7 @@ bool ZoneList::GetZoneByInstance(ZoneChangeDetails* zone_details, int32 instance
if ( instance_id > 0 )
tmp->SetupInstance(instance_id);
database.LoadZoneInfo(tmp);
database.LoadZoneInfo(tmp, minLevel, maxLevel, avgLevel, firstLevel);
tmp->Init();
}
}

View File

@ -419,8 +419,8 @@ class ZoneList {
void Add(ZoneServer* zone);
void Remove(ZoneServer* zone);
bool GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::string opt_zone_name = "", bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true, bool check_peers = true, bool check_instances = false, bool only_always_loaded = false, bool skip_self = false);
bool GetZoneByInstance(ZoneChangeDetails* zone_details, int32 instance_id, int32 zone_id = 0, bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true, bool check_peers = true);
bool GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::string opt_zone_name = "", bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true, bool check_peers = true, bool check_instances = false, bool only_always_loaded = false, bool skip_self = false, int32 minLevel = 0, int32 maxLevel = 0, int32 avgLevel = 0, int32 firstLevel = 0);
bool GetZoneByInstance(ZoneChangeDetails* zone_details, int32 instance_id, int32 zone_id = 0, bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true, bool check_peers = true, int32 minLevel = 0, int32 maxLevel = 0, int32 avgLevel = 0, int32 firstLevel = 0);
bool IsClientConnectedPeer(int32 account_id);
//ZoneServer* Get(int32 id, bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true);

View File

@ -2954,9 +2954,11 @@ void WorldDatabase::LoadCharacterFriendsIgnoreList(Player* player) {
}
}
void WorldDatabase::LoadZoneInfo(ZoneServer* zone){
void WorldDatabase::LoadZoneInfo(ZoneServer* zone, int32 minLevel, int32 maxLevel, int32 avgLevel, int32 firstLevel){
Query query;
int32 ruleset_id;
zone->setGroupRaidLevels(minLevel, maxLevel, avgLevel, firstLevel);
char* escaped = getEscapeString(zone->GetZoneName());
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, file, description, underworld, safe_x, safe_y, safe_z, min_status, min_level, max_level, instance_type+0, shutdown_timer, zone_motd, default_reenter_time, default_reset_time, default_lockout_time, force_group_to_zone, safe_heading, xp_modifier, ruleset_id, expansion_id, weather_allowed, sky_file, can_bind, can_gate, city_zone, can_evac FROM zones where name='%s'",escaped);
if(result && mysql_num_rows(result) > 0) {
@ -2999,8 +3001,10 @@ void WorldDatabase::LoadZoneInfo(ZoneServer* zone){
if (zone->IsInstanceZone())
{
if ( zone->GetInstanceID() < 1 )
zone->SetupInstance(CreateNewInstance(zone->GetZoneID()));
if ( zone->GetInstanceID() < 1 ) {
zone->SetupInstance(CreateNewInstance(zone->GetZoneID(), minLevel, maxLevel, avgLevel, firstLevel));
zone->setGroupRaidLevels(minLevel, maxLevel, avgLevel, firstLevel);
}
else
zone->SetupInstance(zone->GetInstanceID());
}
@ -4085,7 +4089,6 @@ void WorldDatabase::UpdateStartingZone(int32 char_id, int8 class_id, int8 race_i
instance_id = CreateNewInstance(zone_id);
tmp->SetInstanceID(instance_id);
LoadZoneInfo(tmp);
instance_id = CreateNewInstance(zone_id);
AddCharacterInstance(char_id, instance_id, string(tmp->GetZoneName()), tmp->GetInstanceType(), Timer::GetUnixTimeStamp(), 0, tmp->GetDefaultLockoutTime(), tmp->GetDefaultReenterTime());
safe_delete(tmp);
}
@ -5935,13 +5938,13 @@ bool WorldDatabase::UpdateInstancedSpawnRemoved(int32 spawn_location_entry_id, i
return false;
}
int32 WorldDatabase::CreateNewInstance(int32 zone_id)
int32 WorldDatabase::CreateNewInstance(int32 zone_id, int32 playersMinLevel, int32 playersMaxLevel, int32 playersavgLevel, int32 playersfirstLevel)
{
int32 ret = 0;
LogWrite(INSTANCE__DEBUG, 0, "Instance", "Creating new instance for zone: %u ", zone_id);
if( !database_new.Query("INSERT INTO instances (zone_id) VALUES (%u)", zone_id) )
if( !database_new.Query("INSERT INTO instances (zone_id, player_minlevel, player_maxlevel, player_avglevel, player_firstlevel) VALUES (%u, %u, %u, %u, %u)", zone_id, playersMinLevel, playersMaxLevel, playersavgLevel, playersfirstLevel) )
LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in CreateNewInstance() '%s': %i", database_new.GetErrorMsg(), database_new.GetError());
else
ret = database_new.LastInsertID();

View File

@ -217,7 +217,7 @@ public:
void LoadPlayerAA(Player *player);
void LoadCharacterQuestProgress(Client* client);
void LoadCharacterFriendsIgnoreList(Player* player);
void LoadZoneInfo(ZoneServer* zone);
void LoadZoneInfo(ZoneServer* zone, int32 minLevel=0, int32 maxLevel=0, int32 avgLevel=0, int32 firstLevel=0);
void LoadZoneInfo(ZoneInfo* zone_info);
int32 GetZoneID(const char* name);
void SaveZoneInfo(int32 zone_id, const char* field, sint32 value);
@ -398,7 +398,7 @@ public:
// Zone Instance DB Functions
map<int32,int32>* GetInstanceRemovedSpawns(int32 instance_id, int8 type);
int32 CreateNewInstance(int32 zone_id);
int32 CreateNewInstance(int32 zone_id, int32 playersMinLevel=0, int32 playersMaxLevel=0, int32 playersavgLevel=0, int32 playersfirstLevel=0);
//int32 AddCharacterInstance(int32 char_id, int32 instance_id, int32 grant_reenter_time_left=0, int32 grant_reset_time_left=0, int32 lockout_time=0);
int32 AddCharacterInstance(int32 char_id, int32 instance_id, string zone_name, int8 instance_type, int32 last_success, int32 last_failure, int32 success_lockout, int32 failure_lockout);
bool UpdateCharacterInstanceTimers(int32 char_id, int32 instance_id, int32 lockout_time=0, int32 reset_time=0, int32 reenter_time=0 );

View File

@ -4152,7 +4152,9 @@ void Client::SetCurrentZoneByInstanceID(int32 id, int32 zoneid) {
current_zone->RemoveSpawn(player, false, true, true, true, true);
}
ZoneChangeDetails zone_details;
if (zone_list.GetZoneByInstance(&zone_details, id, zoneid, true, false, true, false)) {
int32 minLevel = 0, maxLevel = 0, avgLevel = 0, firstLevel = 0;
world.GetGroupManager()->EstablishRaidLevelRange(this, &minLevel, &maxLevel, &avgLevel, &firstLevel);
if (zone_list.GetZoneByInstance(&zone_details, id, zoneid, true, false, true, false, minLevel, maxLevel, avgLevel, firstLevel)) {
SetCurrentZone((ZoneServer*)zone_details.zonePtr);
}
else {
@ -4649,6 +4651,9 @@ bool Client::IdentifyInstance(ZoneChangeDetails* zone_details, int32 zoneID) {
if (instanceType < 1)
return false;
int32 minLevel = 0, maxLevel = 0, avgLevel = 0, firstLevel = 0;
world.GetGroupManager()->EstablishRaidLevelRange(this, &minLevel, &maxLevel, &avgLevel, &firstLevel);
bool foundZone = false;
InstanceData* data = GetPlayer()->GetCharacterInstances()->FindInstanceByZoneID(zoneID);
if (data) {
@ -4659,7 +4664,7 @@ bool Client::IdentifyInstance(ZoneChangeDetails* zone_details, int32 zoneID) {
}
// Need to update `character_instances` table with new timestamps (for persistent) and instance id's
foundZone = zone_list.GetZoneByInstance(zone_details, data->instance_id, zoneID, true, false, false);
foundZone = zone_list.GetZoneByInstance(zone_details, data->instance_id, zoneID, true, false, false, true, minLevel, maxLevel, avgLevel, firstLevel);
// if we got an instance_zone and the instance_id from the data is 0 or data instance id is not the same as the zone instance id then update values
if (foundZone && (data->instance_id == 0 || data->instance_id != zone_details->instanceId)) {
@ -4685,6 +4690,9 @@ bool Client::TryZoneInstance(int32 zoneID, bool zone_coords_valid) {
// determine if this is a group instanced zone that already exists
foundZone = world.GetGroupManager()->IdentifyMemberInGroupOrRaid(&zone_details, this, zoneID);
int32 minLevel = 0, maxLevel = 0, avgLevel = 0, firstLevel = 0;
world.GetGroupManager()->EstablishRaidLevelRange(this, &minLevel, &maxLevel, &avgLevel, &firstLevel);
if (foundZone) {
InstanceData* data = nullptr;
if (zone_details.instanceId)
@ -4720,7 +4728,7 @@ bool Client::TryZoneInstance(int32 zoneID, bool zone_coords_valid) {
case GROUP_LOCKOUT_INSTANCE:
case RAID_LOCKOUT_INSTANCE:
{
if ((foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID))) {
if ((foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID, true, false, true, true, minLevel, maxLevel, avgLevel, firstLevel))) {
// once lockout instance zone shuts down you can't renenter if you have a lockout or if you don't you get a new zone
// so delete `instances` entry for the zone when it shuts down.
int32 db_id = database.AddCharacterInstance(GetPlayer()->GetCharacterID(), zone_details.instanceId, zone_details.zoneName, zone_details.instanceType, 0, 0, zone_details.defaultLockoutTime, zone_details.defaultReenterTime);
@ -4734,7 +4742,7 @@ bool Client::TryZoneInstance(int32 zoneID, bool zone_coords_valid) {
case GROUP_PERSIST_INSTANCE:
case RAID_PERSIST_INSTANCE:
{
if ((foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID))) {
if ((foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID, true, false, true, true, minLevel, maxLevel, avgLevel, firstLevel))) {
int32 db_id = database.AddCharacterInstance(GetPlayer()->GetCharacterID(), zone_details.instanceId, zone_details.zoneName, zone_details.instanceType, Timer::GetUnixTimeStamp(), 0, zone_details.defaultLockoutTime, zone_details.defaultReenterTime);
if (db_id > 0)
@ -4750,7 +4758,7 @@ bool Client::TryZoneInstance(int32 zoneID, bool zone_coords_valid) {
if (instance_zone) {
// Check the current population against the max population, if greater or equal start a new version
if (instance_zone->GetClientCount() >= rule_manager.GetZoneRule(GetCurrentZoneID(), R_Zone, MaxPlayers)->GetInt32()) {
foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID);
foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID, true, false, true, true, minLevel, maxLevel, avgLevel, firstLevel);
}
else {
peer_manager.setZonePeerDataSelf(&zone_details, std::string(instance_zone->GetZoneFile()), std::string(instance_zone->GetZoneName()),
@ -4762,7 +4770,7 @@ bool Client::TryZoneInstance(int32 zoneID, bool zone_coords_valid) {
}
}
else {
foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID);
foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID, true, false, true, true, minLevel, maxLevel, avgLevel, firstLevel);
}
break;
@ -4779,7 +4787,7 @@ bool Client::TryZoneInstance(int32 zoneID, bool zone_coords_valid) {
case QUEST_INSTANCE:
{
foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID);
foundZone = zone_list.GetZoneByInstance(&zone_details, 0, zoneID, true, false, true, true, minLevel, maxLevel, avgLevel, firstLevel);
break;
/*
ALTER TABLE `zones` CHANGE COLUMN `instance_type` `instance_type` ENUM('NONE','GROUP_LOCKOUT_INSTANCE','GROUP_PERSIST_INSTANCE','RAID_LOCKOUT_INSTANCE','RAID_PERSIST_INSTANCE','SOLO_LOCKOUT_INSTANCE','SOLO_PERSIST_INSTANCE','TRADESKILL_INSTANCE','PUBLIC_INSTANCE','PERSONAL_HOUSE_INSTANCE','GUILD_HOUSE_INSTANCE','QUEST_INSTANCE') NOT NULL DEFAULT 'NONE' COLLATE 'latin1_general_ci' AFTER `start_zone`;
@ -4938,7 +4946,9 @@ bool Client::CheckZoneAccess(const char* zoneName) {
void Client::Zone(int32 instanceid, bool set_coords, bool byInstanceID, bool is_spell) {
ZoneChangeDetails zone_details;
if (zone_list.GetZoneByInstance(&zone_details, instanceid, 0)) {
int32 minLevel = 0, maxLevel = 0, avgLevel = 0, firstLevel = 0;
world.GetGroupManager()->EstablishRaidLevelRange(this, &minLevel, &maxLevel, &avgLevel, &firstLevel);
if (zone_list.GetZoneByInstance(&zone_details, instanceid, 0, true, false, true, true, minLevel, maxLevel, avgLevel, firstLevel)) {
Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, set_coords, is_spell);
}
@ -5102,7 +5112,11 @@ void Client::Zone(const char* new_zone, bool set_coords, bool is_spell)
std::string camelCaseName = database.GetZoneName(zone_id);
InstanceData* data = GetPlayer()->GetCharacterInstances()->FindInstanceByZoneID(zone_id);
if ((data && zone_list.GetZoneByInstance(&zone_details, data->instance_id, zone_id)) || zone_list.GetZone(&zone_details, 0, camelCaseName)) {
int32 minLevel = 0, maxLevel = 0, avgLevel = 0, firstLevel = 0;
world.GetGroupManager()->EstablishRaidLevelRange(this, &minLevel, &maxLevel, &avgLevel, &firstLevel);
if ((data && zone_list.GetZoneByInstance(&zone_details, data->instance_id, zone_id, true, false, true, true, minLevel, maxLevel, avgLevel, firstLevel)) || zone_list.GetZone(&zone_details, 0, camelCaseName)) {
Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, set_coords, is_spell);
}
}
@ -13403,3 +13417,40 @@ void Client::SendReceiveOffer(Client* target_client, int8 type, std::string name
}
safe_delete(packet);
}
bool Client::SendDialogChoice(int32 spawnID, const std::string& windowTextPrompt, const std::string& acceptText, const std::string& acceptCommand, const std::string& declineText, const std::string& declineCommand, int32 time, int8 textBox, int8 textBoxRequired, int32 maxLength) {
PacketStruct* p = configReader.getStruct("WS_ChoiceWindow", GetVersion());
if (!p) {
LogWrite(CCLIENT__ERROR, 0, "Client", "CreateChoiceWindow command error: WS_ChoiceWindow packet does not exist for client version %u", GetVersion());
return false;
}
bool successAccept = true;
bool successDecline = true;
if(acceptCommand.size() > 0)
successAccept = dialog_manager.addAccept(acceptCommand, spawnID, time);
if(declineCommand.size() > 0)
successDecline = dialog_manager.addDecline(declineCommand, spawnID, time);
if(!successAccept || !successDecline) { // failed to successfully add command
LogWrite(CCLIENT__ERROR, 0, "Client", "CreateChoiceWindow command error: Window has a conflict with accept (%s) %u or decline (%s) %u. Call ClearChoice(Player, Command, x) x being 0 for accept 1 for decline commands.", acceptCommand.c_str(), successAccept, declineCommand.c_str(), successDecline);
return false;
}
p->setMediumStringByName("text", windowTextPrompt.c_str());
p->setMediumStringByName("accept_text", acceptText.c_str());
p->setMediumStringByName("accept_command", acceptCommand.c_str());
p->setMediumStringByName("cancel_text", declineText.c_str());
p->setMediumStringByName("cancel_command", declineCommand.c_str());
p->setDataByName("time", time);
p->setDataByName("text_box", textBox);
p->setDataByName("text_required", textBoxRequired);
p->setDataByName("max_length", maxLength);
QueuePacket(p->serialize());
safe_delete(p);
return true;
}

View File

@ -24,6 +24,12 @@
#include <atomic>
#include <mutex>
#include <shared_mutex>
#include <iostream>
#include <map>
#include <string>
#include <thread>
#include <chrono>
#include <condition_variable>
#include "../common/EQStream.h"
#include "../common/timer.h"
@ -150,6 +156,85 @@ struct WaypointInfo {
};
class DialogManager {
public:
// Add accept string with int32 id and a timer
bool addAccept(const std::string& key, int32 id, int32 seconds) {
std::lock_guard<std::mutex> lock(mutex_);
if (acceptMap_.count(key) == 0) {
acceptMap_[key] = id;
if (seconds)
startTimer(key, seconds, true); // true indicates it's an accept key
return true;
}
return false; // Duplicate found in either map
}
// Add decline string with int32 id and a timer
bool addDecline(const std::string& key, int32 id, int32 seconds) {
std::lock_guard<std::mutex> lock(mutex_);
if (declineMap_.count(key) == 0) {
declineMap_[key] = id;
if (seconds)
startTimer(key, seconds, false); // false indicates it's a decline key
return true;
}
return false; // Duplicate found in either map
}
// Clear a specific accept string
bool clearAccept(const std::string& key) {
std::lock_guard<std::mutex> lock(mutex_);
return acceptMap_.erase(key) > 0;
}
// Clear a specific decline string
bool clearDecline(const std::string& key) {
std::lock_guard<std::mutex> lock(mutex_);
return declineMap_.erase(key) > 0;
}
int32 getAcceptValue(const std::string& key) {
std::lock_guard<std::mutex> lock(mutex_);
if (acceptMap_.count(key)) {
return acceptMap_.at(key);
}
return 0; // Key not found
}
int32 getDeclineValue(const std::string& key) {
std::lock_guard<std::mutex> lock(mutex_);
if (declineMap_.count(key)) {
return declineMap_.at(key);
}
return 0; // Key not found
}
// Check if a dialog is active
bool isDialogActive(const std::string& key) {
std::lock_guard<std::mutex> lock(mutex_);
return acceptMap_.count(key) > 0 || declineMap_.count(key) > 0;
}
private:
std::map<std::string, int32> acceptMap_;
std::map<std::string, int32> declineMap_;
std::mutex mutex_;
void startTimer(const std::string& key, int32 seconds, bool isAccept) {
std::thread([this, key, seconds, isAccept]() {
std::this_thread::sleep_for(std::chrono::seconds(seconds));
std::lock_guard<std::mutex> lock(mutex_);
if (isAccept) {
acceptMap_.erase(key);
}
else {
declineMap_.erase(key);
}
}).detach();
}
};
class Client {
public:
Client(EQStream* ieqs);
@ -601,6 +686,9 @@ public:
void HandleGroupAcceptResponse(int8 result);
void SetGroupOptionsReference(GroupOptions* options);
void SendReceiveOffer(Client* client_target, int8 type, std::string name, int8 unknown2);
bool SendDialogChoice(int32 spawnID, const std::string& windowTextPrompt, const std::string& acceptText, const std::string& acceptCommand, const std::string& declineText, const std::string& declineCommand, int32 time, int8 textBox, int8 textBoxRequired, int32 maxLength);
DialogManager dialog_manager;
private:
void AddRecipeToPlayerPack(Recipe* recipe, PacketStruct* packet, int16* i);
void SavePlayerImages();

View File

@ -177,6 +177,11 @@ ZoneServer::ZoneServer(const char* name) {
lifetime_client_count = 0;
groupraidMinLevel = 0;
groupraidMaxLevel = 0;
groupraidAvgLevel = 0;
groupraidFirstLevel = 0;
is_initialized = false;
}

View File

@ -1036,6 +1036,11 @@ private:
mutable std::shared_mutex MIgnoredWidgets;
std::map<int32, bool> ignored_widgets;
Map* default_zone_map; // this is the map that npcs, ground spawns, so on use. May not be the same as the clients!
int32 groupraidMinLevel;
int32 groupraidMaxLevel;
int32 groupraidAvgLevel;
int32 groupraidFirstLevel;
public:
Spawn* GetSpawn(int32 id);
@ -1168,6 +1173,29 @@ public:
void SendStateCommand(Spawn* spawn, int32 state);
int32 getGroupraidMinLevel() const {
return groupraidMinLevel;
}
int32 getGroupraidMaxLevel() const {
return groupraidMaxLevel;
}
int32 getGroupraidAvgLevel() const {
return groupraidAvgLevel;
}
int32 getGroupraidFirstLevel() const {
return groupraidFirstLevel;
}
void setGroupRaidLevels(int32 min_level, int32 max_level, int32 avg_level, int32 first_level) {
groupraidMinLevel = min_level;
groupraidMaxLevel = max_level;
groupraidAvgLevel = avg_level;
groupraidFirstLevel = first_level;
}
int32 lifetime_client_count;
int32 incoming_clients;
};