mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-23 09:02:29 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9974aaff57 | |||
| b6c3e549da | |||
| 70ee95efc0 | |||
| 1d38e473d7 | |||
| 1aa3a4b11a | |||
| 398ecbc8cf | |||
| c4613e1b0f | |||
| 3003a59955 | |||
| 0c582cc4f9 | |||
| 69c42510ca | |||
| 79c8858ec8 | |||
| c3d8d423fe | |||
| 1cbda61891 | |||
| 8d12a5b1b1 | |||
| 182327b385 | |||
| aa0ca88d9d | |||
| 103a37e762 | |||
| 34f19489d0 | |||
| c001060429 | |||
| 89be55254e | |||
| 2da6190950 | |||
| d5e024cc02 |
@@ -1,3 +1,67 @@
|
||||
## [22.46.0] 3/2/2024
|
||||
|
||||
### Commands
|
||||
|
||||
* Add #fish Command ([#4136](https://github.com/EQEmu/Server/pull/4136)) @Kinglykrab 2024-03-01
|
||||
|
||||
### Crash Fix
|
||||
|
||||
* Raid::UpdateGroupAAs ([#4139](https://github.com/EQEmu/Server/pull/4139)) @neckkola 2024-03-02
|
||||
* Update to location of qGlobals initialization ([#4144](https://github.com/EQEmu/Server/pull/4144)) @neckkola 2024-03-02
|
||||
|
||||
### Feature
|
||||
|
||||
* Exempt a zone from IP-limit checks. ([#4137](https://github.com/EQEmu/Server/pull/4137)) @catapultam-habeo 2024-03-02
|
||||
|
||||
### Fixes
|
||||
|
||||
* Cleanup NPC Mana Tap Logic ([#4134](https://github.com/EQEmu/Server/pull/4134)) @noudess 2024-03-02
|
||||
* Fix Bots/Bot Pets ending up on XTargets ([#4132](https://github.com/EQEmu/Server/pull/4132)) @Kinglykrab 2024-03-02
|
||||
* Fix issue with NPC Secondary Textures ([#4129](https://github.com/EQEmu/Server/pull/4129)) @Kinglykrab 2024-03-01
|
||||
* GetBotNameByID Temporary Reference Warning ([#4145](https://github.com/EQEmu/Server/pull/4145)) @Kinglykrab 2024-03-02
|
||||
* Update FreeGuildID Routine ([#4143](https://github.com/EQEmu/Server/pull/4143)) @neckkola 2024-03-02
|
||||
* Use std::clamp for Mob::ChangeSize ([#4140](https://github.com/EQEmu/Server/pull/4140)) @joligario 2024-03-02
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add Bot Special Attacks for Immune Aggro/Damage ([#4108](https://github.com/EQEmu/Server/pull/4108)) @Kinglykrab 2024-03-02
|
||||
|
||||
### Zone
|
||||
|
||||
* Zone Routing Improvements ([#4142](https://github.com/EQEmu/Server/pull/4142)) @Akkadius 2024-03-02
|
||||
|
||||
## [22.45.1] 2/29/2024
|
||||
|
||||
### Character Creation
|
||||
|
||||
* Improved Random Name Generator ([#4081](https://github.com/EQEmu/Server/pull/4081)) @catapultam-habeo 2024-02-27
|
||||
|
||||
### Code
|
||||
|
||||
* Fix Server Rules Documentation Generation ([#4125](https://github.com/EQEmu/Server/pull/4125)) @Kinglykrab 2024-02-26
|
||||
* Remove unnecessary stoptimer logs ([#4128](https://github.com/EQEmu/Server/pull/4128)) @Kinglykrab 2024-02-28
|
||||
|
||||
### Commands
|
||||
|
||||
* Add `#forage` command ([#4133](https://github.com/EQEmu/Server/pull/4133)) @joligario 2024-02-29
|
||||
|
||||
### Crash
|
||||
|
||||
* Fix crash issue during database dump ([#4127](https://github.com/EQEmu/Server/pull/4127)) @Akkadius 2024-02-29
|
||||
|
||||
### Crash Fix
|
||||
|
||||
* D20 crash if mitigation average resulted in 0 ([#4131](https://github.com/EQEmu/Server/pull/4131)) @nytmyr 2024-02-29
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix forage returning first result from table ([#4130](https://github.com/EQEmu/Server/pull/4130)) @nytmyr 2024-02-29
|
||||
* Who /all displays incorrect guild name ([#4123](https://github.com/EQEmu/Server/pull/4123)) @neckkola 2024-02-25
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add Pet Owner Methods to Perl/Lua ([#4115](https://github.com/EQEmu/Server/pull/4115)) @Kinglykrab 2024-02-25
|
||||
|
||||
## [22.45.0] 2/24/2024
|
||||
|
||||
### Beacon
|
||||
|
||||
@@ -178,12 +178,13 @@ void WorldContentService::ReloadContentFlags()
|
||||
LogInfo(
|
||||
"Loaded content flag [{}] [{}]",
|
||||
f.flag_name,
|
||||
(f.enabled ? "Enabled" : "Disabled")
|
||||
(f.enabled ? "enabled" : "disabled")
|
||||
);
|
||||
}
|
||||
|
||||
SetContentFlags(set_content_flags);
|
||||
SetContentZones(ZoneRepository::All(*m_content_database));
|
||||
LoadZones();
|
||||
LoadStaticGlobalZoneInstances();
|
||||
}
|
||||
|
||||
Database *WorldContentService::GetDatabase() const
|
||||
@@ -235,19 +236,6 @@ void WorldContentService::SetContentFlag(const std::string &content_flag_name, b
|
||||
ReloadContentFlags();
|
||||
}
|
||||
|
||||
// SetZones sets the zones for the world content service
|
||||
// this is used for zone routing middleware
|
||||
// we pull the zone list from the zone repository and feed from the zone store for now
|
||||
// we're holding a copy in the content service - but we're talking 250kb of data in memory to handle routing of zoning
|
||||
WorldContentService *WorldContentService::SetContentZones(const std::vector<BaseZoneRepository::Zone>& zones)
|
||||
{
|
||||
m_zones = zones;
|
||||
|
||||
LogInfo("Loaded [{}] zones", m_zones.size());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// HandleZoneRoutingMiddleware is meant to handle content and context aware zone routing
|
||||
//
|
||||
// example # 1
|
||||
@@ -260,16 +248,52 @@ WorldContentService *WorldContentService::SetContentZones(const std::vector<Base
|
||||
// scripts handle all the same way, you don't have to think about instances, the middleware will handle the magic
|
||||
// the versions of zones are represented by two zone entries that have potentially different min/max expansion and/or different content flags
|
||||
// we decide to route the client to the correct version of the zone based on the current server side expansion
|
||||
// example # 2
|
||||
void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
|
||||
{
|
||||
// if we're already in an instance, we don't want to route the player to another instance
|
||||
if (zc->instanceID > 0) {
|
||||
auto r = FindZone(zc->zoneID, zc->instanceID);
|
||||
if (r.zone_id == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
zc->instanceID = r.instance.id;
|
||||
}
|
||||
|
||||
// LoadStaticGlobalZoneInstances loads all static global zone instances
|
||||
// these are zones that are never set to expire and are global
|
||||
// these are used commonly in v1/v2/v3 versions of the same zone for expansion routing
|
||||
WorldContentService * WorldContentService::LoadStaticGlobalZoneInstances()
|
||||
{
|
||||
m_zone_instances = InstanceListRepository::GetWhere(*GetDatabase(), fmt::format("never_expires = 1 AND is_global = 1"));
|
||||
|
||||
LogInfo("Loaded [{}] zone_instances", m_zone_instances.size());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// LoadZones sets the zones for the world content service
|
||||
// this is used for zone routing middleware
|
||||
// we pull the zone list from the zone repository and feed from the zone store for now
|
||||
// we're holding a copy in the content service - but we're talking 250kb of data in memory to handle routing of zoning
|
||||
WorldContentService * WorldContentService::LoadZones()
|
||||
{
|
||||
m_zones = ZoneRepository::All(*GetContentDatabase());
|
||||
|
||||
LogInfo("Loaded [{}] zones", m_zones.size());
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// FindZone is critical to the zone routing middleware and any logic that needs to route players to the correct zone
|
||||
// era contextual routing, multiple version of zones, etc
|
||||
WorldContentService::FindZoneResult WorldContentService::FindZone(uint32 zone_id, uint32 instance_id)
|
||||
{
|
||||
// if we're already in a regular instance, we don't want to route the player to another instance
|
||||
if (instance_id > RuleI(Instances, ReservedInstances)) {
|
||||
return WorldContentService::FindZoneResult{};
|
||||
}
|
||||
|
||||
for (auto &z: m_zones) {
|
||||
if (z.zoneidnumber == zc->zoneID) {
|
||||
if (z.zoneidnumber == zone_id) {
|
||||
auto f = ContentFlags{
|
||||
.min_expansion = z.min_expansion,
|
||||
.max_expansion = z.max_expansion,
|
||||
@@ -286,33 +310,45 @@ void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
|
||||
z.long_name
|
||||
);
|
||||
|
||||
auto instances = InstanceListRepository::GetWhere(
|
||||
*GetDatabase(),
|
||||
fmt::format(
|
||||
"zone = {} AND version = {} AND never_expires = 1 AND is_global = 1",
|
||||
z.zoneidnumber,
|
||||
z.version
|
||||
)
|
||||
// first pass, explicit match on public static global zone instances
|
||||
for (auto &i: m_zone_instances) {
|
||||
if (i.zone == zone_id && i.version == z.version) {
|
||||
LogInfo(
|
||||
"Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
i.id,
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name,
|
||||
i.notes
|
||||
);
|
||||
|
||||
return WorldContentService::FindZoneResult{
|
||||
.zone_id = static_cast<uint32>(z.zoneidnumber),
|
||||
.instance = i,
|
||||
.zone = z
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"Routed player to non-instance zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name,
|
||||
z.note
|
||||
);
|
||||
|
||||
if (!instances.empty()) {
|
||||
auto instance = instances.front();
|
||||
zc->instanceID = instance.id;
|
||||
|
||||
LogInfo(
|
||||
"Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
instance.id,
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name,
|
||||
instance.notes
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
return WorldContentService::FindZoneResult{
|
||||
.zone_id = static_cast<uint32>(z.zoneidnumber),
|
||||
.instance = InstanceListRepository::NewEntity(),
|
||||
.zone = z
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return WorldContentService::FindZoneResult{};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
#include "../repositories/content_flags_repository.h"
|
||||
#include "../repositories/zone_repository.h"
|
||||
#include "../repositories/instance_list_repository.h"
|
||||
|
||||
class Database;
|
||||
|
||||
@@ -169,7 +170,14 @@ public:
|
||||
void SetContentFlag(const std::string &content_flag_name, bool enabled);
|
||||
|
||||
void HandleZoneRoutingMiddleware(ZoneChange_Struct *zc);
|
||||
WorldContentService * SetContentZones(const std::vector<ZoneRepository::Zone>& zones);
|
||||
|
||||
struct FindZoneResult {
|
||||
uint32 zone_id = 0;
|
||||
InstanceListRepository::InstanceList instance;
|
||||
ZoneRepository::Zone zone;
|
||||
};
|
||||
|
||||
FindZoneResult FindZone(uint32 zone_id, uint32 instance_id);
|
||||
private:
|
||||
int current_expansion{};
|
||||
std::vector<ContentFlagsRepository::ContentFlags> content_flags;
|
||||
@@ -180,6 +188,9 @@ private:
|
||||
|
||||
// holds a record of the zone table from the database
|
||||
std::vector<ZoneRepository::Zone> m_zones = {};
|
||||
WorldContentService *LoadStaticGlobalZoneInstances();
|
||||
std::vector<InstanceListRepository::InstanceList> m_zone_instances;
|
||||
WorldContentService * LoadZones();
|
||||
};
|
||||
|
||||
extern WorldContentService content_service;
|
||||
|
||||
@@ -575,7 +575,12 @@ void DatabaseDumpService::RemoveSqlBackup()
|
||||
{
|
||||
std::string file = fmt::format("{}.sql", GetDumpFileNameWithPath());
|
||||
if (File::Exists(file)) {
|
||||
std::filesystem::remove(file);
|
||||
try {
|
||||
std::filesystem::remove(file);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
LogError("std::filesystem::remove err [{}]", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
RemoveCredentialsFile();
|
||||
|
||||
@@ -698,6 +698,8 @@ const std::map<uint32, std::string>& EQ::constants::GetSpecialAbilityMap()
|
||||
{ IMMUNE_OPEN, "Immune to Open" },
|
||||
{ IMMUNE_ASSASSINATE, "Immune to Assassinate" },
|
||||
{ IMMUNE_HEADSHOT, "Immune to Headshot" },
|
||||
{ IMMUNE_AGGRO_BOT, "Immune to Bot Aggro" },
|
||||
{ IMMUNE_DAMAGE_BOT, "Immune to Bot Damage" },
|
||||
};
|
||||
|
||||
return special_ability_map;
|
||||
|
||||
@@ -656,7 +656,9 @@ enum {
|
||||
IMMUNE_OPEN = 53,
|
||||
IMMUNE_ASSASSINATE = 54,
|
||||
IMMUNE_HEADSHOT = 55,
|
||||
MAX_SPECIAL_ATTACK = 56
|
||||
IMMUNE_AGGRO_BOT = 56,
|
||||
IMMUNE_DAMAGE_BOT = 57,
|
||||
MAX_SPECIAL_ATTACK = 58
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@ namespace Logs {
|
||||
Zoning,
|
||||
EqTime,
|
||||
Corpses,
|
||||
XTargets,
|
||||
MaxCategoryID /* Don't Remove this */
|
||||
};
|
||||
|
||||
@@ -241,6 +242,7 @@ namespace Logs {
|
||||
"Zoning",
|
||||
"EqTime",
|
||||
"Corpses",
|
||||
"XTargets"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -834,6 +834,16 @@
|
||||
OutF(LogSys, Logs::Detail, Logs::Corpses, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogXTargets(message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(Logs::General, Logs::XTargets))\
|
||||
OutF(LogSys, Logs::General, Logs::XTargets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define LogXTargetsDetail(message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(Logs::Detail, Logs::XTargets))\
|
||||
OutF(LogSys, Logs::Detail, Logs::XTargets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} while (0)
|
||||
|
||||
#define Log(debug_level, log_category, message, ...) do {\
|
||||
if (LogSys.IsLogEnabled(debug_level, log_category))\
|
||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
|
||||
+7
-34
@@ -342,41 +342,14 @@ bool BaseGuildManager::_StoreGuildDB(uint32 guild_id)
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 BaseGuildManager::_GetFreeGuildID()
|
||||
{
|
||||
GuildsRepository::DeleteWhere(*m_db, "`name` = ''");
|
||||
|
||||
GuildsRepository::Guilds out;
|
||||
out.id = 0;
|
||||
out.leader = 0;
|
||||
out.minstatus = 0;
|
||||
out.tribute = 0;
|
||||
out.name = "";
|
||||
out.motd = "";
|
||||
out.motd_setter = "";
|
||||
out.url = "";
|
||||
out.channel = "";
|
||||
auto last_insert_id = GuildsRepository::InsertOne(*m_db, out);
|
||||
if (last_insert_id.id > 0) {
|
||||
LogGuilds("Located a free guild ID [{}] in the database", last_insert_id.id);
|
||||
return last_insert_id.id;
|
||||
}
|
||||
|
||||
LogGuilds("Unable to find a free guild ID in the database");
|
||||
return GUILD_NONE;
|
||||
}
|
||||
|
||||
uint32 BaseGuildManager::CreateGuild(std::string name, uint32 leader_char_id)
|
||||
{
|
||||
uint32 guild_id = UpdateDbCreateGuild(name, leader_char_id);
|
||||
if (guild_id == GUILD_NONE) {
|
||||
return (GUILD_NONE);
|
||||
}
|
||||
//RefreshGuild(guild_id);
|
||||
//SendGuildRefresh(guild_id, true, false, false, false);
|
||||
//SendCharRefresh(GUILD_NONE, guild_id, leader_char_id);
|
||||
uint32 guild_id = UpdateDbCreateGuild(name, leader_char_id);
|
||||
if (guild_id == GUILD_NONE) {
|
||||
return (GUILD_NONE);
|
||||
}
|
||||
|
||||
return guild_id;
|
||||
return guild_id;
|
||||
}
|
||||
|
||||
bool BaseGuildManager::DeleteGuild(uint32 guild_id)
|
||||
@@ -539,8 +512,8 @@ bool BaseGuildManager::SetPublicNote(uint32 charid, std::string public_note)
|
||||
|
||||
uint32 BaseGuildManager::UpdateDbCreateGuild(std::string name, uint32 leader)
|
||||
{
|
||||
auto new_id = _GetFreeGuildID();
|
||||
if (new_id == GUILD_NONE) {
|
||||
auto new_id = GuildsRepository::GetMaxId(*m_db) + 1;
|
||||
if (!new_id) {
|
||||
return GUILD_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -208,7 +208,6 @@ class BaseGuildManager
|
||||
|
||||
bool _StoreGuildDB(uint32 guild_id);
|
||||
GuildInfo* _CreateGuild(uint32 guild_id, std::string guild_name, uint32 leader_char_id, uint8 minstatus, std::string guild_motd, std::string motd_setter, std::string Channel, std::string URL, uint32 favour);
|
||||
uint32 _GetFreeGuildID();
|
||||
GuildsRepository::Guilds CreateGuildRepoFromGuildInfo(uint32 guild_id, BaseGuildManager::GuildInfo& in);
|
||||
};
|
||||
#endif /*GUILD_BASE_H_*/
|
||||
|
||||
+35
-5
@@ -34,8 +34,8 @@ const char *RuleManager::s_categoryNames[_CatCount + 1] = {
|
||||
"InvalidCategory"
|
||||
};
|
||||
|
||||
const RuleManager::RuleInfo RuleManager::s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount + 1] = {
|
||||
/* this is done in three steps so we can reliably get to them by index*/
|
||||
const RuleManager::RuleInfo RuleManager::s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount + StringRuleCount + 1] = {
|
||||
/* this is done in three steps, so we can reliably get to them by index*/
|
||||
#define RULE_INT(category_name, rule_name, default_value, notes) \
|
||||
{ #category_name ":" #rule_name, Category__##category_name, IntRule, Int__##rule_name, notes },
|
||||
#include "ruletypes.h"
|
||||
@@ -45,6 +45,9 @@ const RuleManager::RuleInfo RuleManager::s_RuleInfo[IntRuleCount + RealRuleCount
|
||||
#define RULE_BOOL(category_name, rule_name, default_value, notes) \
|
||||
{ #category_name ":" #rule_name, Category__##category_name, BoolRule, Bool__##rule_name, notes },
|
||||
#include "ruletypes.h"
|
||||
#define RULE_STRING(category_name, rule_name, default_value, notes) \
|
||||
{ #category_name ":" #rule_name, Category__##category_name, StringRule, String__##rule_name, notes },
|
||||
#include "ruletypes.h"
|
||||
{ "Invalid Rule", _CatCount, IntRule }
|
||||
};
|
||||
|
||||
@@ -114,6 +117,9 @@ bool RuleManager::GetRule(const std::string &rule_name, std::string &rule_value)
|
||||
case BoolRule:
|
||||
rule_value = m_RuleBoolValues[index] ? "true" : "false";
|
||||
break;
|
||||
case StringRule:
|
||||
rule_value = m_RuleStringValues[index];
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -152,6 +158,10 @@ bool RuleManager::SetRule(const std::string &rule_name, const std::string &rule_
|
||||
m_RuleBoolValues[index] = static_cast<uint32>(Strings::ToBool(rule_value));
|
||||
LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleBoolValues[index] == 1 ? "true" : "false");
|
||||
break;
|
||||
case StringRule:
|
||||
m_RuleStringValues[index] = rule_value;
|
||||
LogRules("Set rule [{}] to value [{}]", rule_name, rule_value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (db_save) {
|
||||
@@ -215,11 +225,13 @@ std::string RuleManager::_GetRuleName(RuleType type, uint16 index) {
|
||||
return s_RuleInfo[index + IntRuleCount].name;
|
||||
case BoolRule:
|
||||
return s_RuleInfo[index + IntRuleCount + RealRuleCount].name;
|
||||
case StringRule:
|
||||
return s_RuleInfo[index + IntRuleCount + RealRuleCount + StringRuleCount].name;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount].name;
|
||||
return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount + StringRuleCount].name;
|
||||
}
|
||||
|
||||
//assumes index is valid!
|
||||
@@ -231,11 +243,13 @@ const std::string &RuleManager::_GetRuleNotes(RuleType type, uint16 index) {
|
||||
return s_RuleInfo[index + IntRuleCount].notes;
|
||||
case BoolRule:
|
||||
return s_RuleInfo[index + IntRuleCount + RealRuleCount].notes;
|
||||
case StringRule:
|
||||
return s_RuleInfo[index + IntRuleCount + RealRuleCount + StringRuleCount].notes;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount].notes;
|
||||
return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount + StringRuleCount].notes;
|
||||
}
|
||||
|
||||
bool RuleManager::LoadRules(Database *db, const std::string &rule_set_name, bool reload) {
|
||||
@@ -343,6 +357,10 @@ void RuleManager::SaveRules(Database *db, const std::string &rule_set_name) {
|
||||
for (i = 0; i < BoolRuleCount; i++) {
|
||||
_SaveRule(db, BoolRule, i);
|
||||
}
|
||||
|
||||
for (i = 0; i < StringRuleCount; i++) {
|
||||
_SaveRule(db, StringRule, i);
|
||||
}
|
||||
}
|
||||
|
||||
void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) {
|
||||
@@ -367,6 +385,9 @@ void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) {
|
||||
case BoolRule:
|
||||
rule_value = m_RuleBoolValues[index] ? "true" : "false";
|
||||
break;
|
||||
case StringRule:
|
||||
rule_value = m_RuleStringValues[index];
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& rule_notes = _GetRuleNotes(type, index);
|
||||
@@ -446,6 +467,10 @@ bool RuleManager::UpdateInjectedRules(Database *db, const std::string &rule_set_
|
||||
rule_data[r.name].first = fmt::format("{}", m_RuleBoolValues[r.rule_index] ? "true" : "false");
|
||||
rule_data[r.name].second = &r.notes;
|
||||
break;
|
||||
case StringRule:
|
||||
rule_data[r.name].first = m_RuleStringValues[r.rule_index];
|
||||
rule_data[r.name].second = &r.notes;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -552,7 +577,7 @@ bool RuleManager::RestoreRuleNotes(Database *db)
|
||||
}
|
||||
}
|
||||
|
||||
return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount];
|
||||
return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount + StringRuleCount];
|
||||
}(e.rule_name);
|
||||
|
||||
if (Strings::Contains(rule.name, e.rule_name)) {
|
||||
@@ -617,3 +642,8 @@ bool RuleManager::GetBoolRule(RuleManager::BoolType t) const
|
||||
{
|
||||
return m_RuleBoolValues[t] == 1;
|
||||
}
|
||||
|
||||
std::string RuleManager::GetStringRule(RuleManager::StringType t) const
|
||||
{
|
||||
return m_RuleStringValues[t];
|
||||
}
|
||||
|
||||
+45
-25
@@ -23,6 +23,7 @@
|
||||
* - RuleI(category, rule) -> fetch an integer rule's value
|
||||
* - RuleR(category, rule) -> fetch a real (float) rule's value
|
||||
* - RuleB(category, rule) -> fetch a boolean/flag rule's value
|
||||
* - RuleS(category, rule) -> fetch a string rule's value
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -35,6 +36,8 @@
|
||||
RuleManager::Instance()->GetRealRule( RuleManager::Real__##rule_name )
|
||||
#define RuleB(category_name, rule_name) \
|
||||
RuleManager::Instance()->GetBoolRule( RuleManager::Bool__##rule_name )
|
||||
#define RuleS(category_name, rule_name) \
|
||||
RuleManager::Instance()->GetStringRule( RuleManager::String__##rule_name )
|
||||
|
||||
|
||||
#include <vector>
|
||||
@@ -81,6 +84,17 @@ public:
|
||||
|
||||
static const int BoolRuleCount = static_cast<int>(_BoolRuleCount);
|
||||
|
||||
typedef enum {
|
||||
#define RULE_STRING(category_name, rule_name, default_value, notes) \
|
||||
String__##rule_name,
|
||||
|
||||
#include "ruletypes.h"
|
||||
|
||||
_StringRuleCount
|
||||
} StringType;
|
||||
|
||||
static const int StringRuleCount = static_cast<int>(_StringRuleCount);
|
||||
|
||||
typedef enum {
|
||||
#define RULE_CATEGORY(category_name) \
|
||||
Category__##category_name,
|
||||
@@ -99,45 +113,49 @@ public:
|
||||
static const IntType InvalidInt = _IntRuleCount;
|
||||
static const RealType InvalidReal = _RealRuleCount;
|
||||
static const BoolType InvalidBool = _BoolRuleCount;
|
||||
static const StringType InvalidString = _StringRuleCount;
|
||||
static const CategoryType InvalidCategory = _CatCount;
|
||||
|
||||
static const uint32 RulesCount = IntRuleCount + RealRuleCount + BoolRuleCount;
|
||||
static const uint32 RulesCount = IntRuleCount + RealRuleCount + BoolRuleCount + StringRuleCount;
|
||||
|
||||
//fetch routines, you should generally use the Rule* macros instead of this
|
||||
int GetIntRule(IntType t) const;
|
||||
float GetRealRule(RealType t) const;
|
||||
bool GetBoolRule(BoolType t) const;
|
||||
std::string GetStringRule(StringType t) const;
|
||||
|
||||
//management routines
|
||||
static std::string GetRuleName(IntType t) { return s_RuleInfo[static_cast<int>(t)].name; }
|
||||
static std::string GetRuleName(RealType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount].name; }
|
||||
static std::string GetRuleName(BoolType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount + RealRuleCount].name; }
|
||||
static const std::string &GetRuleNotes(IntType t) { return s_RuleInfo[static_cast<int>(t)].notes; }
|
||||
static const std::string &GetRuleNotes(RealType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount].notes; }
|
||||
static const std::string &GetRuleNotes(BoolType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount + RealRuleCount].notes; }
|
||||
static std::string GetRuleName(StringType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount + RealRuleCount + StringRuleCount].name; }
|
||||
static const std::string& GetRuleNotes(IntType t) { return s_RuleInfo[static_cast<int>(t)].notes; }
|
||||
static const std::string& GetRuleNotes(RealType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount].notes; }
|
||||
static const std::string& GetRuleNotes(BoolType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount + RealRuleCount].notes; }
|
||||
static const std::string& GetRuleNotes(StringType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount + RealRuleCount + StringRuleCount].notes; }
|
||||
static uint32 CountRules() { return RulesCount; }
|
||||
static CategoryType FindCategory(const std::string &category_name);
|
||||
bool ListRules(const std::string &category_name, std::vector <std::string> &l);
|
||||
bool ListCategories(std::vector <std::string> &l);
|
||||
bool GetRule(const std::string &rule_name, std::string &rule_value);
|
||||
static CategoryType FindCategory(const std::string& category_name);
|
||||
bool ListRules(const std::string& category_name, std::vector<std::string>& l);
|
||||
bool ListCategories(std::vector<std::string>& l);
|
||||
bool GetRule(const std::string& rule_name, std::string& rule_value);
|
||||
bool SetRule(
|
||||
const std::string &rule_name,
|
||||
const std::string &rule_value,
|
||||
Database *db = nullptr,
|
||||
const std::string& rule_name,
|
||||
const std::string& rule_value,
|
||||
Database* db = nullptr,
|
||||
bool db_save = false,
|
||||
bool reload = false
|
||||
);
|
||||
|
||||
int GetActiveRulesetID() const { return m_activeRuleset; }
|
||||
std::string GetActiveRuleset() const { return m_activeName; }
|
||||
static bool ListRulesets(Database *db, std::map<int, std::string> &l);
|
||||
static bool ListRulesets(Database* db, std::map<int, std::string>& l);
|
||||
|
||||
void ResetRules(bool reload = false);
|
||||
bool LoadRules(Database *db, const std::string &rule_set_name, bool reload = false);
|
||||
void SaveRules(Database *db, const std::string &rule_set_name);
|
||||
bool UpdateInjectedRules(Database *db, const std::string &rule_set_name, bool quiet_update = false);
|
||||
bool UpdateOrphanedRules(Database *db, bool quiet_update = false);
|
||||
bool RestoreRuleNotes(Database *db);
|
||||
bool LoadRules(Database* db, const std::string& rule_set_name, bool reload = false);
|
||||
void SaveRules(Database* db, const std::string& rule_set_name);
|
||||
bool UpdateInjectedRules(Database* db, const std::string& rule_set_name, bool quiet_update = false);
|
||||
bool UpdateOrphanedRules(Database* db, bool quiet_update = false);
|
||||
bool RestoreRuleNotes(Database* db);
|
||||
|
||||
private:
|
||||
RuleManager();
|
||||
@@ -147,21 +165,23 @@ private:
|
||||
int m_activeRuleset;
|
||||
std::string m_activeName;
|
||||
|
||||
int m_RuleIntValues[IntRuleCount];
|
||||
float m_RuleRealValues[RealRuleCount];
|
||||
uint32 m_RuleBoolValues[BoolRuleCount];
|
||||
int m_RuleIntValues[IntRuleCount];
|
||||
float m_RuleRealValues[RealRuleCount];
|
||||
uint32 m_RuleBoolValues[BoolRuleCount];
|
||||
std::string m_RuleStringValues[StringRuleCount];
|
||||
|
||||
typedef enum {
|
||||
IntRule,
|
||||
RealRule,
|
||||
BoolRule
|
||||
BoolRule,
|
||||
StringRule
|
||||
} RuleType;
|
||||
|
||||
static bool _FindRule(const std::string &rule_name, RuleType &type_into, uint16 &index_into);
|
||||
static bool _FindRule(const std::string& rule_name, RuleType& type_into, uint16& index_into);
|
||||
static std::string _GetRuleName(RuleType type, uint16 index);
|
||||
static const std::string &_GetRuleNotes(RuleType type, uint16 index);
|
||||
static int _FindOrCreateRuleset(Database *db, const std::string &rule_set_name);
|
||||
void _SaveRule(Database *db, RuleType type, uint16 index);
|
||||
static const std::string& _GetRuleNotes(RuleType type, uint16 index);
|
||||
static int _FindOrCreateRuleset(Database* db, const std::string& rule_set_name);
|
||||
void _SaveRule(Database* db, RuleType type, uint16 index);
|
||||
|
||||
static const char* s_categoryNames[];
|
||||
typedef struct {
|
||||
|
||||
+9
-3
@@ -28,6 +28,9 @@
|
||||
#ifndef RULE_BOOL
|
||||
#define RULE_BOOL(cat, rule, default_value, notes)
|
||||
#endif
|
||||
#ifndef RULE_STRING
|
||||
#define RULE_STRING(cat, rule, default_value, notes)
|
||||
#endif
|
||||
#ifndef RULE_CATEGORY_END
|
||||
#define RULE_CATEGORY_END()
|
||||
#endif
|
||||
@@ -48,10 +51,10 @@ RULE_BOOL(Character, DeathKeepLevel, false, "Players can not drop below 0% exper
|
||||
RULE_BOOL(Character, UseDeathExpLossMult, false, "Setting to control whether DeathExpLossMultiplier or the code default is used: (Level x Level / 18.0) x 12000")
|
||||
RULE_BOOL(Character, UseOldRaceRezEffects, false, "Older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore")
|
||||
RULE_INT(Character, CorpseDecayTime, 604800000, "Time after which the corpse decays (milliseconds) DEFAULT: 604800000 (7 Days)")
|
||||
RULE_INT( Character, EmptyCorpseDecayTime, 10800000, "Time after which an empty corpse decays (milliseconds) DEFAULT: 10800000 (3 Hours)")
|
||||
RULE_INT(Character, EmptyCorpseDecayTime, 10800000, "Time after which an empty corpse decays (milliseconds) DEFAULT: 10800000 (3 Hours)")
|
||||
RULE_INT(Character, CorpseResTime, 10800000, "Time after which the corpse can no longer be resurrected (milliseconds) DEFAULT: 10800000 (3 Hours)")
|
||||
RULE_INT( Character, DuelCorpseResTime, 600000, "Time before cant res corpse after a duel (milliseconds) DEFAULT: 600000 (10 Minutes)")
|
||||
RULE_INT( Character, CorpseOwnerOnlineTime, 30000, "How often corpse will check if its owner is online DEFAULT: 30000 (30 Seconds)")
|
||||
RULE_INT(Character, DuelCorpseResTime, 600000, "Time before cant res corpse after a duel (milliseconds) DEFAULT: 600000 (10 Minutes)")
|
||||
RULE_INT(Character, CorpseOwnerOnlineTime, 30000, "How often corpse will check if its owner is online DEFAULT: 30000 (30 Seconds)")
|
||||
RULE_BOOL(Character, LeaveCorpses, true, "Setting whether you leave a corpse behind")
|
||||
RULE_BOOL(Character, LeaveNakedCorpses, false, "Setting whether you leave a corpse without items")
|
||||
RULE_INT(Character, MaxDraggedCorpses, 2, "Maximum number of corpses you can drag at once")
|
||||
@@ -324,6 +327,8 @@ RULE_INT(World, MaximumQuestErrors, 30, "Changes the maximum number of quest err
|
||||
RULE_INT(World, BootHour, 0, "Sets the in-game hour world will set when it first boots. 0-24 are valid options, where 0 disables this rule")
|
||||
RULE_BOOL(World, UseItemLinksForKeyRing, false, "Uses item links for Key Ring Listing instead of item name")
|
||||
RULE_BOOL(World, UseOldShadowKnightClassExport, true, "Disable to have Shadowknight show as Shadow Knight (live-like)")
|
||||
RULE_STRING(World, IPExemptionZones, "", "Comma-delimited list of zones to exclude from IP-limit checks. Empty string to disable.")
|
||||
RULE_STRING(World, MOTD, "", "Server MOTD sent on login, change from empty to have this be used instead of variables table 'motd' value")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Zone)
|
||||
@@ -951,4 +956,5 @@ RULE_CATEGORY_END()
|
||||
#undef RULE_INT
|
||||
#undef RULE_REAL
|
||||
#undef RULE_BOOL
|
||||
#undef RULE_STRING
|
||||
#undef RULE_CATEGORY_END
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
|
||||
// Build variables
|
||||
// these get injected during the build pipeline
|
||||
#define CURRENT_VERSION "22.45.0-dev" // always append -dev to the current version for custom-builds
|
||||
#define CURRENT_VERSION "22.46.0-dev" // always append -dev to the current version for custom-builds
|
||||
#define LOGIN_VERSION "0.8.0"
|
||||
#define COMPILE_DATE __DATE__
|
||||
#define COMPILE_TIME __TIME__
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eqemu-server",
|
||||
"version": "22.45.0",
|
||||
"version": "22.46.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EQEmu/Server.git"
|
||||
|
||||
+72
-70
@@ -52,6 +52,7 @@
|
||||
#include "../common/repositories/player_event_logs_repository.h"
|
||||
#include "../common/repositories/inventory_repository.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "../common/content/world_content_service.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
@@ -599,79 +600,63 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app)
|
||||
}
|
||||
|
||||
bool Client::HandleGenerateRandomNamePacket(const EQApplicationPacket *app) {
|
||||
// creates up to a 10 char name
|
||||
char vowels[18]="aeiouyaeiouaeioe";
|
||||
char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd";
|
||||
char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||
char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr";
|
||||
int rndnum=emu_random.Int(0, 75),n=1;
|
||||
bool dlc=false;
|
||||
bool vwl=false;
|
||||
bool dbl=false;
|
||||
if (rndnum>63)
|
||||
{ // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel
|
||||
rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk"
|
||||
rndname[0]=paircons[rndnum];
|
||||
rndname[1]=paircons[rndnum+1];
|
||||
n=2;
|
||||
}
|
||||
else if (rndnum>16)
|
||||
{
|
||||
rndnum-=17;
|
||||
rndname[0]=cons[rndnum];
|
||||
}
|
||||
else
|
||||
{
|
||||
rndname[0]=vowels[rndnum];
|
||||
vwl=true;
|
||||
}
|
||||
int namlen=emu_random.Int(5, 10);
|
||||
for (int i=n;i<namlen;i++)
|
||||
{
|
||||
dlc=false;
|
||||
if (vwl) //last char was a vowel
|
||||
{ // so pick a cons or cons pair
|
||||
rndnum=emu_random.Int(0, 62);
|
||||
if (rndnum>46)
|
||||
{ // pick a cons pair
|
||||
if (i>namlen-3) // last 2 chars in name?
|
||||
{ // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng"
|
||||
rndnum=emu_random.Int(0, 7)*2;
|
||||
}
|
||||
else
|
||||
{ // pick any from the set
|
||||
rndnum=(rndnum-47)*2;
|
||||
}
|
||||
rndname[i]=paircons[rndnum];
|
||||
rndname[i+1]=paircons[rndnum+1];
|
||||
dlc=true; // flag keeps second letter from being doubled below
|
||||
i+=1;
|
||||
}
|
||||
else
|
||||
{ // select a single cons
|
||||
rndname[i]=cons[rndnum];
|
||||
char newName[17] = {0};
|
||||
bool unique = false;
|
||||
|
||||
while (!unique) {
|
||||
std::string cons = "bcdfghjklmnpqrstvwxyz";
|
||||
std::string vows = "aeou";
|
||||
std::string allVows = "aeiou";
|
||||
std::vector<std::string> endPhon = {"a", "e", "i", "o", "u", "os", "as", "us", "is", "y", "an", "en", "in", "on", "un"};
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
|
||||
std::uniform_int_distribution<int> lenDist(5, 10);
|
||||
std::uniform_int_distribution<int> firstCharDist(0, 1);
|
||||
std::uniform_int_distribution<int> consDist(0, cons.size() - 1);
|
||||
std::uniform_int_distribution<int> vowDist(0, vows.size() - 1);
|
||||
std::uniform_int_distribution<int> allVowDist(0, allVows.size() - 1);
|
||||
std::uniform_int_distribution<int> endPhonDist(0, endPhon.size() - 1);
|
||||
|
||||
int len = 0;
|
||||
memset(newName, 0, sizeof(newName));
|
||||
|
||||
if (firstCharDist(gen) == 0) {
|
||||
newName[len++] = vows[vowDist(gen)];
|
||||
newName[len++] = cons[consDist(gen)];
|
||||
} else {
|
||||
newName[len++] = cons[consDist(gen)];
|
||||
newName[len++] = allVows[allVowDist(gen)];
|
||||
}
|
||||
|
||||
newName[0] = toupper(newName[0]);
|
||||
|
||||
while (len < lenDist(gen) - 1) {
|
||||
if (len % 2 == 0) {
|
||||
newName[len++] = cons[consDist(gen)];
|
||||
} else {
|
||||
newName[len++] = allVows[allVowDist(gen)];
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // select a vowel
|
||||
rndname[i]=vowels[emu_random.Int(0, 16)];
|
||||
|
||||
std::string end = endPhon[endPhonDist(gen)];
|
||||
for (char c : end) {
|
||||
if (len < 10) newName[len++] = c;
|
||||
}
|
||||
vwl=!vwl;
|
||||
if (!dbl && !dlc)
|
||||
{ // one chance at double letters in name
|
||||
if (!emu_random.Int(0, i+9)) // chances decrease towards end of name
|
||||
{
|
||||
rndname[i+1]=rndname[i];
|
||||
dbl=true;
|
||||
i+=1;
|
||||
|
||||
if (database.CheckNameFilter(newName)) {
|
||||
std::string query = StringFormat("SELECT `name` FROM `character_data` WHERE `name` = '%s'", newName);
|
||||
auto res = database.QueryDatabase(query);
|
||||
if (res.Success() && res.RowCount() == 0) {
|
||||
unique = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rndname[0]=toupper(rndname[0]);
|
||||
NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer;
|
||||
memset(ngs->name,0,64);
|
||||
strcpy(ngs->name,rndname);
|
||||
memset(ngs->name, 0, 64);
|
||||
strcpy(ngs->name, newName);
|
||||
|
||||
QueuePacket(app);
|
||||
return true;
|
||||
@@ -787,6 +772,18 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto r = content_service.FindZone(zone_id, instance_id);
|
||||
if (r.zone_id && r.instance.id != instance_id) {
|
||||
LogInfo(
|
||||
"Zone [{}] has been remapped to instance_id [{}] from instance_id [{}] for client [{}]",
|
||||
r.zone.short_name,
|
||||
r.instance.id,
|
||||
instance_id,
|
||||
char_name
|
||||
);
|
||||
instance_id = r.instance.id;
|
||||
}
|
||||
|
||||
// Make sure this account owns this character
|
||||
if (temporary_account_id != account_id) {
|
||||
LogInfo("Account [{}] does not own the character named [{}] from account [{}]", account_id, char_name, temporary_account_id);
|
||||
@@ -903,14 +900,19 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_MOTD);
|
||||
std::string motd_message;
|
||||
if (database.GetVariable("MOTD", motd_message)) {
|
||||
outapp->size = motd_message.length() + 1;
|
||||
std::string motd = RuleS(World, MOTD);
|
||||
if (!motd.empty()) {
|
||||
outapp->size = motd.length() + 1;
|
||||
outapp->pBuffer = new uchar[outapp->size];
|
||||
memset(outapp->pBuffer, 0, outapp->size);
|
||||
strcpy((char*)outapp->pBuffer, motd_message.c_str());
|
||||
strcpy((char*) outapp->pBuffer, motd.c_str());
|
||||
} else if (database.GetVariable("MOTD", motd)) {
|
||||
outapp->size = motd.length() + 1;
|
||||
outapp->pBuffer = new uchar[outapp->size];
|
||||
memset(outapp->pBuffer, 0, outapp->size);
|
||||
strcpy((char*) outapp->pBuffer, motd.c_str());
|
||||
} else { // Null Message of the Day. :)
|
||||
outapp->size = 1;
|
||||
outapp->size = 1;
|
||||
outapp->pBuffer = new uchar[outapp->size];
|
||||
outapp->pBuffer[0] = 0;
|
||||
}
|
||||
|
||||
@@ -96,6 +96,15 @@ void ClientList::GetCLEIP(uint32 in_ip) {
|
||||
|
||||
while (iterator.MoreElements()) {
|
||||
cle = iterator.GetData();
|
||||
|
||||
const auto zones = Strings::Split(RuleS(World, IPExemptionZones), ",");
|
||||
for (const auto &z : zones) {
|
||||
if (Strings::ToUnsignedInt(z) == cle->zone()) {
|
||||
iterator.Advance();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
cle->GetIP() == in_ip &&
|
||||
(
|
||||
|
||||
@@ -142,6 +142,7 @@ std::vector<Reload> reload_types = {
|
||||
Reload{.command = "base_data", .opcode = ServerOP_ReloadBaseData, .desc = "Base Data"},
|
||||
Reload{.command = "blocked_spells", .opcode = ServerOP_ReloadBlockedSpells, .desc = "Blocked Spells"},
|
||||
Reload{.command = "commands", .opcode = ServerOP_ReloadCommands, .desc = "Commands"},
|
||||
Reload{.command = "content_flags", .opcode = ServerOP_ReloadContentFlags, .desc = "Content Flags"},
|
||||
Reload{.command = "data_buckets_cache", .opcode = ServerOP_ReloadDataBucketsCache, .desc = "Data Buckets Cache"},
|
||||
Reload{.command = "doors", .opcode = ServerOP_ReloadDoors, .desc = "Doors"},
|
||||
Reload{.command = "dztemplates", .opcode = ServerOP_ReloadDzTemplates, .desc = "Dynamic Zone Templates"},
|
||||
|
||||
@@ -188,6 +188,11 @@ int main(int argc, char **argv)
|
||||
RegisterConsoleFunctions(console);
|
||||
}
|
||||
|
||||
content_service.SetDatabase(&database)
|
||||
->SetContentDatabase(&content_db)
|
||||
->SetExpansionContext()
|
||||
->ReloadContentFlags();
|
||||
|
||||
std::unique_ptr<EQ::Net::ServertalkServer> server_connection;
|
||||
server_connection = std::make_unique<EQ::Net::ServertalkServer>();
|
||||
|
||||
|
||||
+11
-1
@@ -230,13 +230,23 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
||||
case ServerOP_GuildChannel:
|
||||
case ServerOP_GuildURL:
|
||||
case ServerOP_GuildMemberRemove:
|
||||
case ServerOP_GuildMemberAdd:
|
||||
case ServerOP_GuildSendGuildList:
|
||||
case ServerOP_GuildMembersList:
|
||||
{
|
||||
zoneserver_list.SendPacketToBootedZones(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_GuildMemberAdd:
|
||||
{
|
||||
auto in = (ServerOP_GuildMessage_Struct *)pack->pBuffer;
|
||||
auto guild = GetGuildByGuildID(in->guild_id);
|
||||
if (!guild) {
|
||||
BaseGuildManager::RefreshGuild(in->guild_id);
|
||||
}
|
||||
|
||||
zoneserver_list.SendPacketToBootedZones(pack);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogGuilds("Unknown packet {:#04x} received from zone??", pack->opcode);
|
||||
break;
|
||||
|
||||
@@ -1100,7 +1100,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
}
|
||||
|
||||
auto smotd = (ServerMotd_Struct*) pack->pBuffer;
|
||||
database.SetVariable("MOTD", smotd->motd);
|
||||
RuleManager::Instance()->SetRule("MOTD", smotd->motd, &database, true, true);
|
||||
zoneserver_list.SendPacket(pack);
|
||||
break;
|
||||
}
|
||||
|
||||
+25
-15
@@ -621,28 +621,38 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
|
||||
// NPC *npc1, *npc2;
|
||||
int reverse;
|
||||
|
||||
if(!zone->CanDoCombat())
|
||||
return false;
|
||||
|
||||
// some special cases
|
||||
if(!target)
|
||||
return false;
|
||||
|
||||
if(this == target) // you can attack yourself
|
||||
return true;
|
||||
|
||||
if(target->GetSpecialAbility(NO_HARM_FROM_CLIENT)){
|
||||
if (!zone->CanDoCombat()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT) && IsClient())
|
||||
// some special cases
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target->GetSpecialAbility(IMMUNE_DAMAGE_NPC) && IsNPC())
|
||||
return false;
|
||||
if (this == target) { // you can attack yourself
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target->IsHorse())
|
||||
if (target->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsBot() && target->GetSpecialAbility(IMMUNE_DAMAGE_BOT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsClient() && target->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsNPC() && target->GetSpecialAbility(IMMUNE_DAMAGE_NPC)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target->IsHorse()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// can't damage own pet (applies to everthing)
|
||||
Mob *target_owner = target->GetOwner();
|
||||
|
||||
+31
-16
@@ -1005,7 +1005,7 @@ double Mob::RollD20(int offense, int mitigation)
|
||||
auto atk_roll = zone->random.Roll0(offense + 5);
|
||||
auto def_roll = zone->random.Roll0(mitigation + 5);
|
||||
|
||||
int avg = (offense + mitigation + 10) / 2;
|
||||
int avg = std::max(1, (offense + mitigation + 10) / 2);
|
||||
int index = std::max(0, (atk_roll - def_roll) + (avg / 2));
|
||||
|
||||
index = EQ::Clamp((index * 20) / avg, 0, 19);
|
||||
@@ -3091,26 +3091,37 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
TryTriggerOnCastRequirement();
|
||||
}
|
||||
|
||||
if (IsClient() && !IsAIControlled())
|
||||
if (IsClient() && !IsAIControlled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO))
|
||||
if (IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSpecialAbility(IMMUNE_AGGRO_NPC) && other->IsNPC())
|
||||
if (other->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && other->IsClient())
|
||||
if (other->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsValidSpell(spell_id) && IsNoDetrimentalSpellAggroSpell(spell_id))
|
||||
if (other->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (other == myowner)
|
||||
if (IsValidSpell(spell_id) && IsNoDetrimentalSpellAggroSpell(spell_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->GetSpecialAbility(IMMUNE_AGGRO_ON))
|
||||
if (other == myowner) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->GetSpecialAbility(IMMUNE_AGGRO_ON)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSpecialAbility(NPC_TUNNELVISION)) {
|
||||
int tv_mod = GetSpecialAbilityParam(NPC_TUNNELVISION, 0);
|
||||
@@ -3194,8 +3205,9 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
// owner must get on list, but he's not actually gained any hate yet
|
||||
if (
|
||||
!owner->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
!(GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && owner->IsClient()) &&
|
||||
!(GetSpecialAbility(IMMUNE_AGGRO_NPC) && owner->IsNPC())
|
||||
!(owner->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
|
||||
!(owner->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
|
||||
!(owner->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC))
|
||||
) {
|
||||
if (owner->IsClient() && !CheckAggro(owner)) {
|
||||
owner->CastToClient()->AddAutoXTarget(this);
|
||||
@@ -3209,8 +3221,9 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
if (
|
||||
!mypet->IsFamiliar() &&
|
||||
!mypet->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
!(mypet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && IsClient()) &&
|
||||
!(mypet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && IsNPC())
|
||||
!(IsBot() && mypet->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
|
||||
!(IsClient() && mypet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
|
||||
!(IsNPC() && mypet->GetSpecialAbility(IMMUNE_AGGRO_NPC))
|
||||
) {
|
||||
mypet->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
|
||||
}
|
||||
@@ -3219,8 +3232,9 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
if (
|
||||
myowner->IsAIControlled() &&
|
||||
!myowner->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
!(GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && myowner->IsClient()) &&
|
||||
!(GetSpecialAbility(IMMUNE_AGGRO_NPC) && myowner->IsNPC())
|
||||
!(myowner->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
|
||||
!(myowner->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
|
||||
!(myowner->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC))
|
||||
) {
|
||||
myowner->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
|
||||
}
|
||||
@@ -4060,8 +4074,9 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
!pet->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
!pet->IsEngaged() &&
|
||||
attacker &&
|
||||
!(pet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) &&
|
||||
!(pet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) &&
|
||||
!(attacker->IsBot() && pet->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
|
||||
!(attacker->IsClient() && pet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
|
||||
!(attacker->IsNPC() && pet->GetSpecialAbility(IMMUNE_AGGRO_NPC)) &&
|
||||
attacker != this &&
|
||||
!attacker->IsCorpse() &&
|
||||
!pet->IsGHeld() &&
|
||||
|
||||
@@ -2367,7 +2367,7 @@ const uint8 BotDatabase::GetBotLevelByID(const uint32 bot_id)
|
||||
return e.bot_id ? e.level : 0;
|
||||
}
|
||||
|
||||
const std::string& BotDatabase::GetBotNameByID(const uint32 bot_id)
|
||||
const std::string BotDatabase::GetBotNameByID(const uint32 bot_id)
|
||||
{
|
||||
const auto& e = BotDataRepository::FindOne(database, bot_id);
|
||||
|
||||
|
||||
+1
-1
@@ -164,7 +164,7 @@ public:
|
||||
const uint8 GetBotGenderByID(const uint32 bot_id);
|
||||
std::vector<uint32> GetBotIDsByCharacterID(const uint32 character_id, uint8 class_id = Class::None);
|
||||
const uint8 GetBotLevelByID(const uint32 bot_id);
|
||||
const std::string& GetBotNameByID(const uint32 bot_id);
|
||||
const std::string GetBotNameByID(const uint32 bot_id);
|
||||
const uint16 GetBotRaceByID(const uint32 bot_id);
|
||||
|
||||
class fail {
|
||||
|
||||
+42
-11
@@ -284,6 +284,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
PendingSacrifice = false;
|
||||
controlling_boat_id = 0;
|
||||
controlled_mob_id = 0;
|
||||
qGlobals = nullptr;
|
||||
|
||||
if (!RuleB(Character, PerCharacterQglobalMaxLevel) && !RuleB(Character, PerCharacterBucketMaxLevel)) {
|
||||
SetClientMaxLevel(0);
|
||||
@@ -311,7 +312,6 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
aa_los_them_mob = nullptr;
|
||||
los_status = false;
|
||||
los_status_facing = false;
|
||||
qGlobals = nullptr;
|
||||
HideCorpseMode = HideCorpseNone;
|
||||
PendingGuildInvitation = false;
|
||||
|
||||
@@ -6285,7 +6285,17 @@ void Client::SendZonePoints()
|
||||
zp->zpe[i].z = data->target_z;
|
||||
zp->zpe[i].heading = data->target_heading;
|
||||
zp->zpe[i].zoneid = data->target_zone_id;
|
||||
zp->zpe[i].zoneinstance = data->target_zone_instance;
|
||||
|
||||
// if the target zone is the same as the current zone, use the instance of the current zone
|
||||
// if we don't use the same instance_id that the client was sent, the client will forcefully
|
||||
// issue a zone change request when they should be simply moving to a different point in the same zone
|
||||
// because the client will think the zone point target is different from the current instance
|
||||
auto target_instance = data->target_zone_instance;
|
||||
if (data->target_zone_id == zone->GetZoneID() && data->target_zone_instance == 0) {
|
||||
target_instance = zone->GetInstanceID();
|
||||
}
|
||||
|
||||
zp->zpe[i].zoneinstance = target_instance;
|
||||
i++;
|
||||
}
|
||||
iterator.Advance();
|
||||
@@ -6765,23 +6775,36 @@ void Client::UpdateClientXTarget(Client *c)
|
||||
// IT IS NOT SAFE TO CALL THIS IF IT'S NOT INITIAL AGGRO
|
||||
void Client::AddAutoXTarget(Mob *m, bool send)
|
||||
{
|
||||
if (m->IsBot() || (m->IsPet() && m->IsPetOwnerBot())) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_activeautohatermgr->increment_count(m);
|
||||
|
||||
if (!XTargettingAvailable() || !XTargetAutoAddHaters || IsXTarget(m))
|
||||
if (!XTargettingAvailable() || !XTargetAutoAddHaters || IsXTarget(m)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0; i < GetMaxXTargets(); ++i)
|
||||
{
|
||||
if((XTargets[i].Type == Auto) && (XTargets[i].ID == 0))
|
||||
{
|
||||
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
||||
if (XTargets[i].Type == Auto && XTargets[i].ID == 0) {
|
||||
XTargets[i].ID = m->GetID();
|
||||
if (send) // if we don't send we're bulk sending updates later on
|
||||
|
||||
if (send) { // if we don't send we're bulk sending updates later on
|
||||
SendXTargetPacket(i, m);
|
||||
else
|
||||
} else {
|
||||
XTargets[i].dirty = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LogXTargets(
|
||||
"Adding [{}] to [{}] ({}) XTargets",
|
||||
m->GetCleanName(),
|
||||
GetCleanName(),
|
||||
GetID()
|
||||
);
|
||||
}
|
||||
|
||||
void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots)
|
||||
@@ -6790,15 +6813,23 @@ void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots)
|
||||
// now we may need to clean up our CurrentTargetNPC entries
|
||||
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
||||
if (XTargets[i].Type == CurrentTargetNPC && XTargets[i].ID == m->GetID()) {
|
||||
XTargets[i].Type = Auto;
|
||||
XTargets[i].ID = 0;
|
||||
XTargets[i].Type = Auto;
|
||||
XTargets[i].ID = 0;
|
||||
XTargets[i].dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto r = GetRaid();
|
||||
if (r) {
|
||||
r->UpdateRaidXTargets();
|
||||
}
|
||||
|
||||
LogXTargets(
|
||||
"Removing [{}] from [{}] ({}) XTargets",
|
||||
m->GetCleanName(),
|
||||
GetCleanName(),
|
||||
GetID()
|
||||
);
|
||||
}
|
||||
|
||||
void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name)
|
||||
|
||||
+1
-1
@@ -1090,7 +1090,7 @@ public:
|
||||
void SetPEQZoneFlag(uint32 zone_id);
|
||||
|
||||
bool CanFish();
|
||||
void GoFish();
|
||||
void GoFish(bool guarantee = false, bool use_bait = true);
|
||||
void ForageItem(bool guarantee = false);
|
||||
//Calculate vendor price modifier based on CHA: (reverse==selling)
|
||||
float CalcPriceMod(Mob* other = 0, bool reverse = false);
|
||||
|
||||
@@ -131,8 +131,10 @@ int command_init(void)
|
||||
command_add("feature", "Change your or your target's feature's temporarily", AccountStatus::QuestTroupe, command_feature) ||
|
||||
command_add("size", "Change your targets size (alias of #feature size)", AccountStatus::QuestTroupe, command_feature) ||
|
||||
command_add("find", "Search command used to find various things", AccountStatus::Guide, command_find) ||
|
||||
command_add("fish", "Fish for an item", AccountStatus::QuestTroupe, command_fish) ||
|
||||
command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", AccountStatus::QuestTroupe, command_fixmob) ||
|
||||
command_add("flagedit", "Edit zone flags on your target. Use #flagedit help for more info.", AccountStatus::GMAdmin, command_flagedit) ||
|
||||
command_add("forage", "Forage an item", AccountStatus::QuestTroupe, command_forage) ||
|
||||
command_add("gearup", "Developer tool to quickly equip yourself or your target", AccountStatus::GMMgmt, command_gearup) ||
|
||||
command_add("giveitem", "[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.", AccountStatus::GMMgmt, command_giveitem) ||
|
||||
command_add("givemoney", "[Platinum] [Gold] [Silver] [Copper] - Gives specified amount of money to you or your player target", AccountStatus::GMMgmt, command_givemoney) ||
|
||||
@@ -822,8 +824,10 @@ void command_bot(Client *c, const Seperator *sep)
|
||||
#include "gm_commands/faction.cpp"
|
||||
#include "gm_commands/feature.cpp"
|
||||
#include "gm_commands/find.cpp"
|
||||
#include "gm_commands/fish.cpp"
|
||||
#include "gm_commands/fixmob.cpp"
|
||||
#include "gm_commands/flagedit.cpp"
|
||||
#include "gm_commands/forage.cpp"
|
||||
#include "gm_commands/gearup.cpp"
|
||||
#include "gm_commands/giveitem.cpp"
|
||||
#include "gm_commands/givemoney.cpp"
|
||||
|
||||
@@ -83,8 +83,10 @@ void command_faction(Client *c, const Seperator *sep);
|
||||
void command_faction_association(Client *c, const Seperator *sep);
|
||||
void command_feature(Client *c, const Seperator *sep);
|
||||
void command_find(Client *c, const Seperator *sep);
|
||||
void command_fish(Client* c, const Seperator* sep);
|
||||
void command_fixmob(Client *c, const Seperator *sep);
|
||||
void command_flagedit(Client *c, const Seperator *sep);
|
||||
void command_forage(Client* c, const Seperator* sep);
|
||||
void command_gearup(Client *c, const Seperator *sep);
|
||||
void command_giveitem(Client *c, const Seperator *sep);
|
||||
void command_givemoney(Client *c, const Seperator *sep);
|
||||
|
||||
@@ -73,6 +73,15 @@ Doors::Doors(const DoorsRepository::Doors &door) :
|
||||
m_door_param = door.door_param;
|
||||
m_size = door.size;
|
||||
m_invert_state = door.invert_state;
|
||||
|
||||
// if the target zone is the same as the current zone, use the instance of the current zone
|
||||
// if we don't use the same instance_id that the client was sent, the client will forcefully
|
||||
// issue a zone change request when they should be simply moving to a different point in the same zone
|
||||
// because the client will think the zone point target is different from the current instance
|
||||
if (door.dest_zone == zone->GetShortName() && m_destination_instance_id == 0) {
|
||||
m_destination_instance_id = zone->GetInstanceID();
|
||||
}
|
||||
|
||||
m_destination_instance_id = door.dest_instance;
|
||||
m_is_ldon_door = door.is_ldon_door;
|
||||
m_dz_switch_id = door.dz_switch_id;
|
||||
|
||||
+6
-4
@@ -4379,8 +4379,9 @@ void EntityList::AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy)
|
||||
if (n->GetSwarmInfo()->owner_id == owner->GetID()) {
|
||||
if (
|
||||
!n->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
!(n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && other->IsClient()) &&
|
||||
!(n->GetSpecialAbility(IMMUNE_AGGRO_NPC) && other->IsNPC())
|
||||
!(other->IsBot() && n->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
|
||||
!(other->IsClient() && n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
|
||||
!(other->IsNPC() && n->GetSpecialAbility(IMMUNE_AGGRO_NPC))
|
||||
) {
|
||||
n->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
|
||||
}
|
||||
@@ -4405,8 +4406,9 @@ void EntityList::AddTempPetsToHateListOnOwnerDamage(Mob *owner, Mob* attacker, i
|
||||
attacker != n &&
|
||||
!n->IsEngaged() &&
|
||||
!n->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
!(n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) &&
|
||||
!(n->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) &&
|
||||
!(attacker->IsBot() && n->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
|
||||
!(attacker->IsClient() && n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
|
||||
!(attacker->IsNPC() && n->GetSpecialAbility(IMMUNE_AGGRO_NPC)) &&
|
||||
!attacker->IsTrap() &&
|
||||
!attacker->IsCorpse()
|
||||
) {
|
||||
|
||||
+7
-5
@@ -89,7 +89,7 @@ uint32 ZoneDatabase::LoadForage(uint32 zone_id, uint8 skill_level)
|
||||
}
|
||||
|
||||
forage_items[count] = e.Itemid;
|
||||
forage_chances[count] = e.chance;
|
||||
forage_chances[count] = e.chance + current_chance;
|
||||
|
||||
current_chance = forage_chances[count];
|
||||
|
||||
@@ -257,7 +257,7 @@ bool Client::CanFish() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::GoFish()
|
||||
void Client::GoFish(bool guarantee, bool use_bait)
|
||||
{
|
||||
|
||||
//TODO: generate a message if we're already fishing
|
||||
@@ -306,7 +306,7 @@ void Client::GoFish()
|
||||
fishing_skill = 100+((fishing_skill-100)/2);
|
||||
}
|
||||
|
||||
if (zone->random.Int(0,175) < fishing_skill) {
|
||||
if (guarantee || zone->random.Int(0,175) < fishing_skill) {
|
||||
uint32 food_id = 0;
|
||||
|
||||
//25% chance to fish an item.
|
||||
@@ -343,8 +343,10 @@ void Client::GoFish()
|
||||
}
|
||||
}
|
||||
|
||||
//consume bait, should we always consume bait on success?
|
||||
DeleteItemInInventory(bslot, 1, true); //do we need client update?
|
||||
if (use_bait) {
|
||||
//consume bait, should we always consume bait on success?
|
||||
DeleteItemInInventory(bslot, 1, true); //do we need client update?
|
||||
}
|
||||
|
||||
if(food_id == 0) {
|
||||
int index = zone->random.Int(0, MAX_COMMON_FISH_IDS-1);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "../client.h"
|
||||
|
||||
void command_fish(Client *c, const Seperator *sep)
|
||||
{
|
||||
c->GoFish(true, false);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "../client.h"
|
||||
|
||||
void command_forage(Client *c, const Seperator *sep)
|
||||
{
|
||||
c->ForageItem(true);
|
||||
}
|
||||
+18
-1
@@ -5653,6 +5653,10 @@ bool get_ruleb(int rule) {
|
||||
return RuleManager::Instance()->GetBoolRule((RuleManager::BoolType)rule);
|
||||
}
|
||||
|
||||
std::string get_rules(int rule) {
|
||||
return RuleManager::Instance()->GetStringRule((RuleManager::StringType)rule);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_general() {
|
||||
return luabind::namespace_("eq")
|
||||
[(
|
||||
@@ -7244,7 +7248,13 @@ luabind::scope lua_register_rules_const() {
|
||||
#define RULE_BOOL(cat, rule, default_value, notes) \
|
||||
luabind::value(#rule, RuleManager::Bool__##rule),
|
||||
#include "../common/ruletypes.h"
|
||||
luabind::value("_BoolRuleCount", RuleManager::_BoolRuleCount)
|
||||
luabind::value("_BoolRuleCount", RuleManager::_BoolRuleCount),
|
||||
#undef RULE_BOOL
|
||||
#define RULE_STRING(cat, rule, default_value, notes) \
|
||||
luabind::value(#rule, RuleManager::String__##rule),
|
||||
#include "../common/ruletypes.h"
|
||||
luabind::value("_StringRuleCount", RuleManager::_StringRuleCount)
|
||||
#undef RULE_STRING
|
||||
)];
|
||||
}
|
||||
|
||||
@@ -7269,6 +7279,13 @@ luabind::scope lua_register_ruleb() {
|
||||
];
|
||||
}
|
||||
|
||||
luabind::scope lua_register_rules() {
|
||||
return luabind::namespace_("RuleS")
|
||||
[
|
||||
luabind::def("Get", &get_rules)
|
||||
];
|
||||
}
|
||||
|
||||
luabind::scope lua_register_journal_speakmode() {
|
||||
return luabind::class_<Journal_SpeakMode>("SpeakMode")
|
||||
.enum_("constants")
|
||||
|
||||
+3
-1
@@ -3866,7 +3866,9 @@ luabind::scope lua_register_special_abilities() {
|
||||
luabind::value("modify_avoid_damage", static_cast<int>(MODIFY_AVOID_DAMAGE)),
|
||||
luabind::value("immune_open", static_cast<int>(IMMUNE_OPEN)),
|
||||
luabind::value("immune_assassinate", static_cast<int>(IMMUNE_ASSASSINATE)),
|
||||
luabind::value("immune_headshot", static_cast<int>(IMMUNE_HEADSHOT))
|
||||
luabind::value("immune_headshot", static_cast<int>(IMMUNE_HEADSHOT)),
|
||||
luabind::value("immune_aggro_bot", static_cast<int>(IMMUNE_AGGRO_BOT)),
|
||||
luabind::value("immune_damage_bot", static_cast<int>(IMMUNE_DAMAGE_BOT))
|
||||
)];
|
||||
}
|
||||
|
||||
|
||||
@@ -403,7 +403,6 @@ int main(int argc, char **argv)
|
||||
|
||||
content_service.SetDatabase(&database)
|
||||
->SetContentDatabase(&content_db)
|
||||
->SetContentZones(zone_store.GetZones())
|
||||
->SetExpansionContext()
|
||||
->ReloadContentFlags();
|
||||
|
||||
|
||||
+128
-45
@@ -1220,10 +1220,7 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
||||
UpdateActiveLight();
|
||||
ns->spawn.light = m_Light.Type[EQ::lightsource::LightActive];
|
||||
|
||||
if (IsNPC() && race == ERUDITE)
|
||||
ns->spawn.showhelm = 1;
|
||||
else
|
||||
ns->spawn.showhelm = (helmtexture && helmtexture != 0xFF) ? 1 : 0;
|
||||
ns->spawn.showhelm = helmtexture != std::numeric_limits<uint8>::max() ? 1 : 0;
|
||||
|
||||
ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players
|
||||
ns->spawn.NPC = IsClient() ? 0 : 1;
|
||||
@@ -1272,10 +1269,8 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
||||
|
||||
strn0cpy(ns->spawn.lastName, lastname, sizeof(ns->spawn.lastName));
|
||||
|
||||
//for (i = 0; i < _MaterialCount; i++)
|
||||
for (i = 0; i < 9; i++) {
|
||||
// Only Player Races Wear Armor
|
||||
if (IsPlayerRace(race) || i > 6) {
|
||||
for (i = 0; i < EQ::textures::materialCount; i++) {
|
||||
if (IsPlayerRace(race) || i > EQ::textures::armorFeet) {
|
||||
ns->spawn.equipment.Slot[i].Material = GetEquipmentMaterial(i);
|
||||
ns->spawn.equipment.Slot[i].EliteModel = IsEliteMaterialItem(i);
|
||||
ns->spawn.equipment.Slot[i].HerosForgeModel = GetHerosForgeModel(i);
|
||||
@@ -1283,13 +1278,42 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
||||
}
|
||||
}
|
||||
|
||||
if (texture > 0) {
|
||||
for (i = 0; i < 9; i++) {
|
||||
if (i == EQ::textures::weaponPrimary || i == EQ::textures::weaponSecondary || texture == 255) {
|
||||
continue;
|
||||
}
|
||||
ns->spawn.equipment.Slot[i].Material = texture;
|
||||
for (i = 0; i < EQ::textures::weaponPrimary; i++) {
|
||||
if (texture == std::numeric_limits<uint8>::max()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == EQ::textures::armorHead && helmtexture != texture) {
|
||||
ns->spawn.equipment.Slot[i].Material = helmtexture;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == EQ::textures::armorArms && armtexture != 0) {
|
||||
ns->spawn.equipment.Slot[i].Material = armtexture;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == EQ::textures::armorWrist && bracertexture != 0) {
|
||||
ns->spawn.equipment.Slot[i].Material = bracertexture;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == EQ::textures::armorHands && handtexture != 0) {
|
||||
ns->spawn.equipment.Slot[i].Material = handtexture;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == EQ::textures::armorLegs && legtexture != 0) {
|
||||
ns->spawn.equipment.Slot[i].Material = legtexture;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == EQ::textures::armorFeet && feettexture != 0) {
|
||||
ns->spawn.equipment.Slot[i].Material = feettexture;
|
||||
continue;
|
||||
}
|
||||
|
||||
ns->spawn.equipment.Slot[i].Material = texture;
|
||||
}
|
||||
|
||||
memset(ns->spawn.set_to_0xFF, 0xFF, sizeof(ns->spawn.set_to_0xFF));
|
||||
@@ -4360,17 +4384,15 @@ void Mob::SendWearChangeAndLighting(int8 last_texture) {
|
||||
|
||||
void Mob::ChangeSize(float in_size = 0, bool unrestricted)
|
||||
{
|
||||
size = std::clamp(in_size, 1.0f, 255.0f);
|
||||
|
||||
if (!unrestricted) {
|
||||
if (IsClient() || petid != 0) {
|
||||
EQ::Clamp(in_size, 3.0f, 15.0f);
|
||||
size = std::clamp(in_size, 3.0f, 15.0f);
|
||||
}
|
||||
}
|
||||
|
||||
EQ::Clamp(in_size, 1.0f, 255.0f);
|
||||
|
||||
size = in_size;
|
||||
|
||||
SendAppearancePacket(AppearanceType::Size, static_cast<uint32>(in_size));
|
||||
SendAppearancePacket(AppearanceType::Size, static_cast<uint32>(size));
|
||||
}
|
||||
|
||||
Mob* Mob::GetOwnerOrSelf()
|
||||
@@ -5182,32 +5204,47 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime)
|
||||
|
||||
}
|
||||
|
||||
void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on, int level_override) {
|
||||
void Mob::ExecWeaponProc(const EQ::ItemInstance* inst, uint16 spell_id, Mob* on, int level_override)
|
||||
{
|
||||
// Changed proc targets to look up based on the spells goodEffect flag.
|
||||
// This should work for the majority of weapons.
|
||||
if (!on) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!IsValidSpell(spell_id) || on->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
|
||||
if (!IsValidSpell(spell_id) || on->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
|
||||
//This is so 65535 doesn't get passed to the client message and to logs because it is not relavant information for debugging.
|
||||
return;
|
||||
}
|
||||
|
||||
if (on->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT) && IsClient())
|
||||
if (IsBot() && on->GetSpecialAbility(IMMUNE_DAMAGE_BOT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (on->GetSpecialAbility(IMMUNE_DAMAGE_NPC) && IsNPC())
|
||||
if (IsClient() && on->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsNoCast())
|
||||
if (IsNPC() && on->GetSpecialAbility(IMMUNE_DAMAGE_NPC)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!IsValidSpell(spell_id)) { // Check for a valid spell otherwise it will crash through the function
|
||||
if(IsClient()){
|
||||
Message(0, "Invalid spell proc %u", spell_id);
|
||||
if (IsNoCast()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValidSpell(spell_id)) { // Check for a valid spell otherwise it will crash through the function
|
||||
if (IsClient()) {
|
||||
Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Invalid spell ID for proc {}.",
|
||||
spell_id
|
||||
).c_str()
|
||||
);
|
||||
LogSpells("Player [{}] Weapon Procced invalid spell [{}]", GetName(), spell_id);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5221,7 +5258,7 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on,
|
||||
return;
|
||||
}
|
||||
|
||||
if(inst && IsClient()) {
|
||||
if (inst && IsClient()) {
|
||||
//const cast is dirty but it would require redoing a ton of interfaces at this point
|
||||
//It should be safe as we don't have any truly const EQ::ItemInstance floating around anywhere.
|
||||
//So we'll live with it for now
|
||||
@@ -5241,30 +5278,76 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on,
|
||||
}
|
||||
}
|
||||
|
||||
bool twinproc = false;
|
||||
int32 twinproc_chance = 0;
|
||||
bool twin_proc = false;
|
||||
int32 twin_proc_chance = 0;
|
||||
|
||||
if (IsClient() || IsBot()) {
|
||||
twinproc_chance = GetFocusEffect(focusTwincast, spell_id);
|
||||
twin_proc_chance = GetFocusEffect(focusTwincast, spell_id);
|
||||
}
|
||||
|
||||
if (twinproc_chance && zone->random.Roll(twinproc_chance)) {
|
||||
twinproc = true;
|
||||
if (twin_proc_chance && zone->random.Roll(twin_proc_chance)) {
|
||||
twin_proc = true;
|
||||
}
|
||||
|
||||
if (IsBeneficialSpell(spell_id) && (!IsNPC() || (IsNPC() && CastToNPC()->GetInnateProcSpellID() != spell_id)) && spells[spell_id].target_type != ST_TargetsTarget) { // NPC innate procs don't take this path ever
|
||||
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
|
||||
if (twinproc) {
|
||||
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
|
||||
if (
|
||||
IsBeneficialSpell(spell_id) &&
|
||||
(
|
||||
!IsNPC() ||
|
||||
(
|
||||
IsNPC() &&
|
||||
CastToNPC()->GetInnateProcSpellID() != spell_id
|
||||
)
|
||||
) &&
|
||||
spells[spell_id].target_type != ST_TargetsTarget
|
||||
) { // NPC innate procs don't take this path ever
|
||||
SpellFinished(
|
||||
spell_id,
|
||||
this,
|
||||
EQ::spells::CastingSlot::Item,
|
||||
0,
|
||||
-1,
|
||||
spells[spell_id].resist_difficulty,
|
||||
true,
|
||||
level_override
|
||||
);
|
||||
|
||||
if (twin_proc) {
|
||||
SpellFinished(
|
||||
spell_id,
|
||||
this,
|
||||
EQ::spells::CastingSlot::Item,
|
||||
0,
|
||||
-1,
|
||||
spells[spell_id].resist_difficulty,
|
||||
true,
|
||||
level_override
|
||||
);
|
||||
}
|
||||
} else if (!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients
|
||||
SpellFinished(
|
||||
spell_id,
|
||||
on,
|
||||
EQ::spells::CastingSlot::Item,
|
||||
0,
|
||||
-1,
|
||||
spells[spell_id].resist_difficulty,
|
||||
true,
|
||||
level_override
|
||||
);
|
||||
|
||||
if (twin_proc && (!(on->IsClient() && on->CastToClient()->dead))) {
|
||||
SpellFinished(
|
||||
spell_id,
|
||||
on,
|
||||
EQ::spells::CastingSlot::Item,
|
||||
0,
|
||||
-1,
|
||||
spells[spell_id].resist_difficulty,
|
||||
true,
|
||||
level_override
|
||||
);
|
||||
}
|
||||
}
|
||||
else if(!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients
|
||||
SpellFinished(spell_id, on, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
|
||||
if (twinproc && (!(on->IsClient() && on->CastToClient()->dead))) {
|
||||
SpellFinished(spell_id, on, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 Mob::GetZoneID() const {
|
||||
|
||||
@@ -3798,6 +3798,8 @@ void NPC::DescribeSpecialAbilities(Client* c)
|
||||
IMMUNE_OPEN,
|
||||
IMMUNE_ASSASSINATE,
|
||||
IMMUNE_HEADSHOT,
|
||||
IMMUNE_AGGRO_BOT,
|
||||
IMMUNE_DAMAGE_BOT
|
||||
};
|
||||
|
||||
// These abilities have parameters that need to be parsed out individually
|
||||
|
||||
@@ -752,11 +752,7 @@ void QuestManager::stoptimer(const std::string& timer_name)
|
||||
);
|
||||
|
||||
for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) {
|
||||
LogInfo("Current [{}] Timer [{}]", e->name, timer_name);
|
||||
|
||||
if (e->mob && e->mob == owner && e->name == timer_name) {
|
||||
LogInfo("Matched [{}] Timer [{}]", e->name, timer_name);
|
||||
|
||||
if (has_stop_event) {
|
||||
if (owner->IsClient()) {
|
||||
parse->EventPlayer(EVENT_TIMER_STOP, owner->CastToClient(), timer_name, 0);
|
||||
|
||||
+2
-6
@@ -40,8 +40,6 @@ extern WorldServer worldserver;
|
||||
Raid::Raid(uint32 raidID)
|
||||
: GroupIDConsumer(raidID)
|
||||
{
|
||||
memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct));
|
||||
memset(group_aa, 0, sizeof(GroupLeadershipAA_Struct) * MAX_RAID_GROUPS);
|
||||
for (auto& gm : group_mentor) {
|
||||
gm.mentor_percent = 0;
|
||||
gm.mentoree = nullptr;
|
||||
@@ -65,8 +63,6 @@ Raid::Raid(uint32 raidID)
|
||||
Raid::Raid(Client* nLeader)
|
||||
: GroupIDConsumer()
|
||||
{
|
||||
memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct));
|
||||
memset(group_aa, 0, sizeof(GroupLeadershipAA_Struct) * MAX_RAID_GROUPS);
|
||||
for (auto& gm : group_mentor) {
|
||||
gm.mentor_percent = 0;
|
||||
gm.mentoree = nullptr;
|
||||
@@ -451,13 +447,13 @@ void Raid::SaveRaidLeaderAA()
|
||||
void Raid::UpdateGroupAAs(uint32 gid)
|
||||
{
|
||||
|
||||
if (gid > MAX_RAID_GROUPS) {
|
||||
if (gid > MAX_RAID_GROUPS || gid == RAID_GROUPLESS || gid < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Client *gl = GetGroupLeader(gid);
|
||||
|
||||
if (gl) {
|
||||
if (gl && gl->IsClient()) {
|
||||
gl->GetGroupAAs(&group_aa[gid]);
|
||||
} else {
|
||||
memset(&group_aa[gid], 0, sizeof(GroupLeadershipAA_Struct));
|
||||
|
||||
+2
-2
@@ -292,8 +292,8 @@ protected:
|
||||
bool disbandCheck;
|
||||
bool forceDisband;
|
||||
std::string motd;
|
||||
RaidLeadershipAA_Struct raid_aa;
|
||||
GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS];
|
||||
RaidLeadershipAA_Struct raid_aa{};
|
||||
GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS]{};
|
||||
|
||||
GroupMentor group_mentor[MAX_RAID_GROUPS];
|
||||
|
||||
|
||||
+5
-2
@@ -221,9 +221,12 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
}
|
||||
|
||||
// check to see if target is a caster mob before performing a mana tap
|
||||
if(GetTarget() && IsManaTapSpell(spell_id)) {
|
||||
if (!RuleB(Spells, ManaTapsOnAnyClass) && GetTarget()->GetCasterClass() != 'N' && RuleB(Spells, ManaTapsRequireNPCMana) && GetTarget()->GetMana() == 0) {
|
||||
// If melee, block if ManaTapsOnAnyClass rule is false
|
||||
// if caster, block if ManaTapsRequireNPCMana and no mana
|
||||
bool melee_block = !RuleB(Spells, ManaTapsOnAnyClass);
|
||||
bool caster_block = (GetTarget()->GetCasterClass() != 'N' && RuleB(Spells, ManaTapsRequireNPCMana) && GetTarget()->GetMana() == 0);
|
||||
if (melee_block || caster_block) {
|
||||
InterruptSpell(TARGET_NO_MANA, 0x121, spell_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
+56
-7
@@ -405,11 +405,23 @@ void Client::SendZoneCancel(ZoneChange_Struct *zc) {
|
||||
zc->success
|
||||
);
|
||||
|
||||
strcpy(zc2->char_name, zc->char_name);
|
||||
strn0cpy(zc2->char_name, zc->char_name, 64);
|
||||
zc2->zoneID = zone->GetZoneID();
|
||||
zc2->success = 1;
|
||||
outapp->priority = 6;
|
||||
FastQueuePacket(&outapp);
|
||||
zc2->instanceID = zone->GetInstanceID();
|
||||
|
||||
// this fixes an issue where when we do a zone cancel what often ends up happening is we are sending
|
||||
// the client the wrong coordinates to zone back to. Often times it is the x,y,z of the destination zone
|
||||
// because we saved the destination x,y,z on the client profile before we rejected the zone request.
|
||||
// we're using rewind location because it should be where the client relatively was before we rejected the zone request.
|
||||
// it also prevents the client from getting caught up in a zone loop because if we sent them exactly back to where they
|
||||
// originated the request we could end up in a situation where the client is caught in a zone loop.
|
||||
m_Position.x = m_RewindLocation.x;
|
||||
m_Position.y = m_RewindLocation.y;
|
||||
m_Position.z = m_RewindLocation.z;
|
||||
zc2->x = m_Position.x;
|
||||
zc2->y = m_Position.y;
|
||||
zc2->z = m_Position.z;
|
||||
|
||||
LogZoning(
|
||||
"(zc2) Client [{}] char_name [{}] zoning to [{}] ({}) cancelled instance_id [{}] x [{}] y [{}] z [{}] zone_reason [{}] success [{}]",
|
||||
@@ -425,6 +437,9 @@ void Client::SendZoneCancel(ZoneChange_Struct *zc) {
|
||||
zc2->success
|
||||
);
|
||||
|
||||
outapp->priority = 6;
|
||||
FastQueuePacket(&outapp);
|
||||
|
||||
//reset to unsolicited.
|
||||
zone_mode = ZoneUnsolicited;
|
||||
// reset since we're not zoning anymore
|
||||
@@ -740,10 +755,34 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
|
||||
pZoneName = strcpy(new char[zd->long_name.length() + 1], zd->long_name.c_str());
|
||||
}
|
||||
|
||||
// If we are zoning to the same zone, we need to use the current instance ID if it is not specified.
|
||||
if (zoneID == zone->GetZoneID() && instance_id == 0) {
|
||||
instance_id = zone->GetInstanceID();
|
||||
}
|
||||
|
||||
auto r = content_service.FindZone(zoneID, instance_id);
|
||||
if (r.zone_id) {
|
||||
zoneID = r.zone_id;
|
||||
instance_id = r.instance.id;
|
||||
LogZoning(
|
||||
"Client caught HandleZoneRoutingMiddleware [{}] zone_id [{}] instance_id [{}] x [{}] y [{}] z [{}] heading [{}] ignorerestrictions [{}] zone_mode [{}]",
|
||||
GetCleanName(),
|
||||
zoneID,
|
||||
instance_id,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
heading,
|
||||
ignorerestrictions,
|
||||
static_cast<int>(zm)
|
||||
);
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"Client [{}] zone_id [{}] x [{}] y [{}] z [{}] heading [{}] ignorerestrictions [{}] zone_mode [{}]",
|
||||
"Client [{}] zone_id [{}] instance_id [{}] x [{}] y [{}] z [{}] heading [{}] ignorerestrictions [{}] zone_mode [{}]",
|
||||
GetCleanName(),
|
||||
zoneID,
|
||||
instance_id,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
@@ -867,9 +906,8 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
|
||||
outapp->priority = 6;
|
||||
FastQueuePacket(&outapp);
|
||||
}
|
||||
else if(zm == ZoneSolicited || zm == ZoneToSafeCoords) {
|
||||
auto outapp =
|
||||
new EQApplicationPacket(OP_RequestClientZoneChange, sizeof(RequestClientZoneChange_Struct));
|
||||
else if (zm == ZoneSolicited || zm == ZoneToSafeCoords) {
|
||||
auto outapp = new EQApplicationPacket(OP_RequestClientZoneChange, sizeof(RequestClientZoneChange_Struct));
|
||||
RequestClientZoneChange_Struct* gmg = (RequestClientZoneChange_Struct*) outapp->pBuffer;
|
||||
|
||||
gmg->zone_id = zoneID;
|
||||
@@ -880,6 +918,17 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
|
||||
gmg->instance_id = instance_id;
|
||||
gmg->type = 0x01; //an observed value, not sure of meaning
|
||||
|
||||
LogZoning(
|
||||
"Player [{}] has requested zoning to zone_id [{}] instance_id [{}] x [{}] y [{}] z [{}] heading [{}]",
|
||||
GetCleanName(),
|
||||
zoneID,
|
||||
instance_id,
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
heading
|
||||
);
|
||||
|
||||
outapp->priority = 6;
|
||||
FastQueuePacket(&outapp);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user