mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-31 04:56:20 +00:00
Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9be2485330 | |||
| a2bf10624a | |||
| ed58d16f1f | |||
| e9b45fb360 | |||
| 803972873a | |||
| d9e57eca79 | |||
| 6429dc80d3 | |||
| c4262b3fa6 | |||
| 92128b98fd | |||
| b9cfdea76c | |||
| c8a7066d0e | |||
| 950cc4a325 | |||
| 82b48fe6e8 | |||
| ca9c1fdd24 | |||
| fe08961d25 | |||
| 5b9f7ff4c9 | |||
| 23743a4050 | |||
| 444d688ad2 | |||
| d554eb3423 | |||
| deb298dda7 | |||
| 19e785b842 | |||
| 7b9691d486 | |||
| 30fddcc5a0 | |||
| 235e59a2d8 | |||
| bc1ffe0716 | |||
| 44497414db | |||
| 96e34fe8f7 | |||
| ceca28d2a3 | |||
| b040571427 | |||
| 49664cc1a0 | |||
| 5e4fd43920 | |||
| 937b947597 | |||
| bb70850421 | |||
| 46511365a7 | |||
| 6d69ac7a98 | |||
| 938937c271 | |||
| cd808416c8 | |||
| a05d0752f6 | |||
| 799609fb21 | |||
| 213fe6a9e9 | |||
| be6a5d5f50 | |||
| 1af29bd7b1 | |||
| ef945e6e99 | |||
| 9528c1e7fc | |||
| fd6e5f465d | |||
| d00125abe1 | |||
| 5d69235a4c | |||
| e93785f885 | |||
| 3c2545cfaf | |||
| 8d1a9efac9 | |||
| f6b18fb003 | |||
| 00e77f190c | |||
| 9cb72a6ba7 |
+111
@@ -1,3 +1,114 @@
|
|||||||
|
## [23.4.0] 3/30/2025
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
* Expose Zoneserver Compile Metadata ([#4815](https://github.com/EQEmu/Server/pull/4815)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Bots
|
||||||
|
|
||||||
|
* Charmed Pets were breaking Mob respawns ([#4780](https://github.com/EQEmu/Server/pull/4780)) @nytmyr 2025-03-16
|
||||||
|
* Enraged positioning ([#4789](https://github.com/EQEmu/Server/pull/4789)) @nytmyr 2025-03-29
|
||||||
|
* Fix IsValidSpellTypeBySpellID to account for all types ([#4764](https://github.com/EQEmu/Server/pull/4764)) @nytmyr 2025-03-19
|
||||||
|
* Fix Rule ZonesWithSpawnLimits/ZonesWithForcedSpawnLimits errors ([#4791](https://github.com/EQEmu/Server/pull/4791)) @nytmyr 2025-03-29
|
||||||
|
* Fix rule Bots:FinishBuffing ([#4788](https://github.com/EQEmu/Server/pull/4788)) @nytmyr 2025-03-29
|
||||||
|
* Line of Sight and Mez optimizations and cleanup ([#4746](https://github.com/EQEmu/Server/pull/4746)) @nytmyr 2025-03-29
|
||||||
|
* Prevent bot pets from despawning on #repop ([#4790](https://github.com/EQEmu/Server/pull/4790)) @nytmyr 2025-03-29
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
* Control flow defaults missed in recent bot updates ([#4817](https://github.com/EQEmu/Server/pull/4817)) @joligario 2025-03-30
|
||||||
|
* Remove Extraneous Time Type in ShowZoneData ([#4806](https://github.com/EQEmu/Server/pull/4806)) @Kinglykrab 2025-03-29
|
||||||
|
* Remove Unused Command Methods ([#4805](https://github.com/EQEmu/Server/pull/4805)) @Kinglykrab 2025-03-29
|
||||||
|
* UCS Member Count ([#4819](https://github.com/EQEmu/Server/pull/4819)) @joligario 2025-03-30
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
* Add #show zone_variables ([#4812](https://github.com/EQEmu/Server/pull/4812)) @Akkadius 2025-03-29
|
||||||
|
* Add Instance Support to #zoneshutdown ([#4807](https://github.com/EQEmu/Server/pull/4807)) @Kinglykrab 2025-03-29
|
||||||
|
|
||||||
|
### Crash
|
||||||
|
|
||||||
|
* Fix Rarer World Crash with Player Event Thread Processor ([#4800](https://github.com/EQEmu/Server/pull/4800)) @Akkadius 2025-03-29
|
||||||
|
* Fix Repop Race Condition Crash ([#4814](https://github.com/EQEmu/Server/pull/4814)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
* Fix Respawn Times Table ([#4802](https://github.com/EQEmu/Server/pull/4802)) @Akkadius 2025-03-29
|
||||||
|
* Wrap PurgeExpiredInstances in a Transaction ([#4824](https://github.com/EQEmu/Server/pull/4824)) @Akkadius 2025-03-30
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* Implement /changename & related script bindings. Clean up #set name ([#4770](https://github.com/EQEmu/Server/pull/4770)) @catapultam-habeo 2025-03-20
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* AllowFVNoDrop Flag trades ([#4809](https://github.com/EQEmu/Server/pull/4809)) @neckkola 2025-03-27
|
||||||
|
* Fix Instance Creation Race Condition ([#4803](https://github.com/EQEmu/Server/pull/4803)) @Akkadius 2025-03-29
|
||||||
|
* Fix zone crash when attempting to add a disappearing client to hate list. ([#4782](https://github.com/EQEmu/Server/pull/4782)) @zimp-wow 2025-03-19
|
||||||
|
* Globally Reloading Quests when not loaded ([#4813](https://github.com/EQEmu/Server/pull/4813)) @Akkadius 2025-03-29
|
||||||
|
* Instance DZ Creation ([#4823](https://github.com/EQEmu/Server/pull/4823)) @Akkadius 2025-03-30
|
||||||
|
* Zone State Entity Variable Load Pre-Spawn ([#4785](https://github.com/EQEmu/Server/pull/4785)) @Akkadius 2025-03-19
|
||||||
|
* Zone State Position Fix ([#4784](https://github.com/EQEmu/Server/pull/4784)) @Akkadius 2025-03-19
|
||||||
|
* Zone State Variables Load First ([#4798](https://github.com/EQEmu/Server/pull/4798)) @Akkadius 2025-03-29
|
||||||
|
* Zone state edge case with 0 hp ([#4787](https://github.com/EQEmu/Server/pull/4787)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Instance
|
||||||
|
|
||||||
|
* Clear Respawn Timers on Creation ([#4801](https://github.com/EQEmu/Server/pull/4801)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Instances
|
||||||
|
|
||||||
|
* Add `expire_at` Column ([#4820](https://github.com/EQEmu/Server/pull/4820)) @Akkadius 2025-03-30
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
* Add several database indexes ([#4811](https://github.com/EQEmu/Server/pull/4811)) @Akkadius 2025-03-29
|
||||||
|
* Have World Send Smarter Guild Updates ([#4796](https://github.com/EQEmu/Server/pull/4796)) @Akkadius 2025-03-29
|
||||||
|
* Improve Character Select DB Performance ([#4799](https://github.com/EQEmu/Server/pull/4799)) @Akkadius 2025-03-29
|
||||||
|
* Reduce Adventure S2S chatter ([#4793](https://github.com/EQEmu/Server/pull/4793)) @Akkadius 2025-03-29
|
||||||
|
* Reduce CorpseOwnerOnline S2S Chatter to World ([#4795](https://github.com/EQEmu/Server/pull/4795)) @Akkadius 2025-03-29
|
||||||
|
* Reduce LFGuild Chatter ([#4794](https://github.com/EQEmu/Server/pull/4794)) @Akkadius 2025-03-29
|
||||||
|
* Reduce UpdateWho S2S Chatter to World ([#4792](https://github.com/EQEmu/Server/pull/4792)) @Akkadius 2025-03-29
|
||||||
|
* Send Smarter Emote Packets ([#4818](https://github.com/EQEmu/Server/pull/4818)) @Akkadius 2025-03-30
|
||||||
|
|
||||||
|
### Quest API
|
||||||
|
|
||||||
|
* Add Support for NPC ID and NPC Name Specificity ([#4781](https://github.com/EQEmu/Server/pull/4781)) @Kinglykrab 2025-03-19
|
||||||
|
|
||||||
|
### Reload
|
||||||
|
|
||||||
|
* Add Reload for Maps / Navs ([#4816](https://github.com/EQEmu/Server/pull/4816)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Zone State Automated Testing and Improvements ([#4808](https://github.com/EQEmu/Server/pull/4808)) @Akkadius 2025-03-30
|
||||||
|
* Zone State Improvements Part 3 ([#4773](https://github.com/EQEmu/Server/pull/4773)) @Akkadius 2025-03-13
|
||||||
|
|
||||||
|
## [23.3.4] 3/14/2025
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Add check for simultaneous direct vendor and parcel Trader/Buyer Purchase ([#4778](https://github.com/EQEmu/Server/pull/4778)) @neckkola 2025-03-14
|
||||||
|
* Fix for rare circumstance where NPC's would have 0 health on restore @Akkadius
|
||||||
|
|
||||||
|
## [23.3.3] 3/13/2025
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
* Add indexes for data_buckets and zone_state_spawns ([#4771](https://github.com/EQEmu/Server/pull/4771)) @Akkadius 2025-03-11
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Update GuildBank to correctly handle items with charges equal to zero ([#4774](https://github.com/EQEmu/Server/pull/4774)) @neckkola 2025-03-13
|
||||||
|
|
||||||
|
### Networking
|
||||||
|
|
||||||
|
* Fix "port in use" error ([#4772](https://github.com/EQEmu/Server/pull/4772)) @Akkadius 2025-03-12
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Zone State Improvements Part 3 ([#4773](https://github.com/EQEmu/Server/pull/4773)) @Akkadius 2025-03-13
|
||||||
|
|
||||||
## [23.3.2] 3/11/2025
|
## [23.3.2] 3/11/2025
|
||||||
|
|
||||||
### DynamicZones
|
### DynamicZones
|
||||||
|
|||||||
@@ -955,6 +955,29 @@ bool Database::UpdateName(const std::string& old_name, const std::string& new_na
|
|||||||
return CharacterDataRepository::UpdateOne(*this, e);
|
return CharacterDataRepository::UpdateOne(*this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Database::UpdateNameByID(const int character_id, const std::string& new_name)
|
||||||
|
{
|
||||||
|
LogInfo("Renaming [{}] to [{}]", character_id, new_name);
|
||||||
|
|
||||||
|
auto l = CharacterDataRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`id` = {}",
|
||||||
|
character_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (l.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& e = l.front();
|
||||||
|
|
||||||
|
e.name = new_name;
|
||||||
|
|
||||||
|
return CharacterDataRepository::UpdateOne(*this, e);
|
||||||
|
}
|
||||||
|
|
||||||
bool Database::IsNameUsed(const std::string& name)
|
bool Database::IsNameUsed(const std::string& name)
|
||||||
{
|
{
|
||||||
if (RuleB(Bots, Enabled)) {
|
if (RuleB(Bots, Enabled)) {
|
||||||
@@ -982,6 +1005,20 @@ bool Database::IsNameUsed(const std::string& name)
|
|||||||
return !character_data.empty();
|
return !character_data.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Players cannot have the same name as a pet vanity name, or memory corruption occurs.
|
||||||
|
bool Database::IsPetNameUsed(const std::string& name)
|
||||||
|
{
|
||||||
|
const auto& pet_name_data = CharacterPetNameRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`name` = '{}'",
|
||||||
|
Strings::Escape(name)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return !pet_name_data.empty();
|
||||||
|
}
|
||||||
|
|
||||||
uint32 Database::GetServerType()
|
uint32 Database::GetServerType()
|
||||||
{
|
{
|
||||||
const auto& l = VariablesRepository::GetWhere(*this, "`varname` = 'ServerType' LIMIT 1");
|
const auto& l = VariablesRepository::GetWhere(*this, "`varname` = 'ServerType' LIMIT 1");
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ public:
|
|||||||
bool ReserveName(uint32 account_id, const std::string& name);
|
bool ReserveName(uint32 account_id, const std::string& name);
|
||||||
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp);
|
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp);
|
||||||
bool UpdateName(const std::string& old_name, const std::string& new_name);
|
bool UpdateName(const std::string& old_name, const std::string& new_name);
|
||||||
|
bool UpdateNameByID(const int character_id, const std::string& new_name);
|
||||||
bool CopyCharacter(
|
bool CopyCharacter(
|
||||||
const std::string& source_character_name,
|
const std::string& source_character_name,
|
||||||
const std::string& destination_character_name,
|
const std::string& destination_character_name,
|
||||||
@@ -116,6 +117,7 @@ public:
|
|||||||
bool CheckGMIPs(const std::string& login_ip, uint32 account_id);
|
bool CheckGMIPs(const std::string& login_ip, uint32 account_id);
|
||||||
bool CheckNameFilter(const std::string& name, bool surname = false);
|
bool CheckNameFilter(const std::string& name, bool surname = false);
|
||||||
bool IsNameUsed(const std::string& name);
|
bool IsNameUsed(const std::string& name);
|
||||||
|
bool IsPetNameUsed(const std::string& name);
|
||||||
|
|
||||||
uint32 GetAccountIDByChar(const std::string& name, uint32* character_id = 0);
|
uint32 GetAccountIDByChar(const std::string& name, uint32* character_id = 0);
|
||||||
uint32 GetAccountIDByChar(uint32 character_id);
|
uint32 GetAccountIDByChar(uint32 character_id);
|
||||||
@@ -139,6 +141,7 @@ public:
|
|||||||
bool CheckInstanceExpired(uint16 instance_id);
|
bool CheckInstanceExpired(uint16 instance_id);
|
||||||
bool CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration);
|
bool CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration);
|
||||||
bool GetUnusedInstanceID(uint16& instance_id);
|
bool GetUnusedInstanceID(uint16& instance_id);
|
||||||
|
bool TryGetUnusedInstanceID(uint16& instance_id);
|
||||||
bool IsGlobalInstance(uint16 instance_id);
|
bool IsGlobalInstance(uint16 instance_id);
|
||||||
bool RemoveClientFromInstance(uint16 instance_id, uint32 char_id);
|
bool RemoveClientFromInstance(uint16 instance_id, uint32 char_id);
|
||||||
bool RemoveClientsFromInstance(uint16 instance_id);
|
bool RemoveClientsFromInstance(uint16 instance_id);
|
||||||
|
|||||||
@@ -6792,7 +6792,7 @@ UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 341) + 5810)
|
|||||||
},
|
},
|
||||||
ManifestEntry{
|
ManifestEntry{
|
||||||
.version = 9304,
|
.version = 9304,
|
||||||
.description = "2024_12_01_2024_update_guild_bank",
|
.description = "2024_12_01_update_guild_bank",
|
||||||
.check = "SHOW COLUMNS FROM `guild_bank` LIKE 'augment_one_id'",
|
.check = "SHOW COLUMNS FROM `guild_bank` LIKE 'augment_one_id'",
|
||||||
.condition = "empty",
|
.condition = "empty",
|
||||||
.match = "",
|
.match = "",
|
||||||
@@ -6914,7 +6914,7 @@ CREATE TABLE `zone_state_spawns` (
|
|||||||
},
|
},
|
||||||
ManifestEntry{
|
ManifestEntry{
|
||||||
.version = 9308,
|
.version = 9308,
|
||||||
.description = "2025_add_multivalue_support_to_evolving_subtype.sql",
|
.description = "2025_03_29_add_multivalue_support_to_evolving_subtype.sql",
|
||||||
.check = "SHOW COLUMNS FROM `items_evolving_details` LIKE 'sub_type'",
|
.check = "SHOW COLUMNS FROM `items_evolving_details` LIKE 'sub_type'",
|
||||||
.condition = "missing",
|
.condition = "missing",
|
||||||
.match = "varchar(200)",
|
.match = "varchar(200)",
|
||||||
@@ -6980,13 +6980,110 @@ ALTER TABLE data_buckets ADD INDEX idx_bot_expires (bot_id, expires);
|
|||||||
},
|
},
|
||||||
ManifestEntry{
|
ManifestEntry{
|
||||||
.version = 9313,
|
.version = 9313,
|
||||||
.description = "2025_03_11_data_bucket_indexes.sql",
|
.description = "2025_03_11_zone_state_spawns.sql",
|
||||||
.check = "SHOW INDEX FROM zone_state_spawns",
|
.check = "SHOW INDEX FROM zone_state_spawns",
|
||||||
.condition = "missing",
|
.condition = "missing",
|
||||||
.match = "idx_zone_instance",
|
.match = "idx_zone_instance",
|
||||||
.sql = R"(
|
.sql = R"(
|
||||||
ALTER TABLE zone_state_spawns ADD INDEX idx_zone_instance (zone_id, instance_id);
|
ALTER TABLE zone_state_spawns ADD INDEX idx_zone_instance (zone_id, instance_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9314,
|
||||||
|
.description = "2025_03_12_zone_state_spawns_one_time_truncate.sql",
|
||||||
|
.check = "SELECT * FROM db_version WHERE version >= 9314",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
TRUNCATE TABLE zone_state_spawns;
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9315,
|
||||||
|
.description = "2025_03_29_character_tribute_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM character_tribute",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_character_id",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE character_tribute ADD INDEX idx_character_id (character_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9316,
|
||||||
|
.description = "2025_03_29_player_titlesets_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM player_titlesets",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_char_id",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE player_titlesets ADD INDEX idx_char_id (char_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9317,
|
||||||
|
.description = "2025_03_29_respawn_times_instance_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM respawn_times",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_instance_id",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE respawn_times ADD INDEX idx_instance_id (instance_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9318,
|
||||||
|
.description = "2025_03_29_zone_state_spawns_instance_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM zone_state_spawns",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_instance_id",
|
||||||
|
.sql = R"(
|
||||||
ALTER TABLE zone_state_spawns ADD INDEX idx_instance_id (instance_id);
|
ALTER TABLE zone_state_spawns ADD INDEX idx_instance_id (instance_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9319,
|
||||||
|
.description = "2025_03_29_data_buckets_expires_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM data_buckets",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_expires",
|
||||||
|
.sql = R"(
|
||||||
|
CREATE INDEX idx_expires ON data_buckets (expires);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9320,
|
||||||
|
.description = "2025_03_23_add_respawn_times_expire_at.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `respawn_times` LIKE 'expire_at'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `respawn_times`
|
||||||
|
ADD COLUMN `expire_at` int(11) UNSIGNED NULL DEFAULT 0 AFTER `duration`;
|
||||||
|
|
||||||
|
UPDATE respawn_times set expire_at = `start` + `duration`; -- backfill existing data
|
||||||
|
|
||||||
|
CREATE INDEX `idx_expire_at` ON `respawn_times` (`expire_at`);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9321,
|
||||||
|
.description = "2025_03_30_instance_list_add_expire_at.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `instance_list` LIKE 'expire_at'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `instance_list`
|
||||||
|
ADD COLUMN `expire_at` bigint(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `duration`;
|
||||||
|
|
||||||
|
UPDATE instance_list set expire_at = `start_time` + `duration`; -- backfill existing data
|
||||||
|
|
||||||
|
CREATE INDEX `idx_expire_at` ON `instance_list` (`expire_at`);
|
||||||
)",
|
)",
|
||||||
.content_schema_update = false
|
.content_schema_update = false
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -128,11 +128,35 @@ bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version
|
|||||||
e.version = version;
|
e.version = version;
|
||||||
e.start_time = std::time(nullptr);
|
e.start_time = std::time(nullptr);
|
||||||
e.duration = duration;
|
e.duration = duration;
|
||||||
|
e.expire_at = e.start_time + duration;
|
||||||
|
|
||||||
return InstanceListRepository::InsertOne(*this, e).id;
|
RespawnTimesRepository::ClearInstanceTimers(*this, e.id);
|
||||||
|
InstanceListRepository::ReplaceOne(*this, e);
|
||||||
|
return instance_id > 0 && e.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Database::GetUnusedInstanceID(uint16 &instance_id)
|
bool Database::GetUnusedInstanceID(uint16 &instance_id)
|
||||||
|
{
|
||||||
|
// attempt to get an unused instance id
|
||||||
|
for (int a = 0; a < 10; a++) {
|
||||||
|
uint16 attempted_id = 0;
|
||||||
|
if (TryGetUnusedInstanceID(attempted_id)) {
|
||||||
|
auto i = InstanceListRepository::NewEntity();
|
||||||
|
i.id = attempted_id;
|
||||||
|
i.notes = "Prefetching";
|
||||||
|
auto n = InstanceListRepository::InsertOne(*this, i);
|
||||||
|
if (n.id > 0) {
|
||||||
|
instance_id = n.id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instance_id = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::TryGetUnusedInstanceID(uint16 &instance_id)
|
||||||
{
|
{
|
||||||
uint32 max_reserved_instance_id = RuleI(Instances, ReservedInstances);
|
uint32 max_reserved_instance_id = RuleI(Instances, ReservedInstances);
|
||||||
uint32 max_instance_id = 32000;
|
uint32 max_instance_id = 32000;
|
||||||
@@ -537,14 +561,12 @@ void Database::GetCharactersInInstance(uint16 instance_id, std::list<uint32> &ch
|
|||||||
|
|
||||||
void Database::PurgeExpiredInstances()
|
void Database::PurgeExpiredInstances()
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Delay purging by a day so that we can continue using adjacent free instance id's
|
|
||||||
* from the table without risking the chance we immediately re-allocate a zone that freshly expired but
|
|
||||||
* has not been fully de-allocated
|
|
||||||
*/
|
|
||||||
auto l = InstanceListRepository::GetWhere(
|
auto l = InstanceListRepository::GetWhere(
|
||||||
*this,
|
*this,
|
||||||
"(start_time + duration) <= (UNIX_TIMESTAMP() - 86400) AND never_expires = 0"
|
fmt::format(
|
||||||
|
"expire_at <= (UNIX_TIMESTAMP() - {}) and expire_at != 0 AND never_expires = 0",
|
||||||
|
RuleI(Instances, ExpireOffsetTimeSeconds)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
if (l.empty()) {
|
if (l.empty()) {
|
||||||
return;
|
return;
|
||||||
@@ -555,20 +577,24 @@ void Database::PurgeExpiredInstances()
|
|||||||
instance_ids.emplace_back(std::to_string(e.id));
|
instance_ids.emplace_back(std::to_string(e.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto imploded_instance_ids = Strings::Implode(",", instance_ids);
|
const auto ids = Strings::Implode(",", instance_ids);
|
||||||
|
|
||||||
InstanceListRepository::DeleteWhere(*this, fmt::format("id IN ({})", imploded_instance_ids));
|
TransactionBegin();
|
||||||
InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id IN ({})", imploded_instance_ids));
|
InstanceListRepository::DeleteWhere(*this, fmt::format("id IN ({})", ids));
|
||||||
RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id IN ({})", ids));
|
||||||
SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||||
CharacterCorpsesRepository::BuryInstances(*this, imploded_instance_ids);
|
SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||||
DynamicZoneMembersRepository::DeleteByManyInstances(*this, imploded_instance_ids);
|
CharacterCorpsesRepository::BuryInstances(*this, ids);
|
||||||
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
DynamicZoneMembersRepository::DeleteByManyInstances(*this, ids);
|
||||||
Spawn2DisabledRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||||
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id != 0 and instance_id IN ({})", imploded_instance_ids));
|
Spawn2DisabledRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||||
|
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id != 0 and instance_id IN ({})", ids));
|
||||||
if (RuleB(Zone, StateSavingOnShutdown)) {
|
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||||
ZoneStateSpawnsRepository::DeleteWhere(*this, fmt::format("`instance_id` IN ({})", imploded_instance_ids));
|
ZoneStateSpawnsRepository::DeleteWhere(*this, fmt::format("`instance_id` IN ({})", ids));
|
||||||
}
|
}
|
||||||
|
TransactionCommit();
|
||||||
|
|
||||||
|
LogInfo("Purged [{}] expired instances", l.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
||||||
@@ -580,6 +606,7 @@ void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
|||||||
|
|
||||||
i.start_time = std::time(nullptr);
|
i.start_time = std::time(nullptr);
|
||||||
i.duration = new_duration;
|
i.duration = new_duration;
|
||||||
|
i.expire_at = i.start_time + i.duration;
|
||||||
|
|
||||||
InstanceListRepository::UpdateOne(*this, i);
|
InstanceListRepository::UpdateOne(*this, i);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,15 +58,16 @@ uint32_t DynamicZoneBase::CreateInstance()
|
|||||||
insert_instance.start_time = static_cast<int>(std::chrono::system_clock::to_time_t(m_start_time));
|
insert_instance.start_time = static_cast<int>(std::chrono::system_clock::to_time_t(m_start_time));
|
||||||
insert_instance.duration = static_cast<int>(m_duration.count());
|
insert_instance.duration = static_cast<int>(m_duration.count());
|
||||||
insert_instance.never_expires = m_never_expires;
|
insert_instance.never_expires = m_never_expires;
|
||||||
|
insert_instance.expire_at = insert_instance.start_time + insert_instance.duration;
|
||||||
|
|
||||||
auto instance = InstanceListRepository::InsertOne(GetDatabase(), insert_instance);
|
auto instance = InstanceListRepository::ReplaceOne(GetDatabase(), insert_instance);
|
||||||
if (instance.id == 0)
|
if (!instance)
|
||||||
{
|
{
|
||||||
LogDynamicZones("Failed to create instance [{}] for zone [{}]", unused_instance_id, m_zone_id);
|
LogDynamicZones("Failed to create instance [{}] for zone [{}]", unused_instance_id, m_zone_id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_instance_id = instance.id;
|
m_instance_id = unused_instance_id;
|
||||||
|
|
||||||
return m_instance_id;
|
return m_instance_id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,6 +287,8 @@ N(OP_InstillDoubt),
|
|||||||
N(OP_InterruptCast),
|
N(OP_InterruptCast),
|
||||||
N(OP_InvokeChangePetName),
|
N(OP_InvokeChangePetName),
|
||||||
N(OP_InvokeChangePetNameImmediate),
|
N(OP_InvokeChangePetNameImmediate),
|
||||||
|
N(OP_InvokeNameChangeImmediate),
|
||||||
|
N(OP_InvokeNameChangeLazy),
|
||||||
N(OP_ItemLinkClick),
|
N(OP_ItemLinkClick),
|
||||||
N(OP_ItemLinkResponse),
|
N(OP_ItemLinkResponse),
|
||||||
N(OP_ItemLinkText),
|
N(OP_ItemLinkText),
|
||||||
|
|||||||
@@ -5832,21 +5832,28 @@ struct ChangeSize_Struct
|
|||||||
/*16*/
|
/*16*/
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ChangeNameResponse : int {
|
||||||
|
Denied = 0, // 5167: "You have requested an invalid name or a Customer Service Representative has denied your name request. Please try another name."
|
||||||
|
Accepted = 1, // 5976: "Your request for a name change was successful."
|
||||||
|
Timeout = -1, // 5977: "Your request for a name change has timed out. Please try again later."
|
||||||
|
ServerError = -2, // 5978: "The server had an error while processing your name request. Please try again later."
|
||||||
|
RateLimited = -3, // 5979: "You must wait longer before submitting another name request. Please try again in a few minutes."
|
||||||
|
Ineligible = -4, // 5980: "Your character is not eligible for a name change."
|
||||||
|
Pending = -5 // 5193: "You already have a name change pending. Please wait until it is fully processed before attempting another name change."
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AltChangeName_Struct {
|
||||||
|
/*00*/ char new_name[64];
|
||||||
|
/*40*/ char old_name[64];
|
||||||
|
/*80*/ int response_code;
|
||||||
|
};
|
||||||
|
|
||||||
struct ChangePetName_Struct {
|
struct ChangePetName_Struct {
|
||||||
/*00*/ char new_pet_name[64];
|
/*00*/ char new_pet_name[64];
|
||||||
/*40*/ char pet_owner_name[64];
|
/*40*/ char pet_owner_name[64];
|
||||||
/*80*/ int response_code;
|
/*80*/ int response_code;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ChangePetNameResponse : int {
|
|
||||||
Denied = 0, // 5167 You have requested an invalid name or a Customer Service Representative has denied your name request. Please try another name.
|
|
||||||
Accepted = 1, // 5976 Your request for a name change was successful.
|
|
||||||
Timeout = -3, // 5979 You must wait longer before submitting another name request. Please try again in a few minutes.
|
|
||||||
NotEligible = -4, // 5980 Your character is not eligible for a name change.
|
|
||||||
Pending = -5, // 5193 You already have a name change pending. Please wait until it is fully processed before attempting another name change.
|
|
||||||
Unhandled = -1
|
|
||||||
};
|
|
||||||
|
|
||||||
// New OpCode/Struct for SoD+
|
// New OpCode/Struct for SoD+
|
||||||
struct GroupMakeLeader_Struct
|
struct GroupMakeLeader_Struct
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -303,6 +303,14 @@ bool IpUtil::IsPortInUse(const std::string& ip, int port) {
|
|||||||
return true; // Assume in use on failure
|
return true; // Assume in use on failure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
int opt = 1;
|
||||||
|
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&opt, sizeof(opt)); // Windows-specific
|
||||||
|
#else
|
||||||
|
int opt = 1;
|
||||||
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // Linux/macOS
|
||||||
|
#endif
|
||||||
|
|
||||||
sockaddr_in addr{};
|
sockaddr_in addr{};
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(port);
|
addr.sin_port = htons(port);
|
||||||
|
|||||||
@@ -906,26 +906,34 @@ bool EQ::ItemInstance::IsSlotAllowed(int16 slot_id) const {
|
|||||||
|
|
||||||
bool EQ::ItemInstance::IsDroppable(bool recurse) const
|
bool EQ::ItemInstance::IsDroppable(bool recurse) const
|
||||||
{
|
{
|
||||||
if (!m_item)
|
if (!m_item) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
/*if (m_ornamentidfile) // not implemented
|
/*if (m_ornamentidfile) // not implemented
|
||||||
return false;*/
|
return false;*/
|
||||||
if (m_attuned)
|
if (m_attuned) {
|
||||||
return false;
|
return false;
|
||||||
/*if (m_item->FVNoDrop != 0) // not implemented
|
}
|
||||||
return false;*/
|
|
||||||
if (m_item->NoDrop == 0)
|
if (RuleI(World, FVNoDropFlag) == FVNoDropFlagRule::Enabled && m_item->FVNoDrop == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_item->NoDrop == 0) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (recurse) {
|
if (recurse) {
|
||||||
for (auto iter : m_contents) {
|
for (auto iter: m_contents) {
|
||||||
if (!iter.second)
|
if (!iter.second) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!iter.second->IsDroppable(recurse))
|
if (!iter.second->IsDroppable(recurse)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public:
|
|||||||
uint8_t is_global;
|
uint8_t is_global;
|
||||||
uint32_t start_time;
|
uint32_t start_time;
|
||||||
uint32_t duration;
|
uint32_t duration;
|
||||||
|
uint64_t expire_at;
|
||||||
uint8_t never_expires;
|
uint8_t never_expires;
|
||||||
std::string notes;
|
std::string notes;
|
||||||
};
|
};
|
||||||
@@ -43,6 +44,7 @@ public:
|
|||||||
"is_global",
|
"is_global",
|
||||||
"start_time",
|
"start_time",
|
||||||
"duration",
|
"duration",
|
||||||
|
"expire_at",
|
||||||
"never_expires",
|
"never_expires",
|
||||||
"notes",
|
"notes",
|
||||||
};
|
};
|
||||||
@@ -57,6 +59,7 @@ public:
|
|||||||
"is_global",
|
"is_global",
|
||||||
"start_time",
|
"start_time",
|
||||||
"duration",
|
"duration",
|
||||||
|
"expire_at",
|
||||||
"never_expires",
|
"never_expires",
|
||||||
"notes",
|
"notes",
|
||||||
};
|
};
|
||||||
@@ -105,6 +108,7 @@ public:
|
|||||||
e.is_global = 0;
|
e.is_global = 0;
|
||||||
e.start_time = 0;
|
e.start_time = 0;
|
||||||
e.duration = 0;
|
e.duration = 0;
|
||||||
|
e.expire_at = 0;
|
||||||
e.never_expires = 0;
|
e.never_expires = 0;
|
||||||
e.notes = "";
|
e.notes = "";
|
||||||
|
|
||||||
@@ -149,8 +153,9 @@ public:
|
|||||||
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||||
e.never_expires = row[6] ? static_cast<uint8_t>(strtoul(row[6], nullptr, 10)) : 0;
|
e.expire_at = row[6] ? strtoull(row[6], nullptr, 10) : 0;
|
||||||
e.notes = row[7] ? row[7] : "";
|
e.never_expires = row[7] ? static_cast<uint8_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.notes = row[8] ? row[8] : "";
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -189,8 +194,9 @@ public:
|
|||||||
v.push_back(columns[3] + " = " + std::to_string(e.is_global));
|
v.push_back(columns[3] + " = " + std::to_string(e.is_global));
|
||||||
v.push_back(columns[4] + " = " + std::to_string(e.start_time));
|
v.push_back(columns[4] + " = " + std::to_string(e.start_time));
|
||||||
v.push_back(columns[5] + " = " + std::to_string(e.duration));
|
v.push_back(columns[5] + " = " + std::to_string(e.duration));
|
||||||
v.push_back(columns[6] + " = " + std::to_string(e.never_expires));
|
v.push_back(columns[6] + " = " + std::to_string(e.expire_at));
|
||||||
v.push_back(columns[7] + " = '" + Strings::Escape(e.notes) + "'");
|
v.push_back(columns[7] + " = " + std::to_string(e.never_expires));
|
||||||
|
v.push_back(columns[8] + " = '" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -218,6 +224,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.is_global));
|
v.push_back(std::to_string(e.is_global));
|
||||||
v.push_back(std::to_string(e.start_time));
|
v.push_back(std::to_string(e.start_time));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.never_expires));
|
v.push_back(std::to_string(e.never_expires));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -255,6 +262,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.is_global));
|
v.push_back(std::to_string(e.is_global));
|
||||||
v.push_back(std::to_string(e.start_time));
|
v.push_back(std::to_string(e.start_time));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.never_expires));
|
v.push_back(std::to_string(e.never_expires));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -296,8 +304,9 @@ public:
|
|||||||
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||||
e.never_expires = row[6] ? static_cast<uint8_t>(strtoul(row[6], nullptr, 10)) : 0;
|
e.expire_at = row[6] ? strtoull(row[6], nullptr, 10) : 0;
|
||||||
e.notes = row[7] ? row[7] : "";
|
e.never_expires = row[7] ? static_cast<uint8_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.notes = row[8] ? row[8] : "";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -328,8 +337,9 @@ public:
|
|||||||
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||||
e.never_expires = row[6] ? static_cast<uint8_t>(strtoul(row[6], nullptr, 10)) : 0;
|
e.expire_at = row[6] ? strtoull(row[6], nullptr, 10) : 0;
|
||||||
e.notes = row[7] ? row[7] : "";
|
e.never_expires = row[7] ? static_cast<uint8_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.notes = row[8] ? row[8] : "";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -410,6 +420,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.is_global));
|
v.push_back(std::to_string(e.is_global));
|
||||||
v.push_back(std::to_string(e.start_time));
|
v.push_back(std::to_string(e.start_time));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.never_expires));
|
v.push_back(std::to_string(e.never_expires));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -440,6 +451,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.is_global));
|
v.push_back(std::to_string(e.is_global));
|
||||||
v.push_back(std::to_string(e.start_time));
|
v.push_back(std::to_string(e.start_time));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.never_expires));
|
v.push_back(std::to_string(e.never_expires));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public:
|
|||||||
int32_t id;
|
int32_t id;
|
||||||
int32_t start;
|
int32_t start;
|
||||||
int32_t duration;
|
int32_t duration;
|
||||||
|
uint32_t expire_at;
|
||||||
int16_t instance_id;
|
int16_t instance_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@ public:
|
|||||||
"id",
|
"id",
|
||||||
"start",
|
"start",
|
||||||
"duration",
|
"duration",
|
||||||
|
"expire_at",
|
||||||
"instance_id",
|
"instance_id",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -46,6 +48,7 @@ public:
|
|||||||
"id",
|
"id",
|
||||||
"start",
|
"start",
|
||||||
"duration",
|
"duration",
|
||||||
|
"expire_at",
|
||||||
"instance_id",
|
"instance_id",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -90,6 +93,7 @@ public:
|
|||||||
e.id = 0;
|
e.id = 0;
|
||||||
e.start = 0;
|
e.start = 0;
|
||||||
e.duration = 0;
|
e.duration = 0;
|
||||||
|
e.expire_at = 0;
|
||||||
e.instance_id = 0;
|
e.instance_id = 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
@@ -130,7 +134,8 @@ public:
|
|||||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||||
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||||
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
e.instance_id = row[3] ? static_cast<int16_t>(atoi(row[3])) : 0;
|
e.expire_at = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[4] ? static_cast<int16_t>(atoi(row[4])) : 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -167,7 +172,8 @@ public:
|
|||||||
v.push_back(columns[0] + " = " + std::to_string(e.id));
|
v.push_back(columns[0] + " = " + std::to_string(e.id));
|
||||||
v.push_back(columns[1] + " = " + std::to_string(e.start));
|
v.push_back(columns[1] + " = " + std::to_string(e.start));
|
||||||
v.push_back(columns[2] + " = " + std::to_string(e.duration));
|
v.push_back(columns[2] + " = " + std::to_string(e.duration));
|
||||||
v.push_back(columns[3] + " = " + std::to_string(e.instance_id));
|
v.push_back(columns[3] + " = " + std::to_string(e.expire_at));
|
||||||
|
v.push_back(columns[4] + " = " + std::to_string(e.instance_id));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -192,6 +198,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.start));
|
v.push_back(std::to_string(e.start));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.instance_id));
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -225,6 +232,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.start));
|
v.push_back(std::to_string(e.start));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.instance_id));
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
@@ -262,7 +270,8 @@ public:
|
|||||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||||
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||||
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
e.instance_id = row[3] ? static_cast<int16_t>(atoi(row[3])) : 0;
|
e.expire_at = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[4] ? static_cast<int16_t>(atoi(row[4])) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -290,7 +299,8 @@ public:
|
|||||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||||
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||||
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
e.instance_id = row[3] ? static_cast<int16_t>(atoi(row[3])) : 0;
|
e.expire_at = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[4] ? static_cast<int16_t>(atoi(row[4])) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -368,6 +378,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.start));
|
v.push_back(std::to_string(e.start));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.instance_id));
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -394,6 +405,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.start));
|
v.push_back(std::to_string(e.start));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.instance_id));
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
|||||||
@@ -7,49 +7,11 @@
|
|||||||
|
|
||||||
class InstanceListRepository: public BaseInstanceListRepository {
|
class InstanceListRepository: public BaseInstanceListRepository {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
|
||||||
* This file was auto generated and can be modified and extended upon
|
|
||||||
*
|
|
||||||
* Base repository methods are automatically
|
|
||||||
* generated in the "base" version of this repository. The base repository
|
|
||||||
* is immutable and to be left untouched, while methods in this class
|
|
||||||
* are used as extension methods for more specific persistence-layer
|
|
||||||
* accessors or mutators.
|
|
||||||
*
|
|
||||||
* Base Methods (Subject to be expanded upon in time)
|
|
||||||
*
|
|
||||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
|
||||||
*
|
|
||||||
* InsertOne
|
|
||||||
* UpdateOne
|
|
||||||
* DeleteOne
|
|
||||||
* FindOne
|
|
||||||
* GetWhere(std::string where_filter)
|
|
||||||
* DeleteWhere(std::string where_filter)
|
|
||||||
* InsertMany
|
|
||||||
* All
|
|
||||||
*
|
|
||||||
* Example custom methods in a repository
|
|
||||||
*
|
|
||||||
* InstanceListRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
|
||||||
* InstanceListRepository::GetWhereNeverExpires()
|
|
||||||
* InstanceListRepository::GetWhereXAndY()
|
|
||||||
* InstanceListRepository::DeleteWhereXAndY()
|
|
||||||
*
|
|
||||||
* Most of the above could be covered by base methods, but if you as a developer
|
|
||||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
|
||||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
|
||||||
* method and encapsulate filters there
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Custom extended repository methods here
|
|
||||||
|
|
||||||
static int UpdateDuration(Database& db, uint16 instance_id, uint32_t new_duration)
|
static int UpdateDuration(Database& db, uint16 instance_id, uint32_t new_duration)
|
||||||
{
|
{
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"UPDATE `{}` SET `duration` = {} WHERE `{}` = {}",
|
"UPDATE `{}` SET `duration` = {}, `expire_at` = (`duration` + `start_time`) WHERE `{}` = {}",
|
||||||
TableName(),
|
TableName(),
|
||||||
new_duration,
|
new_duration,
|
||||||
PrimaryKey(),
|
PrimaryKey(),
|
||||||
@@ -65,7 +27,7 @@ public:
|
|||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
SQL(
|
SQL(
|
||||||
SELECT ((start_time + duration) - UNIX_TIMESTAMP()) AS `remaining` FROM `{}`
|
SELECT (`expire_at` - UNIX_TIMESTAMP()) AS `remaining` FROM `{}`
|
||||||
WHERE `id` = {}
|
WHERE `id` = {}
|
||||||
),
|
),
|
||||||
TableName(),
|
TableName(),
|
||||||
|
|||||||
@@ -8,47 +8,11 @@
|
|||||||
class RespawnTimesRepository: public BaseRespawnTimesRepository {
|
class RespawnTimesRepository: public BaseRespawnTimesRepository {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
|
||||||
* This file was auto generated and can be modified and extended upon
|
|
||||||
*
|
|
||||||
* Base repository methods are automatically
|
|
||||||
* generated in the "base" version of this repository. The base repository
|
|
||||||
* is immutable and to be left untouched, while methods in this class
|
|
||||||
* are used as extension methods for more specific persistence-layer
|
|
||||||
* accessors or mutators.
|
|
||||||
*
|
|
||||||
* Base Methods (Subject to be expanded upon in time)
|
|
||||||
*
|
|
||||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
|
||||||
*
|
|
||||||
* InsertOne
|
|
||||||
* UpdateOne
|
|
||||||
* DeleteOne
|
|
||||||
* FindOne
|
|
||||||
* GetWhere(std::string where_filter)
|
|
||||||
* DeleteWhere(std::string where_filter)
|
|
||||||
* InsertMany
|
|
||||||
* All
|
|
||||||
*
|
|
||||||
* Example custom methods in a repository
|
|
||||||
*
|
|
||||||
* RespawnTimesRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
|
||||||
* RespawnTimesRepository::GetWhereNeverExpires()
|
|
||||||
* RespawnTimesRepository::GetWhereXAndY()
|
|
||||||
* RespawnTimesRepository::DeleteWhereXAndY()
|
|
||||||
*
|
|
||||||
* Most of the above could be covered by base methods, but if you as a developer
|
|
||||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
|
||||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
|
||||||
* method and encapsulate filters there
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Custom extended repository methods here
|
|
||||||
static void ClearExpiredRespawnTimers(Database& db)
|
static void ClearExpiredRespawnTimers(Database& db)
|
||||||
{
|
{
|
||||||
db.QueryDatabase(
|
db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"DELETE FROM `{}` WHERE (`start` + `duration`) < UNIX_TIMESTAMP(NOW())",
|
"DELETE FROM `{}` WHERE `expire_at` < UNIX_TIMESTAMP(NOW())",
|
||||||
TableName()
|
TableName()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -77,6 +41,11 @@ public:
|
|||||||
|
|
||||||
return ((r.start + r.duration) - time_seconds);
|
return ((r.start + r.duration) - time_seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ClearInstanceTimers(Database &db, int32_t id)
|
||||||
|
{
|
||||||
|
RespawnTimesRepository::DeleteWhere(db, fmt::format("`instance_id` = {}", id));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_RESPAWN_TIMES_REPOSITORY_H
|
#endif //EQEMU_RESPAWN_TIMES_REPOSITORY_H
|
||||||
|
|||||||
@@ -5,9 +5,77 @@
|
|||||||
#include "../strings.h"
|
#include "../strings.h"
|
||||||
#include "base/base_zone_state_spawns_repository.h"
|
#include "base/base_zone_state_spawns_repository.h"
|
||||||
|
|
||||||
class ZoneStateSpawnsRepository: public BaseZoneStateSpawnsRepository {
|
class ZoneStateSpawnsRepository : public BaseZoneStateSpawnsRepository {
|
||||||
public:
|
public:
|
||||||
// Custom extended repository methods here
|
static void PurgeInvalidZoneStates(Database &database)
|
||||||
|
{
|
||||||
|
std::string query = R"(
|
||||||
|
SELECT zone_id, instance_id
|
||||||
|
FROM zone_state_spawns
|
||||||
|
GROUP BY zone_id, instance_id
|
||||||
|
HAVING COUNT(*) = SUM(
|
||||||
|
CASE
|
||||||
|
WHEN hp = 0
|
||||||
|
AND mana = 0
|
||||||
|
AND endurance = 0
|
||||||
|
AND (loot_data IS NULL OR loot_data = '')
|
||||||
|
AND (entity_variables IS NULL OR entity_variables = '')
|
||||||
|
AND (buffs IS NULL OR buffs = '')
|
||||||
|
THEN 1 ELSE 0
|
||||||
|
END
|
||||||
|
);
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
|
if (!results.Success()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto row: results) {
|
||||||
|
uint32 zone_id = std::stoul(row[0]);
|
||||||
|
uint32 instance_id = std::stoul(row[1]);
|
||||||
|
|
||||||
|
int rows = ZoneStateSpawnsRepository::DeleteWhere(
|
||||||
|
database,
|
||||||
|
fmt::format(
|
||||||
|
"`zone_id` = {} AND `instance_id` = {}",
|
||||||
|
zone_id,
|
||||||
|
instance_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
LogInfo(
|
||||||
|
"Purged invalid zone state data for zone [{}] instance [{}] rows [{}]",
|
||||||
|
zone_id,
|
||||||
|
instance_id,
|
||||||
|
Strings::Commify(rows)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PurgeOldZoneStates(Database &database)
|
||||||
|
{
|
||||||
|
int days = RuleI(Zone, StateSaveClearDays);
|
||||||
|
|
||||||
|
std::string query = fmt::format(
|
||||||
|
"DELETE FROM zone_state_spawns WHERE created_at < NOW() - INTERVAL {} DAY",
|
||||||
|
days
|
||||||
|
);
|
||||||
|
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
|
if (!results.Success()) {
|
||||||
|
LogError("Failed to purge old zone state data older than {} days.", days);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.RowsAffected() > 0) {
|
||||||
|
LogInfo(
|
||||||
|
"Purged old zone state data older than days [{}] rows [{}]",
|
||||||
|
days,
|
||||||
|
Strings::Commify(results.RowsAffected())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+15
-10
@@ -54,7 +54,7 @@ RULE_INT(Character, CorpseDecayTime, 604800000, "Time after which the corpse dec
|
|||||||
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, 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, 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, CorpseOwnerOnlineCheckTime, 300, "How often corpse will check if its owner is online DEFAULT: 300 (5 minutes)")
|
||||||
RULE_BOOL(Character, LeaveCorpses, true, "Setting whether you leave a corpse behind")
|
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_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")
|
RULE_INT(Character, MaxDraggedCorpses, 2, "Maximum number of corpses you can drag at once")
|
||||||
@@ -231,6 +231,7 @@ RULE_INT(Character, MendAlwaysSucceedValue, 199, "Value at which mend will alway
|
|||||||
RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false")
|
RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false")
|
||||||
RULE_INT(Character, BandolierSwapDelay, 0, "Bandolier swap delay in milliseconds, default is 0")
|
RULE_INT(Character, BandolierSwapDelay, 0, "Bandolier swap delay in milliseconds, default is 0")
|
||||||
RULE_BOOL(Character, EnableHackedFastCampForGM, false, "Enables hacked fast camp for GM clients, if the GM doesn't have a hacked client they'll camp like normal")
|
RULE_BOOL(Character, EnableHackedFastCampForGM, false, "Enables hacked fast camp for GM clients, if the GM doesn't have a hacked client they'll camp like normal")
|
||||||
|
RULE_BOOL(Character, AlwaysAllowNameChange, false, "Enable this option to allow /changename to work without enabling a name change via scripts.")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Mercs)
|
RULE_CATEGORY(Mercs)
|
||||||
@@ -260,6 +261,7 @@ RULE_INT(Guild, TributeTime, 600000, "Time in ms for guild tributes. Default is
|
|||||||
RULE_INT(Guild, TributeTimeRefreshInterval, 180000, "Time in ms to send all guild members a Tribute Time refresh. Default is 3 mins.")
|
RULE_INT(Guild, TributeTimeRefreshInterval, 180000, "Time in ms to send all guild members a Tribute Time refresh. Default is 3 mins.")
|
||||||
RULE_INT(Guild, TributePlatConversionRate, 10, "The conversion rate of platinum donations. Default is 10 guild favor to 1 platinum.")
|
RULE_INT(Guild, TributePlatConversionRate, 10, "The conversion rate of platinum donations. Default is 10 guild favor to 1 platinum.")
|
||||||
RULE_BOOL(Guild, UseCharacterMaxLevelForGuildTributes, true, "Guild Tributes will adhere to Character:MaxLevel. Default is true.")
|
RULE_BOOL(Guild, UseCharacterMaxLevelForGuildTributes, true, "Guild Tributes will adhere to Character:MaxLevel. Default is true.")
|
||||||
|
RULE_BOOL(Guild, EnableLFGuild, false, "Enable the LFGuild system (Requires queryserv)")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Skills)
|
RULE_CATEGORY(Skills)
|
||||||
@@ -289,6 +291,7 @@ RULE_BOOL(Pets, ClientPetsUseOwnerNameInLastName, true, "Disable this to keep cl
|
|||||||
RULE_BOOL(Pets, CanTakeNoDrop, false, "Setting whether anyone can give no-drop items to pets")
|
RULE_BOOL(Pets, CanTakeNoDrop, false, "Setting whether anyone can give no-drop items to pets")
|
||||||
RULE_INT(Pets, PetTauntRange, 150, "Range at which a pet will taunt targets.")
|
RULE_INT(Pets, PetTauntRange, 150, "Range at which a pet will taunt targets.")
|
||||||
RULE_BOOL(Pets, AlwaysAllowPetRename, false, "Enable this option to allow /changepetname to work without enabling a pet name change via scripts.")
|
RULE_BOOL(Pets, AlwaysAllowPetRename, false, "Enable this option to allow /changepetname to work without enabling a pet name change via scripts.")
|
||||||
|
RULE_BOOL(Pets, PetsRequireLoS, false, "Whether or not pets require line of sight to be told to attack their target")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(GM)
|
RULE_CATEGORY(GM)
|
||||||
@@ -376,7 +379,9 @@ RULE_BOOL(Zone, AllowCrossZoneSpellsOnPets, false, "Set to true to allow cross z
|
|||||||
RULE_BOOL(Zone, ZoneShardQuestMenuOnly, false, "Set to true if you only want quests to show the zone shard menu")
|
RULE_BOOL(Zone, ZoneShardQuestMenuOnly, false, "Set to true if you only want quests to show the zone shard menu")
|
||||||
RULE_BOOL(Zone, StateSaveEntityVariables, true, "Set to true if you want buffs to be saved on shutdown")
|
RULE_BOOL(Zone, StateSaveEntityVariables, true, "Set to true if you want buffs to be saved on shutdown")
|
||||||
RULE_BOOL(Zone, StateSaveBuffs, true, "Set to true if you want buffs to be saved on shutdown")
|
RULE_BOOL(Zone, StateSaveBuffs, true, "Set to true if you want buffs to be saved on shutdown")
|
||||||
|
RULE_INT(Zone, StateSaveClearDays, 7, "Clears state save data older than this many days")
|
||||||
RULE_BOOL(Zone, StateSavingOnShutdown, true, "Set to true if you want zones to save state on shutdown (npcs, corpses, loot, entity variables, buffs etc.)")
|
RULE_BOOL(Zone, StateSavingOnShutdown, true, "Set to true if you want zones to save state on shutdown (npcs, corpses, loot, entity variables, buffs etc.)")
|
||||||
|
RULE_INT(Zone, UpdateWhoTimer, 120, "Seconds between updates to /who list, CLE stale timer")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Map)
|
RULE_CATEGORY(Map)
|
||||||
@@ -386,9 +391,10 @@ RULE_BOOL(Map, MobZVisualDebug, false, "Displays spell effects determining wheth
|
|||||||
RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging")
|
RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging")
|
||||||
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply")
|
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply")
|
||||||
RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position")
|
RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position")
|
||||||
RULE_BOOL(Map, CheckForLoSCheat, false, "Runs predefined zone checks to check for LoS cheating through doors and such.")
|
RULE_BOOL(Map, CheckForDoorLoSCheat, true, "Runs LoS checks to prevent cheating through doors.")
|
||||||
RULE_BOOL(Map, EnableLoSCheatExemptions, false, "Enables exemptions for the LoS Cheat check.")
|
RULE_BOOL(Map, EnableLoSCheatExemptions, false, "Enables exemptions for the LoS Cheat check. Must modify source to create these.")
|
||||||
RULE_REAL(Map, RangeCheckForLoSCheat, 20.0, "Default 20.0. Range to check if one is within range of a door.")
|
RULE_REAL(Map, RangeCheckForDoorLoSCheat, 250.0, "Default 250.0. Range to check if a door is blocking LoS from the target.")
|
||||||
|
RULE_STRING(Map, ZonesToCheckDoorCheat, "89,103", "Zones that will check for the door LoS cheat. You can leave it blank to disable, 'all' to check all zones or use a comma-delimited list of zones. Default Sebilis & Chardok")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Pathing)
|
RULE_CATEGORY(Pathing)
|
||||||
@@ -812,6 +818,7 @@ RULE_INT(Bots, PercentChanceToCastDispel, 75, "The chance for a bot to attempt t
|
|||||||
RULE_INT(Bots, PercentChanceToCastInCombatBuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastInCombatBuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastHateLine, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastHateLine, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
|
RULE_INT(Bots, PercentChanceToCastAEMez, 40, "The chance for a bot to attempt to cast the given spell type in combat. Default 40%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
@@ -823,8 +830,6 @@ RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 500, "The minimum delay in m
|
|||||||
RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2500ms.")
|
RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2500ms.")
|
||||||
RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 1000, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 1000ms.")
|
RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 1000, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 1000ms.")
|
||||||
RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 2500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 2500ms.")
|
RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 2500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 2500ms.")
|
||||||
RULE_INT(Bots, MezChance, 60, "60 Default. Chance for a bot to attempt to Mez a target after validating it is eligible.")
|
|
||||||
RULE_INT(Bots, AEMezChance, 35, "35 Default. Chance for a bot to attempt to AE Mez targets after validating they are eligible.")
|
|
||||||
RULE_INT(Bots, MezSuccessDelay, 2500, "2500 (2.5 sec) Default. Delay between successful Mez attempts.")
|
RULE_INT(Bots, MezSuccessDelay, 2500, "2500 (2.5 sec) Default. Delay between successful Mez attempts.")
|
||||||
RULE_INT(Bots, AEMezSuccessDelay, 5000, "5000 (5 sec) Default. Delay between successful AEMez attempts.")
|
RULE_INT(Bots, AEMezSuccessDelay, 5000, "5000 (5 sec) Default. Delay between successful AEMez attempts.")
|
||||||
RULE_INT(Bots, MezFailDelay, 1250, "1250 (1.25 sec) Default. Delay between failed Mez attempts.")
|
RULE_INT(Bots, MezFailDelay, 1250, "1250 (1.25 sec) Default. Delay between failed Mez attempts.")
|
||||||
@@ -834,14 +839,14 @@ RULE_INT(Bots, MinGroupCureTargets, 3, "Minimum number of targets in valid range
|
|||||||
RULE_INT(Bots, MinTargetsForAESpell, 3, "Minimum number of targets in valid range that are required for an AE spell to cast. Default 3.")
|
RULE_INT(Bots, MinTargetsForAESpell, 3, "Minimum number of targets in valid range that are required for an AE spell to cast. Default 3.")
|
||||||
RULE_INT(Bots, MinTargetsForGroupSpell, 3, "Minimum number of targets in valid range that are required for an group spell to cast. Default 3.")
|
RULE_INT(Bots, MinTargetsForGroupSpell, 3, "Minimum number of targets in valid range that are required for an group spell to cast. Default 3.")
|
||||||
RULE_BOOL(Bots, AllowBuffingHealingFamiliars, false, "Determines if bots are allowed to buff and heal familiars. Default false.")
|
RULE_BOOL(Bots, AllowBuffingHealingFamiliars, false, "Determines if bots are allowed to buff and heal familiars. Default false.")
|
||||||
RULE_BOOL(Bots, RunSpellTypeChecksOnSpawn, false, "This will run a serious of checks on spell types and output errors to LogBotSpellTypeChecks")
|
RULE_BOOL(Bots, RunSpellTypeChecksOnBoot, false, "This will run a series of checks to find potential errors in your bot_spells_entries table on boot and output to LogBotSpellTypeChecks")
|
||||||
RULE_BOOL(Bots, UseParentSpellTypeForChecks, true, "This will check only the parent instead of AE/Group/Pet types (ex: AENukes/AERains/PBAENukes fall under Nukes or PetBuffs fall under buffs) when RunSpellTypeChecksOnSpawn fires")
|
RULE_BOOL(Bots, UseParentSpellTypeForChecks, true, "This will check only the parent instead of AE/Group/Pet types (ex: AENukes/AERains/PBAENukes fall under Nukes or PetBuffs fall under buffs) when RunSpellTypeChecksOnSpawn fires")
|
||||||
RULE_BOOL(Bots, AllowForcedCastsBySpellID, true, "If enabled, players can use ^cast spellid # to cast a specific spell by ID that is in their spell list")
|
RULE_BOOL(Bots, AllowForcedCastsBySpellID, true, "If enabled, players can use ^cast spellid # to cast a specific spell by ID that is in their spell list")
|
||||||
RULE_BOOL(Bots, AllowCastAAs, true, "If enabled, players can use ^cast aa to cast a clickable AA")
|
RULE_BOOL(Bots, AllowCastAAs, true, "If enabled, players can use ^cast aa to cast a clickable AA")
|
||||||
RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summon their epic pets following the rules AllowMagicianEpicPetLevel")
|
RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summon their epic pets following the rules AllowMagicianEpicPetLevel")
|
||||||
RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level")
|
RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level")
|
||||||
RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement")
|
RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement")
|
||||||
RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their spell list to cast.")
|
RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their usable spell list to cast. Empty uses Manifest Elements - 'SumMageMultiElement'")
|
||||||
RULE_INT(Bots, ReclaimEnergySpellID, 331, "Spell ID for reclaim energy when using ^petsettype. Default 331")
|
RULE_INT(Bots, ReclaimEnergySpellID, 331, "Spell ID for reclaim energy when using ^petsettype. Default 331")
|
||||||
RULE_BOOL(Bots, UseSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.")
|
RULE_BOOL(Bots, UseSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.")
|
||||||
RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots")
|
RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots")
|
||||||
@@ -859,8 +864,6 @@ RULE_REAL(Bots, UpperMeleeDistanceMultiplier, 0.55, "Furthest % of the hit box a
|
|||||||
RULE_REAL(Bots, UpperTauntingMeleeDistanceMultiplier, 0.45, "Furthest % of the hit box a taunting melee bot will get from the target. Default 0.45")
|
RULE_REAL(Bots, UpperTauntingMeleeDistanceMultiplier, 0.45, "Furthest % of the hit box a taunting melee bot will get from the target. Default 0.45")
|
||||||
RULE_REAL(Bots, UpperMaxMeleeRangeDistanceMultiplier, 0.95, "Furthest % of the hit box a max melee range melee bot will get from the target. Default 0.95")
|
RULE_REAL(Bots, UpperMaxMeleeRangeDistanceMultiplier, 0.95, "Furthest % of the hit box a max melee range melee bot will get from the target. Default 0.95")
|
||||||
RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "If true, when bots are at max melee distance, special abilities including taunt will be disabled. Default True.")
|
RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "If true, when bots are at max melee distance, special abilities including taunt will be disabled. Default True.")
|
||||||
RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.")
|
|
||||||
RULE_INT(Bots, DistanceTauntingBotsStickMainHate, 10, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.")
|
|
||||||
RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.")
|
RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.")
|
||||||
RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.")
|
RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.")
|
||||||
RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.")
|
RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.")
|
||||||
@@ -901,6 +904,7 @@ RULE_STRING(Bots, ZonesWithForcedSpawnLimits, "", "Comma-delimited list of zones
|
|||||||
RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spawn limits for zones.")
|
RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spawn limits for zones.")
|
||||||
RULE_INT(Bots, AICastSpellTypeDelay, 100, "Delay in milliseconds between AI cast attempts for each spell type. Default 100ms")
|
RULE_INT(Bots, AICastSpellTypeDelay, 100, "Delay in milliseconds between AI cast attempts for each spell type. Default 100ms")
|
||||||
RULE_INT(Bots, AICastSpellTypeHeldDelay, 2500, "Delay in milliseconds between AI cast attempts for each spell type that is held or disabled. Default 2500ms (2.5s)")
|
RULE_INT(Bots, AICastSpellTypeHeldDelay, 2500, "Delay in milliseconds between AI cast attempts for each spell type that is held or disabled. Default 2500ms (2.5s)")
|
||||||
|
RULE_BOOL(Bots, BotsRequireLoS, true, "Whether or not bots require line of sight to be told to attack their target")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Chat)
|
RULE_CATEGORY(Chat)
|
||||||
@@ -1092,6 +1096,7 @@ RULE_CATEGORY(Instances)
|
|||||||
RULE_INT(Instances, ReservedInstances, 100, "Number of instance IDs which are reserved for globals. This value should not be changed while a server is running")
|
RULE_INT(Instances, ReservedInstances, 100, "Number of instance IDs which are reserved for globals. This value should not be changed while a server is running")
|
||||||
RULE_BOOL(Instances, RecycleInstanceIds, true, "Setting whether free instance IDs should be recycled to prevent them from gradually running out at 32k")
|
RULE_BOOL(Instances, RecycleInstanceIds, true, "Setting whether free instance IDs should be recycled to prevent them from gradually running out at 32k")
|
||||||
RULE_INT(Instances, GuildHallExpirationDays, 90, "Amount of days before a Guild Hall instance expires")
|
RULE_INT(Instances, GuildHallExpirationDays, 90, "Amount of days before a Guild Hall instance expires")
|
||||||
|
RULE_INT(Instances, ExpireOffsetTimeSeconds, 3600, "Amount of seconds to beyond instance expiration time we wait to purge the entry from the database. (Default: 1 Hour)")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Expedition)
|
RULE_CATEGORY(Expedition)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace ServerReload {
|
|||||||
LevelEXPMods,
|
LevelEXPMods,
|
||||||
Logs,
|
Logs,
|
||||||
Loot,
|
Loot,
|
||||||
|
Maps,
|
||||||
Merchants,
|
Merchants,
|
||||||
NPCEmotes,
|
NPCEmotes,
|
||||||
NPCSpells,
|
NPCSpells,
|
||||||
@@ -61,6 +62,7 @@ namespace ServerReload {
|
|||||||
"Level EXP Mods",
|
"Level EXP Mods",
|
||||||
"Logs",
|
"Logs",
|
||||||
"Loot",
|
"Loot",
|
||||||
|
"Maps",
|
||||||
"Merchants",
|
"Merchants",
|
||||||
"NPC Emotes",
|
"NPC Emotes",
|
||||||
"NPC Spells",
|
"NPC Spells",
|
||||||
|
|||||||
+19
-19
@@ -1470,9 +1470,10 @@ bool IsFastHealSpell(uint16 spell_id) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id && IsValidSpell(spell_id)) {
|
if (IsValidSpell(spell_id)) {
|
||||||
if (
|
if (
|
||||||
spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME &&
|
spell_id != SPELL_MINOR_HEALING &&
|
||||||
|
(spells[spell_id].cast_time > MAX_VERY_FAST_HEAL_CASTING_TIME && spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME) &&
|
||||||
spells[spell_id].good_effect &&
|
spells[spell_id].good_effect &&
|
||||||
!IsGroupSpell(spell_id)
|
!IsGroupSpell(spell_id)
|
||||||
) {
|
) {
|
||||||
@@ -1509,8 +1510,9 @@ bool IsVeryFastHealSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id) {
|
if (IsValidSpell(spell_id)) {
|
||||||
if (
|
if (
|
||||||
|
spell_id != SPELL_MINOR_HEALING &&
|
||||||
spells[spell_id].cast_time <= MAX_VERY_FAST_HEAL_CASTING_TIME &&
|
spells[spell_id].cast_time <= MAX_VERY_FAST_HEAL_CASTING_TIME &&
|
||||||
spells[spell_id].good_effect &&
|
spells[spell_id].good_effect &&
|
||||||
!IsGroupSpell(spell_id)
|
!IsGroupSpell(spell_id)
|
||||||
@@ -1548,8 +1550,13 @@ bool IsRegularSingleTargetHealSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id) {
|
if (IsValidSpell(spell_id)) {
|
||||||
|
if (spell_id == SPELL_MINOR_HEALING) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
spells[spell_id].cast_time > MAX_FAST_HEAL_CASTING_TIME &&
|
||||||
spells[spell_id].target_type == ST_Target &&
|
spells[spell_id].target_type == ST_Target &&
|
||||||
!IsCompleteHealSpell(spell_id) &&
|
!IsCompleteHealSpell(spell_id) &&
|
||||||
!IsHealOverTimeSpell(spell_id) &&
|
!IsHealOverTimeSpell(spell_id) &&
|
||||||
@@ -1589,9 +1596,14 @@ bool IsRegularPetHealSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id && IsValidSpell(spell_id)) {
|
if (IsValidSpell(spell_id)) {
|
||||||
|
if (spell_id == SPELL_MINOR_HEALING) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_Undead) &&
|
spells[spell_id].cast_time > MAX_FAST_HEAL_CASTING_TIME &&
|
||||||
|
(spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_SummonedPet) &&
|
||||||
!IsCompleteHealSpell(spell_id) &&
|
!IsCompleteHealSpell(spell_id) &&
|
||||||
!IsHealOverTimeSpell(spell_id) &&
|
!IsHealOverTimeSpell(spell_id) &&
|
||||||
!IsGroupSpell(spell_id)
|
!IsGroupSpell(spell_id)
|
||||||
@@ -1630,7 +1642,7 @@ bool IsRegularGroupHealSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id) {
|
if (IsValidSpell(spell_id)) {
|
||||||
if (
|
if (
|
||||||
IsGroupSpell(spell_id) &&
|
IsGroupSpell(spell_id) &&
|
||||||
!IsCompleteHealSpell(spell_id) &&
|
!IsCompleteHealSpell(spell_id) &&
|
||||||
@@ -2796,18 +2808,6 @@ bool IsLichSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidSpellAndLoS(uint32 spell_id, bool has_los) {
|
|
||||||
if (!IsValidSpell(spell_id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_los && IsTargetRequiredForSpell(spell_id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsInstantHealSpell(uint32 spell_id) {
|
bool IsInstantHealSpell(uint32 spell_id) {
|
||||||
if (!IsValidSpell(spell_id)) {
|
if (!IsValidSpell(spell_id)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+5
-3
@@ -215,6 +215,7 @@
|
|||||||
#define SPELL_AMPLIFICATION 2603
|
#define SPELL_AMPLIFICATION 2603
|
||||||
#define SPELL_DIVINE_REZ 2738
|
#define SPELL_DIVINE_REZ 2738
|
||||||
#define SPELL_NATURES_RECOVERY 2520
|
#define SPELL_NATURES_RECOVERY 2520
|
||||||
|
#define SPELL_MINOR_HEALING 200
|
||||||
#define SPELL_ADRENALINE_SWELL 14445
|
#define SPELL_ADRENALINE_SWELL 14445
|
||||||
#define SPELL_ADRENALINE_SWELL_RK2 14446
|
#define SPELL_ADRENALINE_SWELL_RK2 14446
|
||||||
#define SPELL_ADRENALINE_SWELL_RK3 14447
|
#define SPELL_ADRENALINE_SWELL_RK3 14447
|
||||||
@@ -899,8 +900,8 @@ const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellTy
|
|||||||
const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root);
|
const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root);
|
||||||
|
|
||||||
// Bot related functions
|
// Bot related functions
|
||||||
bool IsBotSpellTypeDetrimental (uint16 spell_type);
|
bool IsBotSpellTypeDetrimental(uint16 spell_type);
|
||||||
bool IsBotSpellTypeBeneficial (uint16 spell_type);
|
bool IsBotSpellTypeBeneficial(uint16 spell_type);
|
||||||
bool BotSpellTypeUsesTargetSettings(uint16 spell_type);
|
bool BotSpellTypeUsesTargetSettings(uint16 spell_type);
|
||||||
bool IsBotSpellTypeInnate (uint16 spell_type);
|
bool IsBotSpellTypeInnate (uint16 spell_type);
|
||||||
bool IsAEBotSpellType(uint16 spell_type);
|
bool IsAEBotSpellType(uint16 spell_type);
|
||||||
@@ -916,6 +917,8 @@ bool IsCommandedBotSpellType(uint16 spell_type);
|
|||||||
bool IsPullingBotSpellType(uint16 spell_type);
|
bool IsPullingBotSpellType(uint16 spell_type);
|
||||||
uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id);
|
uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id);
|
||||||
uint16 GetPetBotSpellType(uint16 spell_type);
|
uint16 GetPetBotSpellType(uint16 spell_type);
|
||||||
|
bool IsBotBuffSpellType(uint16 spell_type);
|
||||||
|
bool BotRequiresLoSToCast(uint16 spell_type, uint16 spell_id);
|
||||||
|
|
||||||
// These should not be used to determine spell category..
|
// These should not be used to determine spell category..
|
||||||
// They are a graphical affects (effects?) index only
|
// They are a graphical affects (effects?) index only
|
||||||
@@ -1813,7 +1816,6 @@ bool IsEffectInSpell(uint16 spell_id, int effect_id);
|
|||||||
uint16 GetSpellTriggerSpellID(uint16 spell_id, int effect_id);
|
uint16 GetSpellTriggerSpellID(uint16 spell_id, int effect_id);
|
||||||
bool IsBlankSpellEffect(uint16 spell_id, int effect_index);
|
bool IsBlankSpellEffect(uint16 spell_id, int effect_index);
|
||||||
bool IsValidSpell(uint32 spell_id);
|
bool IsValidSpell(uint32 spell_id);
|
||||||
bool IsValidSpellAndLoS(uint32 spell_id, bool has_los = true);
|
|
||||||
bool IsSummonSpell(uint16 spell_id);
|
bool IsSummonSpell(uint16 spell_id);
|
||||||
bool IsDamageSpell(uint16 spell_id);
|
bool IsDamageSpell(uint16 spell_id);
|
||||||
bool IsAnyDamageSpell(uint16 spell_id);
|
bool IsAnyDamageSpell(uint16 spell_id);
|
||||||
|
|||||||
+42
-254
@@ -1,4 +1,5 @@
|
|||||||
#include "spdat.h"
|
#include "spdat.h"
|
||||||
|
#include "../zone/bot.h"
|
||||||
|
|
||||||
bool IsBotSpellTypeDetrimental(uint16 spell_type) {
|
bool IsBotSpellTypeDetrimental(uint16 spell_type) {
|
||||||
switch (spell_type) {
|
switch (spell_type) {
|
||||||
@@ -417,264 +418,23 @@ uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id) {
|
|||||||
return UINT16_MAX;
|
return UINT16_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 correct_type = UINT16_MAX;
|
uint16 correct_type = spell_type;
|
||||||
SPDat_Spell_Struct spell = spells[spell_id];
|
|
||||||
std::string teleport_zone = spell.teleport_zone;
|
|
||||||
|
|
||||||
if (IsCharmSpell(spell_id)) {
|
if (!Bot::IsValidSpellTypeBySpellID(spell_type, spell_id)) {
|
||||||
correct_type = BotSpellTypes::Charm;
|
correct_type = UINT16_MAX;
|
||||||
}
|
|
||||||
else if (IsFearSpell(spell_id)) {
|
auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START });
|
||||||
correct_type = BotSpellTypes::Fear;
|
auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END });
|
||||||
}
|
|
||||||
else if (IsEffectInSpell(spell_id, SE_Revive)) {
|
for (int i = end; i >= start; --i) {
|
||||||
correct_type = BotSpellTypes::Resurrect;
|
if (!Bot::IsValidBotSpellType(i) || i == BotSpellTypes::InCombatBuff) {
|
||||||
}
|
continue;
|
||||||
else if (IsHarmonySpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Lull;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
teleport_zone.compare("") &&
|
|
||||||
!IsEffectInSpell(spell_id, SE_GateToHomeCity) &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Teleport;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_Succor)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Succor;
|
|
||||||
}
|
|
||||||
else if (IsEffectInSpell(spell_id, SE_BindAffinity)) {
|
|
||||||
correct_type = BotSpellTypes::BindAffinity;
|
|
||||||
}
|
|
||||||
else if (IsEffectInSpell(spell_id, SE_Identify)) {
|
|
||||||
correct_type = BotSpellTypes::Identify;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::Levitate &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_Levitate)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Levitate;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::Rune &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Rune;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::WaterBreathing &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_WaterBreathing)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::WaterBreathing;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::Size &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Size;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::Invisibility &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Invisibility;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::MovementSpeed &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_MovementSpeed)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::MovementSpeed;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
!teleport_zone.compare("") &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_Translocate) || IsEffectInSpell(spell_id, SE_GateToHomeCity))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::SendHome;
|
|
||||||
}
|
|
||||||
else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) {
|
|
||||||
correct_type = BotSpellTypes::SummonCorpse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (correct_type == UINT16_MAX) {
|
if (Bot::IsValidSpellTypeBySpellID(i, spell_id)) {
|
||||||
if (
|
correct_type = i;
|
||||||
IsSummonPetSpell(spell_id) ||
|
|
||||||
IsEffectInSpell(spell_id, SE_TemporaryPets)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Pet;
|
|
||||||
}
|
|
||||||
else if (IsMesmerizeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Mez;
|
|
||||||
}
|
|
||||||
else if (IsEscapeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Escape;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_Root)
|
|
||||||
) {
|
|
||||||
if (IsAnyAESpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::AERoot;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
correct_type = BotSpellTypes::Root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsLifetapSpell(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Lifetap;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_MovementSpeed)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Snare;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
(IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::DOT;
|
|
||||||
}
|
|
||||||
else if (IsDispelSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Dispel;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsSlowSpell(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Slow;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDebuffSpell(spell_id) &&
|
|
||||||
!IsHateReduxSpell(spell_id) &&
|
|
||||||
!IsHateSpell(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Debuff;
|
|
||||||
}
|
|
||||||
else if (IsHateReduxSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::HateRedux;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsHateSpell(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::HateLine;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsBuffSpell(spell_id) &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsBardSong(spell_id)
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
spell_type == BotSpellTypes::InCombatBuffSong ||
|
|
||||||
spell_type == BotSpellTypes::OutOfCombatBuffSong ||
|
|
||||||
spell_type == BotSpellTypes::PreCombatBuffSong
|
|
||||||
) {
|
|
||||||
correct_type = spell_type;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
correct_type = BotSpellTypes::OutOfCombatBuffSong;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
!IsBardSong(spell_id) &&
|
|
||||||
(
|
|
||||||
(IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) ||
|
|
||||||
(spell_type == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id))
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::InCombatBuff;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::PreCombatBuff &&
|
|
||||||
IsAnyBuffSpell(spell_id) &&
|
|
||||||
!IsBardSong(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::PreCombatBuff;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
(IsCureSpell(spell_id) && spell_type == BotSpellTypes::Cure) ||
|
|
||||||
(IsCureSpell(spell_id) && !IsAnyHealSpell(spell_id))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Cure;
|
|
||||||
}
|
|
||||||
else if (IsAnyNukeOrStunSpell(spell_id)) {
|
|
||||||
if (IsAnyAESpell(spell_id)) {
|
|
||||||
if (IsAERainSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::AERains;
|
|
||||||
}
|
|
||||||
else if (IsPBAENukeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::PBAENuke;
|
|
||||||
}
|
|
||||||
else if (IsStunSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::AEStun;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
correct_type = BotSpellTypes::AENukes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsStunSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Stun;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
correct_type = BotSpellTypes::Nuke;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsAnyHealSpell(spell_id)) {
|
|
||||||
if (IsGroupSpell(spell_id)) {
|
|
||||||
if (IsGroupCompleteHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::GroupCompleteHeals;
|
|
||||||
}
|
|
||||||
else if (IsGroupHealOverTimeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::GroupHoTHeals;
|
|
||||||
}
|
|
||||||
else if (IsRegularGroupHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::GroupHeals;
|
|
||||||
}
|
|
||||||
|
|
||||||
return correct_type;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if (IsVeryFastHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::VeryFastHeals;
|
|
||||||
}
|
|
||||||
else if (IsFastHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::FastHeals;
|
|
||||||
}
|
|
||||||
else if (IsCompleteHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::CompleteHeal;
|
|
||||||
}
|
|
||||||
else if (IsHealOverTimeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::HoTHeals;
|
|
||||||
}
|
|
||||||
else if (IsRegularSingleTargetHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::RegularHeal;
|
|
||||||
}
|
|
||||||
else if (IsRegularPetHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::RegularHeal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsAnyBuffSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Buff;
|
|
||||||
|
|
||||||
if (IsResistanceOnlySpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::ResistBuffs;
|
|
||||||
}
|
|
||||||
else if (IsDamageShieldOnlySpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::DamageShields;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -708,3 +468,31 @@ uint16 GetPetBotSpellType(uint16 spell_type) {
|
|||||||
|
|
||||||
return spell_type;
|
return spell_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsBotBuffSpellType(uint16 spell_type) {
|
||||||
|
switch (spell_type) {
|
||||||
|
case BotSpellTypes::Buff:
|
||||||
|
case BotSpellTypes::PetBuffs:
|
||||||
|
case BotSpellTypes::ResistBuffs:
|
||||||
|
case BotSpellTypes::PetResistBuffs:
|
||||||
|
case BotSpellTypes::DamageShields:
|
||||||
|
case BotSpellTypes::PetDamageShields:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BotRequiresLoSToCast(uint16 spell_type, uint16 spell_id) {
|
||||||
|
if (!BotSpellTypeRequiresTarget(spell_type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsTargetRequiredForSpell(spell_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -196,3 +196,25 @@ const uint32 Timer::SetCurrentTime()
|
|||||||
return current_time;
|
return current_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint32 Timer::RollForward(uint32 seconds)
|
||||||
|
{
|
||||||
|
struct timeval read_time{};
|
||||||
|
uint32 this_time;
|
||||||
|
|
||||||
|
gettimeofday(&read_time, nullptr);
|
||||||
|
this_time = read_time.tv_sec * 1000 + read_time.tv_usec / 1000;
|
||||||
|
|
||||||
|
if (last_time == 0) {
|
||||||
|
current_time = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
current_time += this_time - last_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_time = this_time;
|
||||||
|
|
||||||
|
// Roll forward the specified number of seconds (converted to milliseconds)
|
||||||
|
current_time += seconds * 1000;
|
||||||
|
|
||||||
|
return current_time;
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ public:
|
|||||||
inline uint32 GetDuration() { return(timer_time); }
|
inline uint32 GetDuration() { return(timer_time); }
|
||||||
|
|
||||||
static const uint32 SetCurrentTime();
|
static const uint32 SetCurrentTime();
|
||||||
|
static const uint32 RollForward(uint32 seconds);
|
||||||
static const uint32 GetCurrentTime();
|
static const uint32 GetCurrentTime();
|
||||||
static const uint32 GetTimeSeconds();
|
static const uint32 GetTimeSeconds();
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
// Build variables
|
// Build variables
|
||||||
// these get injected during the build pipeline
|
// these get injected during the build pipeline
|
||||||
#define CURRENT_VERSION "23.3.2-dev" // always append -dev to the current version for custom-builds
|
#define CURRENT_VERSION "23.4.0-dev" // always append -dev to the current version for custom-builds
|
||||||
#define LOGIN_VERSION "0.8.0"
|
#define LOGIN_VERSION "0.8.0"
|
||||||
#define COMPILE_DATE __DATE__
|
#define COMPILE_DATE __DATE__
|
||||||
#define COMPILE_TIME __TIME__
|
#define COMPILE_TIME __TIME__
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define CURRENT_BINARY_DATABASE_VERSION 9313
|
#define CURRENT_BINARY_DATABASE_VERSION 9321
|
||||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
|
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "eqemu-server",
|
"name": "eqemu-server",
|
||||||
"version": "23.3.2",
|
"version": "23.4.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/EQEmu/Server.git"
|
"url": "https://github.com/EQEmu/Server.git"
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ int main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (player_event_process_timer.Check()) {
|
if (player_event_process_timer.Check()) {
|
||||||
std::jthread player_event_thread(&PlayerEventLogs::Process, &player_event_logs);
|
player_event_logs.Process();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -177,7 +177,7 @@ void ChatChannelList::SendAllChannels(Client *c) {
|
|||||||
|
|
||||||
std::string Message;
|
std::string Message;
|
||||||
|
|
||||||
char CountString[10];
|
char CountString[13];
|
||||||
|
|
||||||
while(iterator.MoreElements()) {
|
while(iterator.MoreElements()) {
|
||||||
|
|
||||||
@@ -408,7 +408,7 @@ void ChatChannel::SendChannelMembers(Client *c) {
|
|||||||
|
|
||||||
if(!c) return;
|
if(!c) return;
|
||||||
|
|
||||||
char CountString[10];
|
char CountString[13];
|
||||||
|
|
||||||
sprintf(CountString, "(%i)", MemberCount(c->GetAccountStatus()));
|
sprintf(CountString, "(%i)", MemberCount(c->GetAccountStatus()));
|
||||||
|
|
||||||
|
|||||||
@@ -746,3 +746,6 @@ OP_TradeSkillRecipeInspect=0x4f7e
|
|||||||
OP_InvokeChangePetNameImmediate=0x046d
|
OP_InvokeChangePetNameImmediate=0x046d
|
||||||
OP_InvokeChangePetName=0x4506
|
OP_InvokeChangePetName=0x4506
|
||||||
OP_ChangePetName=0x5dab
|
OP_ChangePetName=0x5dab
|
||||||
|
|
||||||
|
OP_InvokeNameChangeImmediate=0x4fe2
|
||||||
|
OP_InvokeNameChangeLazy=0x2f2e
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ echo "# Running NPC hand-in tests"
|
|||||||
./bin/zone tests:npc-handins 2>&1 | tee test_output.log
|
./bin/zone tests:npc-handins 2>&1 | tee test_output.log
|
||||||
./bin/zone tests:npc-handins-multiquest 2>&1 | tee -a test_output.log
|
./bin/zone tests:npc-handins-multiquest 2>&1 | tee -a test_output.log
|
||||||
./bin/zone tests:databuckets 2>&1 | tee -a test_output.log
|
./bin/zone tests:databuckets 2>&1 | tee -a test_output.log
|
||||||
|
./bin/zone tests:zone-state 2>&1 | tee -a test_output.log
|
||||||
|
|
||||||
if grep -E -q "QueryErr|Error|FAILED" test_output.log; then
|
if grep -E -q "QueryErr|Error|FAILED" test_output.log; then
|
||||||
echo "Error found in test output! Failing build."
|
echo "Error found in test output! Failing build."
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/golang/protobuf v1.3.2 // indirect
|
github.com/golang/protobuf v1.3.2 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.35.0 // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
golang.org/x/net v0.36.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD
|
|||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
|||||||
@@ -1850,3 +1850,72 @@ std::map<uint32, ClientListEntry *> ClientList::GetGuildClientsWithTributeOptIn(
|
|||||||
}
|
}
|
||||||
return guild_members;
|
return guild_members;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
std::vector<uint32_t> ClientList::GetGuildZoneServers(uint32 guild_id)
|
||||||
|
{
|
||||||
|
std::vector<uint32_t> zone_server_ids;
|
||||||
|
std::unordered_set<uint32_t> seen_ids;
|
||||||
|
|
||||||
|
LinkedListIterator<ClientListEntry *> iterator(clientlist);
|
||||||
|
|
||||||
|
iterator.Reset();
|
||||||
|
while (iterator.MoreElements()) {
|
||||||
|
ClientListEntry *cle = iterator.GetData();
|
||||||
|
|
||||||
|
if (cle->Online() != CLE_Status::InZone) {
|
||||||
|
iterator.Advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cle->Server()) {
|
||||||
|
iterator.Advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cle->GuildID() == guild_id) {
|
||||||
|
uint32_t id = cle->Server()->GetID();
|
||||||
|
if (seen_ids.insert(id).second) {
|
||||||
|
zone_server_ids.emplace_back(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator.Advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
return zone_server_ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> ClientList::GetZoneServersWithGMs()
|
||||||
|
{
|
||||||
|
std::vector<uint32_t> zone_server_ids;
|
||||||
|
std::unordered_set<uint32_t> seen_ids;
|
||||||
|
LinkedListIterator<ClientListEntry *> iterator(clientlist);
|
||||||
|
|
||||||
|
iterator.Reset();
|
||||||
|
while (iterator.MoreElements()) {
|
||||||
|
ClientListEntry *cle = iterator.GetData();
|
||||||
|
|
||||||
|
if (cle->Online() != CLE_Status::InZone) {
|
||||||
|
iterator.Advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cle->Server()) {
|
||||||
|
iterator.Advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cle->Admin() > 0) {
|
||||||
|
uint32_t id = cle->Server()->GetID();
|
||||||
|
if (seen_ids.insert(id).second) {
|
||||||
|
zone_server_ids.emplace_back(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator.Advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
return zone_server_ids;
|
||||||
|
}
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ public:
|
|||||||
void CLCheckStale();
|
void CLCheckStale();
|
||||||
void CLEKeepAlive(uint32 numupdates, uint32* wid);
|
void CLEKeepAlive(uint32 numupdates, uint32* wid);
|
||||||
void CLEAdd(uint32 login_server_id, const char* login_server_name, const char* login_name, const char* login_key, int16 world_admin = AccountStatus::Player, uint32 ip_address = 0, uint8 is_local=0);
|
void CLEAdd(uint32 login_server_id, const char* login_server_name, const char* login_name, const char* login_key, int16 world_admin = AccountStatus::Player, uint32 ip_address = 0, uint8 is_local=0);
|
||||||
|
std::vector<uint32_t> GetGuildZoneServers(uint32 guild_id);
|
||||||
|
std::vector<uint32_t> GetZoneServersWithGMs();
|
||||||
void UpdateClientGuild(uint32 char_id, uint32 guild_id);
|
void UpdateClientGuild(uint32 char_id, uint32 guild_id);
|
||||||
bool IsAccountInGame(uint32 iLSID);
|
bool IsAccountInGame(uint32 iLSID);
|
||||||
|
|
||||||
|
|||||||
@@ -137,8 +137,11 @@ void DynamicZone::SetSecondsRemaining(uint32_t seconds_remaining)
|
|||||||
m_expire_time = now + new_remaining;
|
m_expire_time = now + new_remaining;
|
||||||
m_duration = std::chrono::duration_cast<std::chrono::seconds>(m_expire_time - m_start_time);
|
m_duration = std::chrono::duration_cast<std::chrono::seconds>(m_expire_time - m_start_time);
|
||||||
|
|
||||||
InstanceListRepository::UpdateDuration(database,
|
InstanceListRepository::UpdateDuration(
|
||||||
GetInstanceID(), static_cast<uint32_t>(m_duration.count()));
|
database,
|
||||||
|
GetInstanceID(),
|
||||||
|
static_cast<uint32_t>(m_duration.count())
|
||||||
|
);
|
||||||
|
|
||||||
SendZonesDurationUpdate(); // update zone caches and actual instance's timer
|
SendZonesDurationUpdate(); // update zone caches and actual instance's timer
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ void callGetZoneList(Json::Value &response)
|
|||||||
row["client_address"] = zone->GetCAddress();
|
row["client_address"] = zone->GetCAddress();
|
||||||
row["client_local_address"] = zone->GetCLocalAddress();
|
row["client_local_address"] = zone->GetCLocalAddress();
|
||||||
row["client_port"] = zone->GetCPort();
|
row["client_port"] = zone->GetCPort();
|
||||||
|
row["compile_version"] = zone->GetCurrentVersion();
|
||||||
|
row["compile_date"] = zone->GetCompileDate();
|
||||||
row["compile_time"] = zone->GetCompileTime();
|
row["compile_time"] = zone->GetCompileTime();
|
||||||
row["id"] = zone->GetID();
|
row["id"] = zone->GetID();
|
||||||
row["instance_id"] = zone->GetInstanceID();
|
row["instance_id"] = zone->GetInstanceID();
|
||||||
|
|||||||
+11
-5
@@ -381,11 +381,19 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
Timer player_event_process_timer(1000);
|
|
||||||
if (player_event_logs.LoadDatabaseConnection()) {
|
if (player_event_logs.LoadDatabaseConnection()) {
|
||||||
player_event_logs.Init();
|
player_event_logs.Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto event_log_processor = std::jthread([](const std::stop_token& stoken) {
|
||||||
|
while (!stoken.stop_requested()) {
|
||||||
|
if (!RuleB(Logging, PlayerEventsQSProcess)) {
|
||||||
|
player_event_logs.Process();
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
auto loop_fn = [&](EQ::Timer* t) {
|
auto loop_fn = [&](EQ::Timer* t) {
|
||||||
Timer::SetCurrentTime();
|
Timer::SetCurrentTime();
|
||||||
|
|
||||||
@@ -448,10 +456,6 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player_event_process_timer.Check()) {
|
|
||||||
std::jthread event_thread(&PlayerEventLogs::Process, &player_event_logs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PurgeInstanceTimer.Check()) {
|
if (PurgeInstanceTimer.Check()) {
|
||||||
database.PurgeExpiredInstances();
|
database.PurgeExpiredInstances();
|
||||||
database.PurgeAllDeletedDataBuckets();
|
database.PurgeAllDeletedDataBuckets();
|
||||||
@@ -502,6 +506,8 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
EQ::EventLoop::Get().Run();
|
EQ::EventLoop::Get().Run();
|
||||||
|
|
||||||
|
event_log_processor.request_stop();
|
||||||
|
|
||||||
LogInfo("World main loop completed");
|
LogInfo("World main loop completed");
|
||||||
LogInfo("Shutting down zone connections (if any)");
|
LogInfo("Shutting down zone connections (if any)");
|
||||||
zoneserver_list.KillAll();
|
zoneserver_list.KillAll();
|
||||||
|
|||||||
+20
-15
@@ -50,7 +50,7 @@ void WorldGuildManager::SendGuildRefresh(uint32 guild_id, bool name, bool motd,
|
|||||||
s->motd_change = motd;
|
s->motd_change = motd;
|
||||||
s->rank_change = rank;
|
s->rank_change = rank;
|
||||||
s->relation_change = relation;
|
s->relation_change = relation;
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(guild_id, pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ void WorldGuildManager::SendCharRefresh(uint32 old_guild_id, uint32 guild_id, ui
|
|||||||
s->guild_id = guild_id;
|
s->guild_id = guild_id;
|
||||||
s->old_guild_id = old_guild_id;
|
s->old_guild_id = old_guild_id;
|
||||||
s->char_id = charid;
|
s->char_id = charid;
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(guild_id, pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ void WorldGuildManager::SendGuildDelete(uint32 guild_id) {
|
|||||||
auto pack = new ServerPacket(ServerOP_DeleteGuild, sizeof(ServerGuildID_Struct));
|
auto pack = new ServerPacket(ServerOP_DeleteGuild, sizeof(ServerGuildID_Struct));
|
||||||
ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer;
|
ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer;
|
||||||
s->guild_id = guild_id;
|
s->guild_id = guild_id;
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(guild_id, pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,15 +85,14 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer;
|
ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer;
|
||||||
LogGuilds("Received and broadcasting guild refresh for [{}], changes: name=[{}], motd=[{}], rank=d, relation=[{}]", s->guild_id, s->name_change, s->motd_change, s->rank_change, s->relation_change);
|
LogGuilds("Received and broadcasting guild refresh for [{}], changes: name=[{}], motd=[{}], rank=d, relation=[{}]", s->guild_id, s->name_change, s->motd_change, s->rank_change, s->relation_change);
|
||||||
|
|
||||||
//broadcast this packet to all zones.
|
|
||||||
zoneserver_list.SendPacket(pack);
|
|
||||||
|
|
||||||
//preform a local refresh.
|
//preform a local refresh.
|
||||||
if(!RefreshGuild(s->guild_id)) {
|
if(!RefreshGuild(s->guild_id)) {
|
||||||
LogGuilds("Unable to preform local refresh on guild [{}]", s->guild_id);
|
BaseGuildManager::RefreshGuild(s->guild_id);
|
||||||
//can we do anything?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//broadcast this packet to all zones.
|
||||||
|
zoneserver_list.SendPacketToZonesWithGuild(s->guild_id, pack);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +107,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
|
|
||||||
//broadcast this update to any zone with a member in this guild.
|
//broadcast this update to any zone with a member in this guild.
|
||||||
//because im sick of this not working, sending it to all zones, just spends a bit more bandwidth.
|
//because im sick of this not working, sending it to all zones, just spends a bit more bandwidth.
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(s->guild_id, pack);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -147,7 +146,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
auto s = (ServerGuildID_Struct *)pack->pBuffer;
|
auto s = (ServerGuildID_Struct *)pack->pBuffer;
|
||||||
RefreshGuild(s->guild_id);
|
RefreshGuild(s->guild_id);
|
||||||
|
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(s->guild_id, pack);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ServerOP_GuildPermissionUpdate:
|
case ServerOP_GuildPermissionUpdate:
|
||||||
@@ -179,7 +178,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
sg->function_value
|
sg->function_value
|
||||||
);
|
);
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(sg->guild_id, pack);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LogError("World Received ServerOP_GuildPermissionUpdate for guild [{}] function id {} with value of {} but guild could not be found.",
|
LogError("World Received ServerOP_GuildPermissionUpdate for guild [{}] function id {} with value of {} but guild could not be found.",
|
||||||
@@ -213,7 +212,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
rnc->rank,
|
rnc->rank,
|
||||||
rnc->rank_name
|
rnc->rank_name
|
||||||
);
|
);
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(rnc->guild_id, pack);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LogError("World Received ServerOP_GuildRankNameChange from zone for guild [{}] rank id {} with new name of {} but could not find guild.",
|
LogError("World Received ServerOP_GuildRankNameChange from zone for guild [{}] rank id {} with new name of {} but could not find guild.",
|
||||||
@@ -230,10 +229,10 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
case ServerOP_GuildChannel:
|
case ServerOP_GuildChannel:
|
||||||
case ServerOP_GuildURL:
|
case ServerOP_GuildURL:
|
||||||
case ServerOP_GuildMemberRemove:
|
case ServerOP_GuildMemberRemove:
|
||||||
case ServerOP_GuildSendGuildList:
|
|
||||||
case ServerOP_GuildMembersList:
|
case ServerOP_GuildMembersList:
|
||||||
{
|
{
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
auto in = (ServerOP_GuildMessage_Struct *) pack->pBuffer;
|
||||||
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, pack);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ServerOP_GuildMemberAdd:
|
case ServerOP_GuildMemberAdd:
|
||||||
@@ -244,9 +243,15 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
BaseGuildManager::RefreshGuild(in->guild_id);
|
BaseGuildManager::RefreshGuild(in->guild_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, pack);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ServerOP_GuildSendGuildList: {
|
||||||
|
auto in = (ServerOP_GuildMessage_Struct *) pack->pBuffer;
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToBootedZones(pack);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LogGuilds("Unknown packet {:#04x} received from zone??", pack->opcode);
|
LogGuilds("Unknown packet {:#04x} received from zone??", pack->opcode);
|
||||||
break;
|
break;
|
||||||
@@ -451,6 +456,6 @@ void WorldGuildManager::SendGuildTributeFavorAndTimer(uint32 guild_id, uint32 fa
|
|||||||
data->tribute_timer = time;
|
data->tribute_timer = time;
|
||||||
data->trophy_timer = 0;
|
data->trophy_timer = 0;
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(sp);
|
zoneserver_list.SendPacketToZonesWithGuild(guild_id, sp);
|
||||||
safe_delete(sp)
|
safe_delete(sp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "../common/zone_store.h"
|
#include "../common/zone_store.h"
|
||||||
#include "../common/path_manager.h"
|
#include "../common/path_manager.h"
|
||||||
#include "../common/database/database_update.h"
|
#include "../common/database/database_update.h"
|
||||||
|
#include "../common/repositories/zone_state_spawns_repository.h"
|
||||||
|
|
||||||
extern ZSList zoneserver_list;
|
extern ZSList zoneserver_list;
|
||||||
extern WorldConfig Config;
|
extern WorldConfig Config;
|
||||||
@@ -412,6 +413,11 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
|
|||||||
LogInfo("Cleaning up instance corpses");
|
LogInfo("Cleaning up instance corpses");
|
||||||
database.CleanupInstanceCorpses();
|
database.CleanupInstanceCorpses();
|
||||||
|
|
||||||
|
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||||
|
ZoneStateSpawnsRepository::PurgeInvalidZoneStates(database);
|
||||||
|
ZoneStateSpawnsRepository::PurgeOldZoneStates(database);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+234
-270
@@ -29,6 +29,10 @@
|
|||||||
#include "../common/repositories/inventory_repository.h"
|
#include "../common/repositories/inventory_repository.h"
|
||||||
#include "../common/repositories/criteria/content_filter_criteria.h"
|
#include "../common/repositories/criteria/content_filter_criteria.h"
|
||||||
#include "../common/zone_store.h"
|
#include "../common/zone_store.h"
|
||||||
|
#include "../common/repositories/character_data_repository.h"
|
||||||
|
#include "../common/repositories/character_bind_repository.h"
|
||||||
|
#include "../common/repositories/character_material_repository.h"
|
||||||
|
#include "../common/repositories/start_zones_repository.h"
|
||||||
|
|
||||||
WorldDatabase database;
|
WorldDatabase database;
|
||||||
WorldDatabase content_db;
|
WorldDatabase content_db;
|
||||||
@@ -50,187 +54,177 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o
|
|||||||
character_limit = 8;
|
character_limit = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string character_list_query = fmt::format(
|
auto characters = CharacterDataRepository::GetWhere(
|
||||||
SQL(
|
database,
|
||||||
SELECT
|
fmt::format(
|
||||||
`id`,
|
"`account_id` = {} AND `deleted_at` IS NULL ORDER BY `name` LIMIT {}",
|
||||||
`name`,
|
|
||||||
`gender`,
|
|
||||||
`race`,
|
|
||||||
`class`,
|
|
||||||
`level`,
|
|
||||||
`deity`,
|
|
||||||
`last_login`,
|
|
||||||
`time_played`,
|
|
||||||
`hair_color`,
|
|
||||||
`beard_color`,
|
|
||||||
`eye_color_1`,
|
|
||||||
`eye_color_2`,
|
|
||||||
`hair_style`,
|
|
||||||
`beard`,
|
|
||||||
`face`,
|
|
||||||
`drakkin_heritage`,
|
|
||||||
`drakkin_tattoo`,
|
|
||||||
`drakkin_details`,
|
|
||||||
`zone_id`
|
|
||||||
FROM
|
|
||||||
`character_data`
|
|
||||||
WHERE
|
|
||||||
`account_id` = {}
|
|
||||||
AND
|
|
||||||
`deleted_at` IS NULL
|
|
||||||
ORDER BY `name`
|
|
||||||
LIMIT {}
|
|
||||||
),
|
|
||||||
account_id,
|
account_id,
|
||||||
character_limit
|
character_limit
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
auto results = database.QueryDatabase(character_list_query);
|
size_t character_count = characters.size();
|
||||||
size_t character_count = results.RowCount();
|
if (characters.empty()) {
|
||||||
if (character_count == 0) {
|
|
||||||
*out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
|
*out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
|
||||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer;
|
auto *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer;
|
||||||
cs->CharCount = 0;
|
cs->CharCount = 0;
|
||||||
cs->TotalChars = character_limit;
|
cs->TotalChars = character_limit;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> character_ids;
|
||||||
|
for (auto &e: characters) {
|
||||||
|
character_ids.push_back(e.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& inventories = InventoryRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`character_id` IN ({}) AND `slot_id` BETWEEN {} AND {}",
|
||||||
|
Strings::Join(character_ids, ","),
|
||||||
|
EQ::invslot::slotHead,
|
||||||
|
EQ::invslot::slotFeet
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto& character_binds = CharacterBindRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`id` IN ({}) ORDER BY id, slot",
|
||||||
|
Strings::Join(character_ids, ",")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto& character_materials = CharacterMaterialRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`id` IN ({}) ORDER BY id, slot",
|
||||||
|
Strings::Join(character_ids, ",")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count);
|
size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count);
|
||||||
*out_app = new EQApplicationPacket(OP_SendCharInfo, packet_size);
|
*out_app = new EQApplicationPacket(OP_SendCharInfo, packet_size);
|
||||||
|
|
||||||
unsigned char *buff_ptr = (*out_app)->pBuffer;
|
unsigned char *buff_ptr = (*out_app)->pBuffer;
|
||||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *) buff_ptr;
|
auto *cs = (CharacterSelect_Struct *) buff_ptr;
|
||||||
|
|
||||||
cs->CharCount = character_count;
|
cs->CharCount = character_count;
|
||||||
cs->TotalChars = character_limit;
|
cs->TotalChars = character_limit;
|
||||||
|
|
||||||
buff_ptr += sizeof(CharacterSelect_Struct);
|
buff_ptr += sizeof(CharacterSelect_Struct);
|
||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
for (auto &e: characters) {
|
||||||
CharacterSelectEntry_Struct *p_character_select_entry_struct = (CharacterSelectEntry_Struct *) buff_ptr;
|
auto *cse = (CharacterSelectEntry_Struct *) buff_ptr;
|
||||||
PlayerProfile_Struct pp;
|
PlayerProfile_Struct pp;
|
||||||
EQ::InventoryProfile inventory_profile;
|
EQ::InventoryProfile inv;
|
||||||
|
|
||||||
pp.SetPlayerProfileVersion(EQ::versions::ConvertClientVersionToMobVersion(client_version));
|
pp.SetPlayerProfileVersion(EQ::versions::ConvertClientVersionToMobVersion(client_version));
|
||||||
inventory_profile.SetInventoryVersion(client_version);
|
inv.SetInventoryVersion(client_version);
|
||||||
inventory_profile.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support
|
inv.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support
|
||||||
|
|
||||||
uint32 character_id = Strings::ToUnsignedInt(row[0]);
|
uint32 character_id = e.id;
|
||||||
uint8 has_home = 0;
|
uint8 has_home = 0;
|
||||||
uint8 has_bind = 0;
|
uint8 has_bind = 0;
|
||||||
|
|
||||||
memset(&pp, 0, sizeof(PlayerProfile_Struct));
|
memset(&pp, 0, sizeof(PlayerProfile_Struct));
|
||||||
memset(p_character_select_entry_struct->Name, 0, sizeof(p_character_select_entry_struct->Name));
|
memset(cse->Name, 0, sizeof(cse->Name));
|
||||||
strcpy(p_character_select_entry_struct->Name, row[1]);
|
strcpy(cse->Name, e.name.c_str());
|
||||||
p_character_select_entry_struct->Class = (uint8) Strings::ToUnsignedInt(row[4]);
|
cse->Class = e.class_;
|
||||||
p_character_select_entry_struct->Race = (uint32) Strings::ToUnsignedInt(row[3]);
|
cse->Race = e.race;
|
||||||
p_character_select_entry_struct->Level = (uint8) Strings::ToUnsignedInt(row[5]);
|
cse->Level = e.level;
|
||||||
p_character_select_entry_struct->ShroudClass = p_character_select_entry_struct->Class;
|
cse->ShroudClass = cse->Class;
|
||||||
p_character_select_entry_struct->ShroudRace = p_character_select_entry_struct->Race;
|
cse->ShroudRace = cse->Race;
|
||||||
p_character_select_entry_struct->Zone = (uint16) Strings::ToUnsignedInt(row[19]);
|
cse->Zone = e.zone_id;
|
||||||
p_character_select_entry_struct->Instance = 0;
|
cse->Instance = 0;
|
||||||
p_character_select_entry_struct->Gender = (uint8) Strings::ToUnsignedInt(row[2]);
|
cse->Gender = e.gender;
|
||||||
p_character_select_entry_struct->Face = (uint8) Strings::ToUnsignedInt(row[15]);
|
cse->Face = e.face;
|
||||||
|
|
||||||
for (uint32 material_slot = 0; material_slot < EQ::textures::materialCount; material_slot++) {
|
for (auto &s: cse->Equip) {
|
||||||
p_character_select_entry_struct->Equip[material_slot].Material = 0;
|
s.Material = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].Unknown1 = 0;
|
s.Unknown1 = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].EliteModel = 0;
|
s.EliteModel = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].HerosForgeModel = 0;
|
s.HerosForgeModel = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].Unknown2 = 0;
|
s.Unknown2 = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].Color = 0;
|
s.Color = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
p_character_select_entry_struct->Unknown15 = 0xFF;
|
cse->Unknown15 = 0xFF;
|
||||||
p_character_select_entry_struct->Unknown19 = 0xFF;
|
cse->Unknown19 = 0xFF;
|
||||||
p_character_select_entry_struct->DrakkinTattoo = (uint32) Strings::ToInt(row[17]);
|
cse->DrakkinTattoo = e.drakkin_tattoo;
|
||||||
p_character_select_entry_struct->DrakkinDetails = (uint32) Strings::ToInt(row[18]);
|
cse->DrakkinDetails = e.drakkin_details;
|
||||||
p_character_select_entry_struct->Deity = (uint32) Strings::ToInt(row[6]);
|
cse->Deity = e.deity;
|
||||||
p_character_select_entry_struct->PrimaryIDFile = 0; // Processed Below
|
cse->PrimaryIDFile = 0; // Processed Below
|
||||||
p_character_select_entry_struct->SecondaryIDFile = 0; // Processed Below
|
cse->SecondaryIDFile = 0; // Processed Below
|
||||||
p_character_select_entry_struct->HairColor = (uint8) Strings::ToInt(row[9]);
|
cse->HairColor = e.hair_color;
|
||||||
p_character_select_entry_struct->BeardColor = (uint8) Strings::ToInt(row[10]);
|
cse->BeardColor = e.beard_color;
|
||||||
p_character_select_entry_struct->EyeColor1 = (uint8) Strings::ToInt(row[11]);
|
cse->EyeColor1 = e.eye_color_1;
|
||||||
p_character_select_entry_struct->EyeColor2 = (uint8) Strings::ToInt(row[12]);
|
cse->EyeColor2 = e.eye_color_2;
|
||||||
p_character_select_entry_struct->HairStyle = (uint8) Strings::ToInt(row[13]);
|
cse->HairStyle = e.hair_style;
|
||||||
p_character_select_entry_struct->Beard = (uint8) Strings::ToInt(row[14]);
|
cse->Beard = e.beard;
|
||||||
p_character_select_entry_struct->GoHome = 0; // Processed Below
|
cse->GoHome = 0; // Processed Below
|
||||||
p_character_select_entry_struct->Tutorial = 0; // Processed Below
|
cse->Tutorial = 0; // Processed Below
|
||||||
p_character_select_entry_struct->DrakkinHeritage = (uint32) Strings::ToInt(row[16]);
|
cse->DrakkinHeritage = e.drakkin_heritage;
|
||||||
p_character_select_entry_struct->Unknown1 = 0;
|
cse->Unknown1 = 0;
|
||||||
p_character_select_entry_struct->Enabled = 1;
|
cse->Enabled = 1;
|
||||||
p_character_select_entry_struct->LastLogin = (uint32) Strings::ToInt(row[7]); // RoF2 value: 1212696584
|
cse->LastLogin = e.last_login; // RoF2 value: 1212696584
|
||||||
p_character_select_entry_struct->Unknown2 = 0;
|
cse->Unknown2 = 0;
|
||||||
|
|
||||||
if (RuleB(World, EnableReturnHomeButton)) {
|
if (RuleB(World, EnableReturnHomeButton)) {
|
||||||
int now = time(nullptr);
|
int now = time(nullptr);
|
||||||
if ((now - Strings::ToInt(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome))
|
if (now - e.last_login >= RuleI(World, MinOfflineTimeToReturnHome)) {
|
||||||
p_character_select_entry_struct->GoHome = 1;
|
cse->GoHome = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RuleB(World, EnableTutorialButton) && (p_character_select_entry_struct->Level <= RuleI(World, MaxLevelForTutorial)))
|
if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) {
|
||||||
p_character_select_entry_struct->Tutorial = 1;
|
cse->Tutorial = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// binds
|
||||||
* Bind
|
int bind_count = 0;
|
||||||
*/
|
for (auto &bind : character_binds) {
|
||||||
character_list_query = fmt::format(
|
if (bind.id != e.id) {
|
||||||
SQL(
|
continue;
|
||||||
SELECT
|
}
|
||||||
`zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`
|
|
||||||
FROM
|
if (bind.slot == 4) {
|
||||||
`character_bind`
|
|
||||||
WHERE
|
|
||||||
`id` = {}
|
|
||||||
LIMIT 5
|
|
||||||
),
|
|
||||||
character_id
|
|
||||||
);
|
|
||||||
auto results_bind = database.QueryDatabase(character_list_query);
|
|
||||||
auto bind_count = results_bind.RowCount();
|
|
||||||
for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) {
|
|
||||||
if (row_b[6] && Strings::ToInt(row_b[6]) == 4) {
|
|
||||||
has_home = 1;
|
has_home = 1;
|
||||||
// If our bind count is less than 5, we need to actually make use of this data so lets parse it
|
pp.binds[4].zone_id = bind.zone_id;
|
||||||
if (bind_count < 5) {
|
pp.binds[4].instance_id = bind.instance_id;
|
||||||
pp.binds[4].zone_id = Strings::ToInt(row_b[0]);
|
pp.binds[4].x = bind.x;
|
||||||
pp.binds[4].instance_id = Strings::ToInt(row_b[1]);
|
pp.binds[4].y = bind.y;
|
||||||
pp.binds[4].x = Strings::ToFloat(row_b[2]);
|
pp.binds[4].z = bind.z;
|
||||||
pp.binds[4].y = Strings::ToFloat(row_b[3]);
|
pp.binds[4].heading = bind.heading;
|
||||||
pp.binds[4].z = Strings::ToFloat(row_b[4]);
|
|
||||||
pp.binds[4].heading = Strings::ToFloat(row_b[5]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row_b[6] && Strings::ToInt(row_b[6]) == 0)
|
if (bind.slot == 0) {
|
||||||
has_bind = 1;
|
has_bind = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_home == 0 || has_bind == 0) {
|
bind_count++;
|
||||||
std::string character_list_query = fmt::format(
|
}
|
||||||
SQL(
|
|
||||||
SELECT
|
|
||||||
`zone_id`, `bind_id`, `x`, `y`, `z`, `heading`
|
|
||||||
FROM
|
|
||||||
`start_zones`
|
|
||||||
WHERE
|
|
||||||
`player_class` = {}
|
|
||||||
AND
|
|
||||||
`player_deity` = {}
|
|
||||||
AND
|
|
||||||
`player_race` = {} {}
|
|
||||||
),
|
|
||||||
p_character_select_entry_struct->Class,
|
|
||||||
p_character_select_entry_struct->Deity,
|
|
||||||
p_character_select_entry_struct->Race,
|
|
||||||
ContentFilterCriteria::apply().c_str()
|
|
||||||
);
|
|
||||||
auto results_bind = content_db.QueryDatabase(character_list_query);
|
|
||||||
for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) {
|
|
||||||
/* If a bind_id is specified, make them start there */
|
|
||||||
if (Strings::ToInt(row_d[1]) != 0) {
|
|
||||||
pp.binds[4].zone_id = (uint32) Strings::ToInt(row_d[1]);
|
|
||||||
|
|
||||||
|
if (has_home == 0 || has_bind == 0) {
|
||||||
|
const auto &start_zones = StartZonesRepository::GetWhere(
|
||||||
|
content_db,
|
||||||
|
fmt::format(
|
||||||
|
"`player_class` = {} AND `player_deity` = {} AND `player_race` = {} {}",
|
||||||
|
cse->Class,
|
||||||
|
cse->Deity,
|
||||||
|
cse->Race,
|
||||||
|
ContentFilterCriteria::apply().c_str()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!start_zones.empty()) {
|
||||||
|
pp.binds[4].zone_id = start_zones[0].zone_id;
|
||||||
|
pp.binds[4].x = start_zones[0].x;
|
||||||
|
pp.binds[4].y = start_zones[0].y;
|
||||||
|
pp.binds[4].z = start_zones[0].z;
|
||||||
|
pp.binds[4].heading = start_zones[0].heading;
|
||||||
|
|
||||||
|
if (start_zones[0].bind_id != 0) {
|
||||||
|
pp.binds[4].zone_id = start_zones[0].bind_id;
|
||||||
auto z = GetZone(pp.binds[4].zone_id);
|
auto z = GetZone(pp.binds[4].zone_id);
|
||||||
if (z) {
|
if (z) {
|
||||||
pp.binds[4].x = z->safe_x;
|
pp.binds[4].x = z->safe_x;
|
||||||
@@ -238,168 +232,151 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o
|
|||||||
pp.binds[4].z = z->safe_z;
|
pp.binds[4].z = z->safe_z;
|
||||||
pp.binds[4].heading = z->safe_heading;
|
pp.binds[4].heading = z->safe_heading;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
/* Otherwise, use the zone and coordinates given */
|
pp.binds[4].zone_id = start_zones[0].zone_id;
|
||||||
else {
|
pp.binds[4].x = start_zones[0].x;
|
||||||
pp.binds[4].zone_id = (uint32) Strings::ToInt(row_d[0]);
|
pp.binds[4].y = start_zones[0].y;
|
||||||
float x = Strings::ToFloat(row_d[2]);
|
pp.binds[4].z = start_zones[0].z;
|
||||||
float y = Strings::ToFloat(row_d[3]);
|
pp.binds[4].heading = start_zones[0].heading;
|
||||||
float z = Strings::ToFloat(row_d[4]);
|
|
||||||
float heading = Strings::ToFloat(row_d[5]);
|
if (pp.binds[4].x == 0 && pp.binds[4].y == 0 && pp.binds[4].z == 0 && pp.binds[4].heading == 0) {
|
||||||
if (x == 0 && y == 0 && z == 0 && heading == 0) {
|
|
||||||
auto zone = GetZone(pp.binds[4].zone_id);
|
auto zone = GetZone(pp.binds[4].zone_id);
|
||||||
if (zone) {
|
if (zone) {
|
||||||
x = zone->safe_x;
|
pp.binds[4].x = zone->safe_x;
|
||||||
y = zone->safe_y;
|
pp.binds[4].y = zone->safe_y;
|
||||||
z = zone->safe_z;
|
pp.binds[4].z = zone->safe_z;
|
||||||
heading = zone->safe_heading;
|
pp.binds[4].heading = zone->safe_heading;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pp.binds[4].x = x;
|
|
||||||
pp.binds[4].y = y;
|
|
||||||
pp.binds[4].z = z;
|
|
||||||
pp.binds[4].heading = heading;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
LogError("No start zone found for class [{}] deity [{}] race [{}]", cse->Class, cse->Deity, cse->Race);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pp.binds[0] = pp.binds[4];
|
pp.binds[0] = pp.binds[4];
|
||||||
/* If no home bind set, set it */
|
|
||||||
|
// If we don't have home set, set it
|
||||||
if (has_home == 0) {
|
if (has_home == 0) {
|
||||||
std::string query = fmt::format(
|
auto bind = CharacterBindRepository::NewEntity();
|
||||||
SQL(
|
bind.id = character_id;
|
||||||
REPLACE INTO
|
bind.zone_id = pp.binds[4].zone_id;
|
||||||
`character_bind`
|
bind.instance_id = 0;
|
||||||
(`id`, `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`)
|
bind.x = pp.binds[4].x;
|
||||||
VALUES ({}, {}, {}, {}, {}, {}, {}, {})
|
bind.y = pp.binds[4].y;
|
||||||
),
|
bind.z = pp.binds[4].z;
|
||||||
character_id,
|
bind.heading = pp.binds[4].heading;
|
||||||
pp.binds[4].zone_id,
|
bind.slot = 4;
|
||||||
0,
|
CharacterBindRepository::ReplaceOne(*this, bind);
|
||||||
pp.binds[4].x,
|
|
||||||
pp.binds[4].y,
|
|
||||||
pp.binds[4].z,
|
|
||||||
pp.binds[4].heading,
|
|
||||||
4
|
|
||||||
);
|
|
||||||
auto results_bset = QueryDatabase(query);
|
|
||||||
}
|
}
|
||||||
/* If no regular bind set, set it */
|
|
||||||
|
// If we don't have regular bind set, set it
|
||||||
if (has_bind == 0) {
|
if (has_bind == 0) {
|
||||||
std::string query = fmt::format(
|
auto bind = CharacterBindRepository::NewEntity();
|
||||||
SQL(
|
bind.id = character_id;
|
||||||
REPLACE INTO
|
bind.zone_id = pp.binds[0].zone_id;
|
||||||
`character_bind`
|
bind.instance_id = 0;
|
||||||
(`id`, `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`)
|
bind.x = pp.binds[0].x;
|
||||||
VALUES ({}, {}, {}, {}, {}, {}, {}, {})
|
bind.y = pp.binds[0].y;
|
||||||
),
|
bind.z = pp.binds[0].z;
|
||||||
character_id,
|
bind.heading = pp.binds[0].heading;
|
||||||
pp.binds[0].zone_id,
|
bind.slot = 0;
|
||||||
0,
|
CharacterBindRepository::ReplaceOne(*this, bind);
|
||||||
pp.binds[0].x,
|
|
||||||
pp.binds[0].y,
|
|
||||||
pp.binds[0].z,
|
|
||||||
pp.binds[0].heading,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
auto results_bset = QueryDatabase(query);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* If our bind count is less than 5, then we have null data that needs to be filled in. */
|
|
||||||
|
// If our bind count is less than 5, then we have null data that needs to be filled in
|
||||||
if (bind_count < 5) {
|
if (bind_count < 5) {
|
||||||
// we know that home and main bind must be valid here, so we don't check those
|
// we know that home and main bind must be valid here, so we don't check those
|
||||||
// we also use home to fill in the null data like live does.
|
// we also use home to fill in the null data like live does.
|
||||||
|
|
||||||
|
std::vector<CharacterBindRepository::CharacterBind> binds;
|
||||||
|
|
||||||
for (int i = 1; i < 4; i++) {
|
for (int i = 1; i < 4; i++) {
|
||||||
if (pp.binds[i].zone_id != 0) // we assume 0 is the only invalid one ...
|
if (pp.binds[i].zone_id != 0) { // we assume 0 is the only invalid one ...
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string query = fmt::format(
|
|
||||||
SQL(
|
|
||||||
REPLACE INTO
|
|
||||||
`character_bind`
|
|
||||||
(`id`, `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`)
|
|
||||||
VALUES ({}, {}, {}, {}, {}, {}, {}, {})
|
|
||||||
),
|
|
||||||
character_id,
|
|
||||||
pp.binds[4].zone_id,
|
|
||||||
0,
|
|
||||||
pp.binds[4].x,
|
|
||||||
pp.binds[4].y,
|
|
||||||
pp.binds[4].z,
|
|
||||||
pp.binds[4].heading,
|
|
||||||
i
|
|
||||||
);
|
|
||||||
auto results_bset = QueryDatabase(query);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
character_list_query = fmt::format(
|
auto bind = CharacterBindRepository::NewEntity();
|
||||||
SQL(
|
|
||||||
SELECT
|
bind.slot = i;
|
||||||
`slot`, `red`, `green`, `blue`, `use_tint`, `color`
|
bind.id = character_id;
|
||||||
FROM
|
bind.zone_id = pp.binds[4].zone_id;
|
||||||
`character_material`
|
bind.instance_id = 0;
|
||||||
WHERE
|
bind.x = pp.binds[4].x;
|
||||||
`id` = {}
|
bind.y = pp.binds[4].y;
|
||||||
),
|
bind.z = pp.binds[4].z;
|
||||||
character_id
|
bind.heading = pp.binds[4].heading;
|
||||||
);
|
binds.emplace_back(bind);
|
||||||
auto results_b = database.QueryDatabase(character_list_query);
|
|
||||||
uint8 slot = 0;
|
|
||||||
for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) {
|
|
||||||
slot = Strings::ToInt(row_b[0]);
|
|
||||||
pp.item_tint.Slot[slot].Red = Strings::ToInt(row_b[1]);
|
|
||||||
pp.item_tint.Slot[slot].Green = Strings::ToInt(row_b[2]);
|
|
||||||
pp.item_tint.Slot[slot].Blue = Strings::ToInt(row_b[3]);
|
|
||||||
pp.item_tint.Slot[slot].UseTint = Strings::ToInt(row_b[4]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetCharSelInventory(account_id, p_character_select_entry_struct->Name, &inventory_profile)) {
|
CharacterBindRepository::ReplaceMany(*this, binds);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &cm : character_materials) {
|
||||||
|
pp.item_tint.Slot[cm.slot].Red = cm.red;
|
||||||
|
pp.item_tint.Slot[cm.slot].Green = cm.green;
|
||||||
|
pp.item_tint.Slot[cm.slot].Blue = cm.blue;
|
||||||
|
pp.item_tint.Slot[cm.slot].UseTint = cm.use_tint;
|
||||||
|
pp.item_tint.Slot[cm.slot].Color = cm.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetCharSelInventory(inventories, e, &inv)) {
|
||||||
const EQ::ItemData *item = nullptr;
|
const EQ::ItemData *item = nullptr;
|
||||||
const EQ::ItemInstance *inst = nullptr;
|
const EQ::ItemInstance *inst = nullptr;
|
||||||
int16 inventory_slot = 0;
|
int16 inventory_slot = 0;
|
||||||
|
|
||||||
for (uint32 matslot = EQ::textures::textureBegin; matslot < EQ::textures::materialCount; matslot++) {
|
for (uint32 matslot = EQ::textures::textureBegin; matslot < EQ::textures::materialCount; matslot++) {
|
||||||
inventory_slot = EQ::InventoryProfile::CalcSlotFromMaterial(matslot);
|
inventory_slot = EQ::InventoryProfile::CalcSlotFromMaterial(matslot);
|
||||||
if (inventory_slot == INVALID_INDEX) { continue; }
|
if (inventory_slot == INVALID_INDEX) { continue; }
|
||||||
inst = inventory_profile.GetItem(inventory_slot);
|
inst = inv.GetItem(inventory_slot);
|
||||||
if (inst == nullptr)
|
if (inst == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
item = inst->GetItem();
|
item = inst->GetItem();
|
||||||
if (item == nullptr)
|
if (item == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (matslot > 6) {
|
if (matslot > 6) {
|
||||||
uint32 item_id_file = 0;
|
uint32 item_id_file = 0;
|
||||||
// Weapon Models
|
// Weapon Models
|
||||||
if (inst->GetOrnamentationIDFile() != 0) {
|
if (inst->GetOrnamentationIDFile() != 0) {
|
||||||
item_id_file = inst->GetOrnamentationIDFile();
|
item_id_file = inst->GetOrnamentationIDFile();
|
||||||
p_character_select_entry_struct->Equip[matslot].Material = item_id_file;
|
cse->Equip[matslot].Material = item_id_file;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
if (strlen(item->IDFile) > 2) {
|
if (strlen(item->IDFile) > 2) {
|
||||||
item_id_file = Strings::ToInt(&item->IDFile[2]);
|
item_id_file = Strings::ToInt(&item->IDFile[2]);
|
||||||
p_character_select_entry_struct->Equip[matslot].Material = item_id_file;
|
cse->Equip[matslot].Material = item_id_file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matslot == EQ::textures::weaponPrimary) {
|
if (matslot == EQ::textures::weaponPrimary) {
|
||||||
p_character_select_entry_struct->PrimaryIDFile = item_id_file;
|
cse->PrimaryIDFile = item_id_file;
|
||||||
} else {
|
|
||||||
p_character_select_entry_struct->SecondaryIDFile = item_id_file;
|
|
||||||
}
|
}
|
||||||
} else {
|
else {
|
||||||
|
cse->SecondaryIDFile = item_id_file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
// Armor Materials/Models
|
// Armor Materials/Models
|
||||||
uint32 color = (
|
uint32 color = (
|
||||||
pp.item_tint.Slot[matslot].UseTint ?
|
pp.item_tint.Slot[matslot].UseTint ?
|
||||||
pp.item_tint.Slot[matslot].Color :
|
pp.item_tint.Slot[matslot].Color :
|
||||||
inst->GetColor()
|
inst->GetColor()
|
||||||
);
|
);
|
||||||
p_character_select_entry_struct->Equip[matslot].Material = item->Material;
|
cse->Equip[matslot].Material = item->Material;
|
||||||
p_character_select_entry_struct->Equip[matslot].EliteModel = item->EliteMaterial;
|
cse->Equip[matslot].EliteModel = item->EliteMaterial;
|
||||||
p_character_select_entry_struct->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot);
|
cse->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot);
|
||||||
p_character_select_entry_struct->Equip[matslot].Color = color;
|
cse->Equip[matslot].Color = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
printf("Error loading inventory for %s\n", p_character_select_entry_struct->Name);
|
else {
|
||||||
|
printf("Error loading inventory for %s\n", cse->Name);
|
||||||
}
|
}
|
||||||
buff_ptr += sizeof(CharacterSelectEntry_Struct);
|
buff_ptr += sizeof(CharacterSelectEntry_Struct);
|
||||||
}
|
}
|
||||||
@@ -849,34 +826,21 @@ bool WorldDatabase::LoadCharacterCreateCombos()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is a slightly modified version of SharedDatabase::GetInventory(...) for character select use-only
|
bool WorldDatabase::GetCharSelInventory(
|
||||||
bool WorldDatabase::GetCharSelInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv)
|
const std::vector<InventoryRepository::Inventory> &inventories,
|
||||||
|
const CharacterDataRepository::CharacterData &character,
|
||||||
|
EQ::InventoryProfile *inv
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (!account_id || !name || !inv) {
|
if (inventories.empty() || !character.id || !inv) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32 character_id = GetCharacterID(name);
|
for (const auto& e : inventories) {
|
||||||
|
if (e.character_id != character.id) {
|
||||||
if (!character_id) {
|
continue;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& l = InventoryRepository::GetWhere(
|
|
||||||
*this,
|
|
||||||
fmt::format(
|
|
||||||
"`character_id` = {} AND `slot_id` BETWEEN {} AND {}",
|
|
||||||
character_id,
|
|
||||||
EQ::invslot::slotHead,
|
|
||||||
EQ::invslot::slotFeet
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (l.empty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& e : l) {
|
|
||||||
switch (e.slot_id) {
|
switch (e.slot_id) {
|
||||||
case EQ::invslot::slotFace:
|
case EQ::invslot::slotFace:
|
||||||
case EQ::invslot::slotEar2:
|
case EQ::invslot::slotEar2:
|
||||||
|
|||||||
+7
-1
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "../common/shareddb.h"
|
#include "../common/shareddb.h"
|
||||||
#include "../common/eq_packet.h"
|
#include "../common/eq_packet.h"
|
||||||
|
#include "../common/repositories/inventory_repository.h"
|
||||||
|
#include "../common/repositories/character_data_repository.h"
|
||||||
|
|
||||||
struct PlayerProfile_Struct;
|
struct PlayerProfile_Struct;
|
||||||
struct CharCreate_Struct;
|
struct CharCreate_Struct;
|
||||||
@@ -43,7 +45,11 @@ private:
|
|||||||
void SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc);
|
void SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc);
|
||||||
void SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc);
|
void SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc);
|
||||||
|
|
||||||
bool GetCharSelInventory(uint32 account_id, char* name, EQ::InventoryProfile* inv);
|
bool GetCharSelInventory(
|
||||||
|
const std::vector<InventoryRepository::Inventory> &inventories,
|
||||||
|
const CharacterDataRepository::CharacterData &character,
|
||||||
|
EQ::InventoryProfile *inv
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern WorldDatabase database;
|
extern WorldDatabase database;
|
||||||
|
|||||||
+64
-10
@@ -36,11 +36,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "shared_task_manager.h"
|
#include "shared_task_manager.h"
|
||||||
#include "dynamic_zone_manager.h"
|
#include "dynamic_zone_manager.h"
|
||||||
#include "ucs.h"
|
#include "ucs.h"
|
||||||
|
#include "clientlist.h"
|
||||||
|
|
||||||
extern uint32 numzones;
|
extern uint32 numzones;
|
||||||
extern EQ::Random emu_random;
|
extern EQ::Random emu_random;
|
||||||
extern WebInterfaceList web_interface;
|
extern WebInterfaceList web_interface;
|
||||||
extern SharedTaskManager shared_task_manager;
|
extern SharedTaskManager shared_task_manager;
|
||||||
|
extern ClientList client_list;
|
||||||
volatile bool UCSServerAvailable_ = false;
|
volatile bool UCSServerAvailable_ = false;
|
||||||
void CatchSignal(int sig_num);
|
void CatchSignal(int sig_num);
|
||||||
|
|
||||||
@@ -515,19 +517,27 @@ void ZSList::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_mins
|
|||||||
SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer);
|
SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZSList::SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message) {
|
void ZSList::SendEmoteMessageRaw(
|
||||||
if (!message)
|
const char *to,
|
||||||
|
uint32 to_guilddbid,
|
||||||
|
int16 to_minstatus,
|
||||||
|
uint32 type,
|
||||||
|
const char *message
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!message) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
auto pack = new ServerPacket;
|
auto pack = new ServerPacket;
|
||||||
|
|
||||||
pack->opcode = ServerOP_EmoteMessage;
|
pack->opcode = ServerOP_EmoteMessage;
|
||||||
pack->size = sizeof(ServerEmoteMessage_Struct) + strlen(message) + 1;
|
pack->size = sizeof(ServerEmoteMessage_Struct) + strlen(message) + 1;
|
||||||
pack->pBuffer = new uchar[pack->size];
|
pack->pBuffer = new uchar[pack->size];
|
||||||
memset(pack->pBuffer, 0, pack->size);
|
memset(pack->pBuffer, 0, pack->size);
|
||||||
ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*)pack->pBuffer;
|
ServerEmoteMessage_Struct *sem = (ServerEmoteMessage_Struct *) pack->pBuffer;
|
||||||
|
|
||||||
if (to) {
|
if (to) {
|
||||||
strcpy((char *)sem->to, to);
|
strcpy((char *) sem->to, to);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sem->to[0] = 0;
|
sem->to[0] = 0;
|
||||||
@@ -537,21 +547,36 @@ void ZSList::SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_m
|
|||||||
sem->minstatus = to_minstatus;
|
sem->minstatus = to_minstatus;
|
||||||
sem->type = type;
|
sem->type = type;
|
||||||
strcpy(&sem->message[0], message);
|
strcpy(&sem->message[0], message);
|
||||||
char tempto[64] = { 0 };
|
char tempto[64] = {0};
|
||||||
if (to)
|
if (to) {
|
||||||
strn0cpy(tempto, to, 64);
|
strn0cpy(tempto, to, 64);
|
||||||
|
}
|
||||||
|
|
||||||
if (tempto[0] == 0) {
|
if (tempto[0] == 0) {
|
||||||
|
if (to_guilddbid > 0) {
|
||||||
|
SendPacketToZonesWithGuild(to_guilddbid, pack);
|
||||||
|
}
|
||||||
|
else if (to_minstatus > 0) {
|
||||||
|
SendPacketToZonesWithGMs(pack);
|
||||||
|
} else {
|
||||||
SendPacket(pack);
|
SendPacket(pack);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
ZoneServer* zs = FindByName(to);
|
ZoneServer *zs = FindByName(to);
|
||||||
|
if (zs) {
|
||||||
if (zs != 0)
|
|
||||||
zs->SendPacket(pack);
|
zs->SendPacket(pack);
|
||||||
else
|
}
|
||||||
|
else if (to_guilddbid > 0) {
|
||||||
|
SendPacketToZonesWithGuild(to_guilddbid, pack);
|
||||||
|
}
|
||||||
|
else if (to_minstatus > 0) {
|
||||||
|
SendPacketToZonesWithGMs(pack);
|
||||||
|
}
|
||||||
|
else {
|
||||||
SendPacket(pack);
|
SendPacket(pack);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
delete pack;
|
delete pack;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,6 +896,33 @@ bool ZSList::SendPacketToBootedZones(ServerPacket* pack)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ZSList::SendPacketToZonesWithGuild(uint32 guild_id, ServerPacket* pack)
|
||||||
|
{
|
||||||
|
auto servers = client_list.GetGuildZoneServers(guild_id);
|
||||||
|
for (auto const& z : zone_server_list) {
|
||||||
|
for (auto const& server_id : servers) {
|
||||||
|
if (z->GetID() == server_id && z->GetZoneID() > 0) {
|
||||||
|
z->SendPacket(pack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZSList::SendPacketToZonesWithGMs(ServerPacket* pack)
|
||||||
|
{
|
||||||
|
for (auto const &z: zone_server_list) {
|
||||||
|
for (auto const &server_id: client_list.GetZoneServersWithGMs()) {
|
||||||
|
if (z->GetID() == server_id && z->GetZoneID() > 0) {
|
||||||
|
z->SendPacket(pack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ZSList::SendServerReload(ServerReload::Type type, uchar *packet)
|
void ZSList::SendServerReload(ServerReload::Type type, uchar *packet)
|
||||||
{
|
{
|
||||||
static auto pack = ServerPacket(ServerOP_ServerReloadRequest, sizeof(ServerReload::Request));
|
static auto pack = ServerPacket(ServerOP_ServerReloadRequest, sizeof(ServerReload::Request));
|
||||||
@@ -894,6 +946,8 @@ void ZSList::SendServerReload(ServerReload::Type type, uchar *packet)
|
|||||||
ServerReload::Type::Commands,
|
ServerReload::Type::Commands,
|
||||||
ServerReload::Type::PerlExportSettings,
|
ServerReload::Type::PerlExportSettings,
|
||||||
ServerReload::Type::DataBucketsCache,
|
ServerReload::Type::DataBucketsCache,
|
||||||
|
ServerReload::Type::Quests,
|
||||||
|
ServerReload::Type::QuestsTimerReset,
|
||||||
ServerReload::Type::WorldRepop,
|
ServerReload::Type::WorldRepop,
|
||||||
ServerReload::Type::WorldWithRespawn
|
ServerReload::Type::WorldWithRespawn
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ public:
|
|||||||
bool SendPacket(ServerPacket *pack);
|
bool SendPacket(ServerPacket *pack);
|
||||||
bool SendPacket(uint32 zoneid, ServerPacket *pack);
|
bool SendPacket(uint32 zoneid, ServerPacket *pack);
|
||||||
bool SendPacket(uint32 zoneid, uint16 instanceid, ServerPacket *pack);
|
bool SendPacket(uint32 zoneid, uint16 instanceid, ServerPacket *pack);
|
||||||
|
bool SendPacketToZonesWithGuild(uint32 guild_id, ServerPacket *pack);
|
||||||
|
bool SendPacketToZonesWithGMs(ServerPacket *pack);
|
||||||
bool SendPacketToBootedZones(ServerPacket* pack);
|
bool SendPacketToBootedZones(ServerPacket* pack);
|
||||||
bool SetLockedZone(uint16 iZoneID, bool iLock);
|
bool SetLockedZone(uint16 iZoneID, bool iLock);
|
||||||
|
|
||||||
|
|||||||
+15
-7
@@ -571,8 +571,14 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (scm->guilddbid > 0) {
|
||||||
|
zoneserver_list.SendPacketToZonesWithGuild(scm->guilddbid, pack);
|
||||||
|
} else if (scm->chan_num == ChatChannel_GMSAY) {
|
||||||
|
zoneserver_list.SendPacketToZonesWithGMs(pack);
|
||||||
|
} else {
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacket(pack);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -729,13 +735,15 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
zs = zoneserver_list.FindByID(s->zone_server_id);
|
zs = zoneserver_list.FindByID(s->zone_server_id);
|
||||||
} else if (s->zone_id) {
|
} else if (s->zone_id) {
|
||||||
zs = zoneserver_list.FindByName(ZoneName(s->zone_id));
|
zs = zoneserver_list.FindByName(ZoneName(s->zone_id));
|
||||||
|
} else if (s->instance_id) {
|
||||||
|
zs = zoneserver_list.FindByInstanceID(s->instance_id);
|
||||||
} else {
|
} else {
|
||||||
zoneserver_list.SendEmoteMessage(
|
zoneserver_list.SendEmoteMessage(
|
||||||
s->admin_name,
|
s->admin_name,
|
||||||
0,
|
0,
|
||||||
AccountStatus::Player,
|
AccountStatus::Player,
|
||||||
Chat::White,
|
Chat::White,
|
||||||
"Error: SOP_ZoneShutdown: neither ID nor name specified"
|
"Error: SOP_ZoneShutdown: Zone ID, Instance ID, nor Zone Short Name specified"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1510,7 +1518,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
|
|
||||||
guild->tribute.timer.Disable();
|
guild->tribute.timer.Disable();
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(data->guild_id, pack);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1553,7 +1561,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
guild_mgr.UpdateDbGuildFavor(data->guild_id, data->favor);
|
guild_mgr.UpdateDbGuildFavor(data->guild_id, data->favor);
|
||||||
guild_mgr.UpdateDbTributeTimeRemaining(data->guild_id, data->time_remaining);
|
guild_mgr.UpdateDbTributeTimeRemaining(data->guild_id, data->time_remaining);
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(data->guild_id, pack);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1587,7 +1595,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
data->time_remaining = in->time_remaining;
|
data->time_remaining = in->time_remaining;
|
||||||
strn0cpy(data->player_name, in->player_name, sizeof(data->player_name));
|
strn0cpy(data->player_name, in->player_name, sizeof(data->player_name));
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(out);
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, out);
|
||||||
safe_delete(out);
|
safe_delete(out);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1610,7 +1618,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
out->tribute_id_2_tier = guild->tribute.id_2_tier;
|
out->tribute_id_2_tier = guild->tribute.id_2_tier;
|
||||||
out->time_remaining = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id);
|
out->time_remaining = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id);
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(sp);
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, sp);
|
||||||
safe_delete(sp);
|
safe_delete(sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1630,7 +1638,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
out->tribute_timer = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id);
|
out->tribute_timer = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id);
|
||||||
out->trophy_timer = 0;
|
out->trophy_timer = 0;
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(sp);
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, sp);
|
||||||
safe_delete(sp);
|
safe_delete(sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1654,7 +1662,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
out->member_time = in->member_time;
|
out->member_time = in->member_time;
|
||||||
strn0cpy(out->player_name, in->player_name, sizeof(out->player_name));
|
strn0cpy(out->player_name, in->player_name, sizeof(out->player_name));
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(sp);
|
zoneserver_list.SendPacketToZonesWithGuild(out->guild_id, sp);
|
||||||
safe_delete(sp)
|
safe_delete(sp)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ public:
|
|||||||
|
|
||||||
inline const char* GetZoneName() const { return zone_name; }
|
inline const char* GetZoneName() const { return zone_name; }
|
||||||
inline const char* GetZoneLongName() const { return long_name; }
|
inline const char* GetZoneLongName() const { return long_name; }
|
||||||
|
inline std::string GetCurrentVersion() const { return CURRENT_VERSION; }
|
||||||
|
inline std::string GetCompileDate() const { return COMPILE_DATE; }
|
||||||
const char* GetCompileTime() const{ return compiled; }
|
const char* GetCompileTime() const{ return compiled; }
|
||||||
void SetCompile(char* in_compile){ strcpy(compiled,in_compile); }
|
void SetCompile(char* in_compile){ strcpy(compiled,in_compile); }
|
||||||
inline uint32 GetZoneID() const { return zone_server_zone_id; }
|
inline uint32 GetZoneID() const { return zone_server_zone_id; }
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ SET(zone_sources
|
|||||||
zone_event_scheduler.cpp
|
zone_event_scheduler.cpp
|
||||||
zone_npc_factions.cpp
|
zone_npc_factions.cpp
|
||||||
zone_reload.cpp
|
zone_reload.cpp
|
||||||
|
zone_save_state.cpp
|
||||||
zoning.cpp
|
zoning.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -292,6 +293,7 @@ SET(zone_headers
|
|||||||
zonedump.h
|
zonedump.h
|
||||||
zone_cli.h
|
zone_cli.h
|
||||||
zone_reload.h
|
zone_reload.h
|
||||||
|
zone_save_state.h
|
||||||
zone_cli.cpp)
|
zone_cli.cpp)
|
||||||
|
|
||||||
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
|
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
|
||||||
|
|||||||
@@ -2507,6 +2507,12 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_resumed_from_zone_suspend && !IsQueuedForCorpse()) {
|
||||||
|
LogInfo("NPC [{}] is resumed from zone suspend, cannot kill until zone resume is complete.", GetCleanName());
|
||||||
|
SetHP(0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsMultiQuestEnabled()) {
|
if (IsMultiQuestEnabled()) {
|
||||||
for (auto &i: m_hand_in.items) {
|
for (auto &i: m_hand_in.items) {
|
||||||
if (i.is_multiquest_item && i.item->GetItem()->NoDrop != 0) {
|
if (i.is_multiquest_item && i.item->GetItem()->NoDrop != 0) {
|
||||||
@@ -3041,9 +3047,15 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
|||||||
if (!other)
|
if (!other)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (other->IsDestroying())
|
||||||
|
return;
|
||||||
|
|
||||||
if (other == this)
|
if (other == this)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (other->IsClient() && (other->CastToClient()->IsZoning() || other->CastToClient()->Connected() == false))
|
||||||
|
return;
|
||||||
|
|
||||||
if (other->IsTrap())
|
if (other->IsTrap())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
+341
-141
@@ -2287,7 +2287,7 @@ void Bot::AI_Process()
|
|||||||
// COMBAT RANGE CALCS
|
// COMBAT RANGE CALCS
|
||||||
bool front_mob = InFrontMob(tar, GetX(), GetY());
|
bool front_mob = InFrontMob(tar, GetX(), GetY());
|
||||||
bool behind_mob = BehindMob(tar, GetX(), GetY());
|
bool behind_mob = BehindMob(tar, GetX(), GetY());
|
||||||
uint8 stop_melee_level = GetStopMeleeLevel();
|
bool stop_melee_level = GetLevel() >= GetStopMeleeLevel();
|
||||||
tar_distance = sqrt(tar_distance); // sqrt this for future calculations
|
tar_distance = sqrt(tar_distance); // sqrt this for future calculations
|
||||||
// Item variables
|
// Item variables
|
||||||
const EQ::ItemInstance* p_item = GetBotItem(EQ::invslot::slotPrimary);
|
const EQ::ItemInstance* p_item = GetBotItem(EQ::invslot::slotPrimary);
|
||||||
@@ -2314,7 +2314,7 @@ void Bot::AI_Process()
|
|||||||
if (PULLING_BOT || RETURNING_BOT) {
|
if (PULLING_BOT || RETURNING_BOT) {
|
||||||
if (!TargetValidation(tar)) { return; }
|
if (!TargetValidation(tar)) { return; }
|
||||||
|
|
||||||
if (!DoLosChecks(tar)) {
|
if (RuleB(Bots, BotsRequireLoS) && !HasLoS()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2442,7 +2442,7 @@ void Bot::AI_Process()
|
|||||||
|
|
||||||
ranged_timer.Start();
|
ranged_timer.Start();
|
||||||
}
|
}
|
||||||
else if (!IsBotRanged() && GetLevel() < stop_melee_level) {
|
else if (!IsBotRanged() && !stop_melee_level) {
|
||||||
if (
|
if (
|
||||||
IsTaunting() ||
|
IsTaunting() ||
|
||||||
!GetMaxMeleeRange() ||
|
!GetMaxMeleeRange() ||
|
||||||
@@ -3128,7 +3128,6 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) {
|
|||||||
bool is_two_hander = input.p_item && input.p_item->GetItem()->IsType2HWeapon();
|
bool is_two_hander = input.p_item && input.p_item->GetItem()->IsType2HWeapon();
|
||||||
bool is_shield = input.s_item && input.s_item->GetItem()->IsTypeShield();
|
bool is_shield = input.s_item && input.s_item->GetItem()->IsTypeShield();
|
||||||
bool is_backstab_weapon = input.p_item && input.p_item->GetItemBackstabDamage();
|
bool is_backstab_weapon = input.p_item && input.p_item->GetItemBackstabDamage();
|
||||||
bool is_stop_melee_level = GetLevel() >= input.stop_melee_level;
|
|
||||||
|
|
||||||
if (IsTaunting()) { // Taunting bots
|
if (IsTaunting()) { // Taunting bots
|
||||||
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, LowerTauntingMeleeDistanceMultiplier);
|
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, LowerTauntingMeleeDistanceMultiplier);
|
||||||
@@ -3143,7 +3142,7 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) {
|
|||||||
o.melee_distance_min = std::max(min_distance, (desired_range / 2));
|
o.melee_distance_min = std::max(min_distance, (desired_range / 2));
|
||||||
o.melee_distance = std::min(max_distance, desired_range);
|
o.melee_distance = std::min(max_distance, desired_range);
|
||||||
}
|
}
|
||||||
else if (is_stop_melee_level) { // Casters
|
else if (input.stop_melee_level) { // Casters
|
||||||
float desired_range = GetBotDistanceRanged();
|
float desired_range = GetBotDistanceRanged();
|
||||||
|
|
||||||
o.melee_distance_min = std::max(o.melee_distance_max, (desired_range / 2));
|
o.melee_distance_min = std::max(o.melee_distance_max, (desired_range / 2));
|
||||||
@@ -3176,14 +3175,17 @@ bool Bot::IsValidTarget(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetHasLoS(DoLosChecks(tar));
|
||||||
|
|
||||||
bool invalid_target_state = false;
|
bool invalid_target_state = false;
|
||||||
|
|
||||||
if (HOLDING ||
|
if (HOLDING ||
|
||||||
!tar->IsNPC() ||
|
!tar->IsNPC() ||
|
||||||
(tar->IsMezzed() && !HasBotAttackFlag(tar)) ||
|
(tar->IsMezzed() && !HasBotAttackFlag(tar)) ||
|
||||||
(!Charmed() && tar->GetUltimateOwner()->IsOfClientBotMerc()) ||
|
(!Charmed() && tar->GetUltimateOwner()->IsOfClientBotMerc()) ||
|
||||||
lo_distance > leash_distance ||
|
lo_distance > leash_distance ||
|
||||||
tar_distance > leash_distance ||
|
tar_distance > leash_distance ||
|
||||||
(!GetAttackingFlag() && !CheckLosCheat(tar) && !leash_owner->CheckLosCheat(tar)) ||
|
(!GetAttackingFlag() && !HasLoS()) ||
|
||||||
!IsAttackAllowed(tar)
|
!IsAttackAllowed(tar)
|
||||||
) {
|
) {
|
||||||
invalid_target_state = true;
|
invalid_target_state = true;
|
||||||
@@ -3578,8 +3580,16 @@ void Bot::Depop() {
|
|||||||
entity_list.RemoveFromHateLists(this);
|
entity_list.RemoveFromHateLists(this);
|
||||||
RemoveAllAuras();
|
RemoveAllAuras();
|
||||||
|
|
||||||
if (HasPet())
|
Mob* bot_pet = GetPet();
|
||||||
GetPet()->Depop();
|
|
||||||
|
if (bot_pet) {
|
||||||
|
if (bot_pet->Charmed()) {
|
||||||
|
bot_pet->BuffFadeByEffect(SE_Charm);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bot_pet->Depop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_botOwner = nullptr;
|
_botOwner = nullptr;
|
||||||
_botOwnerCharacterID = 0;
|
_botOwnerCharacterID = 0;
|
||||||
@@ -3702,11 +3712,6 @@ bool Bot::Spawn(Client* botCharacterOwner) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RuleB(Bots, RunSpellTypeChecksOnSpawn)) {
|
|
||||||
OwnerMessage("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline.");
|
|
||||||
CheckBotSpells(); //This runs through a series of checks and outputs any spells that are set to the wrong spell type in the database
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsBotRanged()) {
|
if (IsBotRanged()) {
|
||||||
ChangeBotRangedWeapons(true);
|
ChangeBotRangedWeapons(true);
|
||||||
}
|
}
|
||||||
@@ -9531,7 +9536,14 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!BotHasEnoughMana(spell_id)) {
|
if (
|
||||||
|
!BotHasEnoughMana(spell_id) &&
|
||||||
|
(
|
||||||
|
!RuleB(Bots, FinishBuffing) ||
|
||||||
|
IsEngaged() ||
|
||||||
|
IsBotBuffSpellType(spell_type)
|
||||||
|
)
|
||||||
|
) {
|
||||||
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spell_id));
|
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spell_id));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -9790,6 +9802,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8 bot_class = GetClass();
|
uint8 bot_class = GetClass();
|
||||||
|
auto spell = spells[spell_id];
|
||||||
|
|
||||||
switch (spell_type) {
|
switch (spell_type) {
|
||||||
case BotSpellTypes::Buff:
|
case BotSpellTypes::Buff:
|
||||||
@@ -9813,7 +9826,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) {
|
|||||||
case BotSpellTypes::SendHome:
|
case BotSpellTypes::SendHome:
|
||||||
if (
|
if (
|
||||||
tar == this &&
|
tar == this &&
|
||||||
spells[spell_id].target_type == ST_TargetsTarget
|
spell.target_type == ST_TargetsTarget
|
||||||
) {
|
) {
|
||||||
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id));
|
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id));
|
||||||
return false;
|
return false;
|
||||||
@@ -9843,7 +9856,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
spells[spell_id].target_type == ST_Pet &&
|
(spell.target_type == ST_Pet || spell.target_type == ST_SummonedPet) &&
|
||||||
(
|
(
|
||||||
!tar->IsPet() ||
|
!tar->IsPet() ||
|
||||||
(
|
(
|
||||||
@@ -10586,6 +10599,8 @@ BotSpell Bot::GetSpellByHealType(uint16 spell_type, Mob* tar) {
|
|||||||
return GetBestBotSpellForHealOverTime(this, tar, spell_type);
|
return GetBestBotSpellForHealOverTime(this, tar, spell_type);
|
||||||
case BotSpellTypes::GroupHoTHeals:
|
case BotSpellTypes::GroupHoTHeals:
|
||||||
return GetBestBotSpellForGroupHealOverTime(this, tar, spell_type);
|
return GetBestBotSpellForGroupHealOverTime(this, tar, spell_type);
|
||||||
|
default:
|
||||||
|
return BotSpell(); // Return an empty BotSpell if no valid spell type is found
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10637,7 +10652,7 @@ int Bot::GetDefaultSetting(uint16 setting_category, uint16 setting_type, uint8 s
|
|||||||
case BotSettingCategories::SpellTypeAnnounceCast:
|
case BotSettingCategories::SpellTypeAnnounceCast:
|
||||||
return GetDefaultSpellTypeAnnounceCast(setting_type, stance);
|
return GetDefaultSpellTypeAnnounceCast(setting_type, stance);
|
||||||
default:
|
default:
|
||||||
break;
|
return 0; // Default return value for unrecognized categories
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10676,7 +10691,7 @@ int Bot::GetSetting(uint16 setting_category, uint16 setting_type) {
|
|||||||
case BotSettingCategories::SpellTypeAnnounceCast:
|
case BotSettingCategories::SpellTypeAnnounceCast:
|
||||||
return GetSpellTypeAnnounceCast(setting_type);
|
return GetSpellTypeAnnounceCast(setting_type);
|
||||||
default:
|
default:
|
||||||
break;
|
return 0; // Default return value for unrecognized categories
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11403,7 +11418,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DoLosChecks(tar)) {
|
if (!HasLoS() && !DoLosChecks(tar)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11566,18 +11581,265 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsBotSpellTypeDetrimental(spell_type) && !IsDetrimentalSpell(spell_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsBotSpellTypeBeneficial(spell_type) && !IsBeneficialSpell(spell_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto spell = spells[spell_id];
|
||||||
|
std::string teleport_zone = spell.teleport_zone;
|
||||||
|
|
||||||
switch (spell_type) {
|
switch (spell_type) {
|
||||||
|
case BotSpellTypes::Nuke:
|
||||||
|
if (IsAnyNukeOrStunSpell(spell_id) && !IsStunSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::RegularHeal:
|
||||||
|
case BotSpellTypes::PetRegularHeals:
|
||||||
|
if (
|
||||||
|
IsAnyHealSpell(spell_id) &&
|
||||||
|
!IsVeryFastHealSpell(spell_id) &&
|
||||||
|
!IsFastHealSpell(spell_id) &&
|
||||||
|
!IsCompleteHealSpell(spell_id) &&
|
||||||
|
!IsHealOverTimeSpell(spell_id) &&
|
||||||
|
!IsBuffSpell(spell_id)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Root:
|
||||||
|
case BotSpellTypes::AERoot:
|
||||||
|
if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_Root)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
case BotSpellTypes::Buff:
|
case BotSpellTypes::Buff:
|
||||||
|
case BotSpellTypes::PreCombatBuff:
|
||||||
case BotSpellTypes::PetBuffs:
|
case BotSpellTypes::PetBuffs:
|
||||||
if (
|
if (
|
||||||
IsResistanceOnlySpell(spell_id) ||
|
IsBuffSpell(spell_id) &&
|
||||||
IsDamageShieldOnlySpell(spell_id) ||
|
IsBeneficialSpell(spell_id) &&
|
||||||
IsDamageShieldAndResistSpell(spell_id)
|
!IsBardSong(spell_id) &&
|
||||||
|
!IsResistanceOnlySpell(spell_id) &&
|
||||||
|
!IsDamageShieldOnlySpell(spell_id) &&
|
||||||
|
!IsDamageShieldAndResistSpell(spell_id)
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
spell_type != BotSpellTypes::PetBuffs &&
|
||||||
|
(spell.target_type == ST_Pet || spell.target_type == ST_SummonedPet)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Escape:
|
||||||
|
if (IsEscapeSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Pet:
|
||||||
|
if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Lifetap:
|
||||||
|
case BotSpellTypes::AELifetap:
|
||||||
|
if (IsDetrimentalSpell(spell_id) && IsLifetapSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Snare:
|
||||||
|
case BotSpellTypes::AESnare:
|
||||||
|
if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::DOT:
|
||||||
|
case BotSpellTypes::AEDoT:
|
||||||
|
if (
|
||||||
|
IsDetrimentalSpell(spell_id) &&
|
||||||
|
(IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id))
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Dispel:
|
||||||
|
case BotSpellTypes::AEDispel:
|
||||||
|
if (IsDispelSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::InCombatBuff:
|
||||||
|
if (
|
||||||
|
!IsBardSong(spell_id) &&
|
||||||
|
(
|
||||||
|
(IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) ||
|
||||||
|
(spell_type == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id))
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Mez:
|
||||||
|
case BotSpellTypes::AEMez:
|
||||||
|
if (IsMesmerizeSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Charm:
|
||||||
|
if (IsCharmSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Slow:
|
||||||
|
case BotSpellTypes::AESlow:
|
||||||
|
if (IsDetrimentalSpell(spell_id) && IsSlowSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Debuff:
|
||||||
|
case BotSpellTypes::AEDebuff:
|
||||||
|
if (
|
||||||
|
IsDebuffSpell(spell_id) &&
|
||||||
|
!IsHateReduxSpell(spell_id) &&
|
||||||
|
!IsHateSpell(spell_id)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Cure:
|
||||||
|
case BotSpellTypes::GroupCures:
|
||||||
|
case BotSpellTypes::PetCures:
|
||||||
|
if (IsCureSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Resurrect:
|
||||||
|
if (IsEffectInSpell(spell_id, SE_Revive)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::HateRedux:
|
||||||
|
if (IsHateReduxSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::InCombatBuffSong:
|
||||||
|
case BotSpellTypes::OutOfCombatBuffSong:
|
||||||
|
case BotSpellTypes::PreCombatBuffSong:
|
||||||
|
if (
|
||||||
|
IsBuffSpell(spell_id) &&
|
||||||
|
IsBeneficialSpell(spell_id) &&
|
||||||
|
IsBardSong(spell_id)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Fear:
|
||||||
|
case BotSpellTypes::AEFear:
|
||||||
|
if (IsFearSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Stun:
|
||||||
|
case BotSpellTypes::AEStun:
|
||||||
|
if (IsDetrimentalSpell(spell_id) && IsStunSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::HateLine:
|
||||||
|
case BotSpellTypes::AEHateLine:
|
||||||
|
if (IsDetrimentalSpell(spell_id) && IsHateSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::CompleteHeal:
|
||||||
|
case BotSpellTypes::GroupCompleteHeals:
|
||||||
|
case BotSpellTypes::PetCompleteHeals:
|
||||||
|
if (IsCompleteHealSpell(spell_id) || IsGroupCompleteHealSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::FastHeals:
|
||||||
|
case BotSpellTypes::PetFastHeals:
|
||||||
|
if (IsFastHealSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::VeryFastHeals:
|
||||||
|
case BotSpellTypes::PetVeryFastHeals:
|
||||||
|
if (IsVeryFastHealSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::GroupHeals:
|
||||||
|
if (IsRegularGroupHealSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::HoTHeals:
|
||||||
|
case BotSpellTypes::GroupHoTHeals:
|
||||||
|
case BotSpellTypes::PetHoTHeals:
|
||||||
|
if (IsHealOverTimeSpell(spell_id) || IsGroupHealOverTimeSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::AENukes:
|
||||||
|
if (
|
||||||
|
IsDetrimentalSpell(spell_id) &&
|
||||||
|
!IsAERainSpell(spell_id) &&
|
||||||
|
!IsPBAENukeSpell(spell_id) &&
|
||||||
|
!IsStunSpell(spell_id)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::AERains:
|
||||||
|
if (IsAERainNukeSpell(spell_id) && !IsStunSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::PBAENuke:
|
||||||
|
if (IsPBAENukeSpell(spell_id) && !IsStunSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
case BotSpellTypes::ResistBuffs:
|
case BotSpellTypes::ResistBuffs:
|
||||||
case BotSpellTypes::PetResistBuffs:
|
case BotSpellTypes::PetResistBuffs:
|
||||||
if (IsResistanceBuffSpell(spell_id)) {
|
if (IsResistanceBuffSpell(spell_id)) {
|
||||||
@@ -11587,44 +11849,7 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) {
|
|||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::DamageShields:
|
case BotSpellTypes::DamageShields:
|
||||||
case BotSpellTypes::PetDamageShields:
|
case BotSpellTypes::PetDamageShields:
|
||||||
if (IsEffectInSpell(spell_id, SE_DamageShield)) {
|
if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_DamageShield)) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
case BotSpellTypes::PBAENuke:
|
|
||||||
if (
|
|
||||||
IsPBAENukeSpell(spell_id) &&
|
|
||||||
!IsStunSpell(spell_id)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
case BotSpellTypes::AERains:
|
|
||||||
if (
|
|
||||||
IsAERainNukeSpell(spell_id) &&
|
|
||||||
!IsStunSpell(spell_id)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case BotSpellTypes::AEStun:
|
|
||||||
case BotSpellTypes::Stun:
|
|
||||||
if (IsStunSpell(spell_id)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
case BotSpellTypes::AENukes:
|
|
||||||
case BotSpellTypes::Nuke:
|
|
||||||
if (!IsStunSpell(spell_id)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
case BotSpellTypes::Lull:
|
|
||||||
if (IsHarmonySpell(spell_id)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11632,13 +11857,18 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) {
|
|||||||
case BotSpellTypes::Teleport:
|
case BotSpellTypes::Teleport:
|
||||||
if (
|
if (
|
||||||
IsBeneficialSpell(spell_id) &&
|
IsBeneficialSpell(spell_id) &&
|
||||||
(
|
(IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))
|
||||||
IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate)
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Lull:
|
||||||
|
case BotSpellTypes::AELull:
|
||||||
|
if (IsHarmonySpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::Succor:
|
case BotSpellTypes::Succor:
|
||||||
if (
|
if (
|
||||||
@@ -11662,25 +11892,21 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::Levitate:
|
case BotSpellTypes::Levitate:
|
||||||
if (
|
if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Levitate)) {
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_Levitate)
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::Rune:
|
case BotSpellTypes::Rune:
|
||||||
if (
|
if (IsBeneficialSpell(spell_id) &&
|
||||||
IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) ||
|
(IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune))
|
||||||
IsEffectInSpell(spell_id, SE_Rune)
|
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::WaterBreathing:
|
case BotSpellTypes::WaterBreathing:
|
||||||
if (IsEffectInSpell(spell_id, SE_WaterBreathing)) {
|
if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11688,29 +11914,22 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) {
|
|||||||
case BotSpellTypes::Size:
|
case BotSpellTypes::Size:
|
||||||
if (
|
if (
|
||||||
IsBeneficialSpell(spell_id) &&
|
IsBeneficialSpell(spell_id) &&
|
||||||
(
|
(IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))
|
||||||
IsEffectInSpell(spell_id, SE_ModelSize) ||
|
|
||||||
IsEffectInSpell(spell_id, SE_ChangeHeight)
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::Invisibility:
|
case BotSpellTypes::Invisibility:
|
||||||
if (
|
if (IsBeneficialSpell(spell_id) &&
|
||||||
IsEffectInSpell(spell_id, SE_SeeInvis) ||
|
(IsEffectInSpell(spell_id, SE_SeeInvis) ||IsInvisibleSpell(spell_id))
|
||||||
IsInvisibleSpell(spell_id)
|
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::MovementSpeed:
|
case BotSpellTypes::MovementSpeed:
|
||||||
if (
|
if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) {
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_MovementSpeed)
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11718,7 +11937,13 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) {
|
|||||||
case BotSpellTypes::SendHome:
|
case BotSpellTypes::SendHome:
|
||||||
if (
|
if (
|
||||||
IsBeneficialSpell(spell_id) &&
|
IsBeneficialSpell(spell_id) &&
|
||||||
IsEffectInSpell(spell_id, SE_GateToHomeCity)
|
(
|
||||||
|
IsEffectInSpell(spell_id, SE_GateToHomeCity) ||
|
||||||
|
(
|
||||||
|
teleport_zone.compare("") &&
|
||||||
|
(IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))
|
||||||
|
)
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -11806,21 +12031,26 @@ void Bot::DoCombatPositioning(
|
|||||||
bool behind_mob,
|
bool behind_mob,
|
||||||
bool front_mob
|
bool front_mob
|
||||||
) {
|
) {
|
||||||
if (HasTargetReflection()) {
|
if (!tar->IsFeared()) {
|
||||||
if (tar->IsRooted() && !IsTaunting()) { // Move non-taunters out of range
|
bool is_too_close = tar_distance < melee_distance_min;
|
||||||
if (tar_distance <= melee_distance_max) {
|
bool los_adjust = !HasRequiredLoSForPositioning(tar);
|
||||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 1.25f), GetBehindMob(), false)) {
|
|
||||||
RunToGoalWithJitter(Goal);
|
|
||||||
|
|
||||||
|
if (tar->IsRooted() && !IsTaunting()) { // Move non-taunting melee out of range
|
||||||
|
bool rooted_adjust = tar_distance <= melee_distance_max && HasTargetReflection();
|
||||||
|
|
||||||
|
if (rooted_adjust) {
|
||||||
|
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 1.25f), GetBehindMob(), !GetBehindMob())) {
|
||||||
|
RunToGoalWithJitter(Goal);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (
|
else {
|
||||||
tar_distance < melee_distance_min ||
|
if (IsTaunting()) { // Taunting adjustments
|
||||||
(!front_mob && IsTaunting())
|
bool taunting_adjust = (!front_mob || is_too_close || los_adjust);
|
||||||
) { // Back up any bots that are too close or if they're taunting and not in front of the mob
|
|
||||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), (IsTaunting() || !GetBehindMob()))) {
|
if (taunting_adjust) {
|
||||||
|
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, true)) {
|
||||||
RunToGoalWithJitter(Goal);
|
RunToGoalWithJitter(Goal);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -11828,52 +12058,26 @@ void Bot::DoCombatPositioning(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!tar->IsFeared()) {
|
if (tar->IsEnraged() && !stop_melee_level && !IsBotRanged()) { // Move non-taunting melee bots behind target during enrage
|
||||||
if (
|
bool enraged_adjust = !behind_mob || is_too_close || los_adjust;
|
||||||
tar_distance < melee_distance_min ||
|
|
||||||
(GetBehindMob() && !behind_mob) ||
|
|
||||||
(IsTaunting() && !front_mob) ||
|
|
||||||
!HasRequiredLoSForPositioning(tar)
|
|
||||||
) { // Regular adjustment
|
|
||||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), (IsTaunting() || !GetBehindMob()))) {
|
|
||||||
RunToGoalWithJitter(Goal);
|
|
||||||
|
|
||||||
return;
|
if (enraged_adjust) {
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
tar->IsEnraged() &&
|
|
||||||
!IsTaunting() &&
|
|
||||||
!stop_melee_level &&
|
|
||||||
!behind_mob
|
|
||||||
) { // Move non-taunting melee bots behind target during enrage
|
|
||||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) {
|
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) {
|
||||||
RunToGoalWithJitter(Goal);
|
RunToGoalWithJitter(Goal);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsTaunting()) { // Taunting adjustments
|
|
||||||
Mob* mob_tar = tar->GetTarget();
|
|
||||||
|
|
||||||
if (mob_tar) {
|
|
||||||
if (
|
|
||||||
RuleB(Bots, TauntingBotsFollowTopHate) &&
|
|
||||||
(Distance(m_Position, mob_tar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate))
|
|
||||||
) { // If enabled, taunting bots will stick to top hate
|
|
||||||
Goal = mob_tar->GetPosition();
|
|
||||||
RunToGoalWithJitter(Goal);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
else { // Otherwise, stick to any other bots that are taunting
|
else { // Regular adjustments
|
||||||
if (
|
bool regular_adjust =
|
||||||
mob_tar->IsBot() &&
|
is_too_close ||
|
||||||
mob_tar->CastToBot()->IsTaunting() &&
|
los_adjust ||
|
||||||
(Distance(m_Position, mob_tar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate))
|
(!GetBehindMob() && !front_mob) ||
|
||||||
) {
|
(GetBehindMob() && !behind_mob);
|
||||||
Goal = mob_tar->GetPosition();
|
|
||||||
|
if (regular_adjust) {
|
||||||
|
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), !GetBehindMob())) {
|
||||||
RunToGoalWithJitter(Goal);
|
RunToGoalWithJitter(Goal);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -11885,8 +12089,6 @@ void Bot::DoCombatPositioning(
|
|||||||
}
|
}
|
||||||
|
|
||||||
DoFaceCheckNoJitter(tar);
|
DoFaceCheckNoJitter(tar);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only, bool front_only, bool bypass_los) {
|
bool Bot::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only, bool front_only, bool bypass_los) {
|
||||||
@@ -11983,7 +12185,11 @@ bool Bot::RequiresLoSForPositioning() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
|
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
|
||||||
if (IsBotSpellTypeDetrimental(i) && !GetSpellTypeHold(i)) {
|
if (IsHealBotSpellType(i) || i == BotSpellTypes::PBAENuke) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GetSpellTypeHold(i)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11996,7 +12202,7 @@ bool Bot::HasRequiredLoSForPositioning(Mob* tar) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RequiresLoSForPositioning() && !DoLosChecks(tar)) {
|
if (RequiresLoSForPositioning() && !HasLoS()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12011,10 +12217,6 @@ bool Bot::HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob*
|
|||||||
for (auto& close_mob : caster->m_close_mobs) {
|
for (auto& close_mob : caster->m_close_mobs) {
|
||||||
Mob* m = close_mob.second;
|
Mob* m = close_mob.second;
|
||||||
|
|
||||||
if (tar == m) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (spell_type) {
|
switch (spell_type) {
|
||||||
case BotSpellTypes::AELull:
|
case BotSpellTypes::AELull:
|
||||||
if (m->GetSpecialAbility(SpecialAbility::PacifyImmunity)) {
|
if (m->GetSpecialAbility(SpecialAbility::PacifyImmunity)) {
|
||||||
@@ -12106,8 +12308,6 @@ bool Bot::HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob*
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetHasLoS(true);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -235,7 +235,7 @@ static std::map<uint16, std::string> botSubType_names = {
|
|||||||
struct CombatRangeInput {
|
struct CombatRangeInput {
|
||||||
Mob* target;
|
Mob* target;
|
||||||
float target_distance;
|
float target_distance;
|
||||||
uint8 stop_melee_level;
|
bool stop_melee_level;
|
||||||
const EQ::ItemInstance* p_item;
|
const EQ::ItemInstance* p_item;
|
||||||
const EQ::ItemInstance* s_item;
|
const EQ::ItemInstance* s_item;
|
||||||
};
|
};
|
||||||
@@ -683,7 +683,6 @@ public:
|
|||||||
void SetSitManaPct(uint8 value) { _SitManaPct = value; }
|
void SetSitManaPct(uint8 value) { _SitManaPct = value; }
|
||||||
|
|
||||||
// Spell lists
|
// Spell lists
|
||||||
void CheckBotSpells();
|
|
||||||
std::list<BotSpellTypeOrder> GetSpellTypesPrioritized(uint8 priority_type);
|
std::list<BotSpellTypeOrder> GetSpellTypesPrioritized(uint8 priority_type);
|
||||||
static uint16 GetParentSpellType(uint16 spell_type);
|
static uint16 GetParentSpellType(uint16 spell_type);
|
||||||
static bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id);
|
static bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id);
|
||||||
|
|||||||
@@ -220,6 +220,11 @@ int bot_command_init(void)
|
|||||||
std::vector<std::pair<std::string, uint8>> injected_bot_command_settings;
|
std::vector<std::pair<std::string, uint8>> injected_bot_command_settings;
|
||||||
std::vector<std::string> orphaned_bot_command_settings;
|
std::vector<std::string> orphaned_bot_command_settings;
|
||||||
|
|
||||||
|
if (RuleB(Bots, RunSpellTypeChecksOnBoot)) {
|
||||||
|
LogBotSpellTypeChecks("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline.");
|
||||||
|
database.botdb.CheckBotSpells();
|
||||||
|
}
|
||||||
|
|
||||||
database.botdb.MapCommandedSpellTypeMinLevels();
|
database.botdb.MapCommandedSpellTypeMinLevels();
|
||||||
|
|
||||||
for (auto bcs_iter : bot_command_settings) {
|
for (auto bcs_iter : bot_command_settings) {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void bot_command_attack(Client *c, const Seperator *sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->DoLosChecks(target_mob)) {
|
if (RuleB(Bots, BotsRequireLoS) && !c->DoLosChecks(target_mob)) {
|
||||||
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -525,6 +525,9 @@ void bot_command_cast(Client* c, const Seperator* sep)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool requires_los = !(IsAnyHealSpell(spell_id) && !IsPBAESpell(spell_id));
|
||||||
|
bot_iter->SetHasLoS(requires_los ? bot_iter->DoLosChecks(new_tar) : true);
|
||||||
|
|
||||||
if (!bot_iter->AttemptAACastSpell(tar, spell_id, rank)) {
|
if (!bot_iter->AttemptAACastSpell(tar, spell_id, rank)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -543,6 +546,9 @@ void bot_command_cast(Client* c, const Seperator* sep)
|
|||||||
tar = bot_iter;
|
tar = bot_iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool los_required = bot_iter != tar && !IsAnyHealSpell(chosen_spell_id) && !IsPBAESpell(chosen_spell_id);
|
||||||
|
bot_iter->SetHasLoS(los_required ? bot_iter->DoLosChecks(new_tar) : true);
|
||||||
|
|
||||||
if (bot_iter->AttemptForcedCastSpell(tar, chosen_spell_id)) {
|
if (bot_iter->AttemptForcedCastSpell(tar, chosen_spell_id)) {
|
||||||
if (!first_found) {
|
if (!first_found) {
|
||||||
first_found = bot_iter;
|
first_found = bot_iter;
|
||||||
@@ -556,6 +562,7 @@ void bot_command_cast(Client* c, const Seperator* sep)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bot_iter->SetCommandedSpell(true);
|
bot_iter->SetCommandedSpell(true);
|
||||||
|
bot_iter->SetHasLoS(BotSpellTypeRequiresLoS(spell_type) ? bot_iter->DoLosChecks(new_tar) : true);
|
||||||
|
|
||||||
if (bot_iter->AICastSpell(new_tar, 100, spell_type, sub_target_type, sub_type)) {
|
if (bot_iter->AICastSpell(new_tar, 100, spell_type, sub_target_type, sub_type)) {
|
||||||
if (!first_found) {
|
if (!first_found) {
|
||||||
|
|||||||
@@ -197,7 +197,11 @@ void bot_command_depart(Client* c, const Seperator* sep)
|
|||||||
|
|
||||||
bot_iter->SetCommandedSpell(true);
|
bot_iter->SetCommandedSpell(true);
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(itr->SpellId, bot_iter->HasLoS())) {
|
if (!IsValidSpell(itr->SpellId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(BotSpellTypes::Teleport, itr->SpellId) && !bot_iter->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ void bot_command_precombat(Client* c, const Seperator* sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->DoLosChecks(c->GetTarget())) {
|
if (RuleB(Bots, BotsRequireLoS) && !c->DoLosChecks(c->GetTarget())) {
|
||||||
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ void bot_command_pull(Client *c, const Seperator *sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->DoLosChecks(target_mob)) {
|
if (RuleB(Bots, BotsRequireLoS) && !c->DoLosChecks(target_mob)) {
|
||||||
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -2523,6 +2523,142 @@ bool BotDatabase::DeleteBotBlockedBuffs(const uint32 bot_id)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BotDatabase::CheckBotSpells() {
|
||||||
|
auto spell_list = BotSpellsEntriesRepository::All(content_db);
|
||||||
|
uint16 spell_id;
|
||||||
|
SPDat_Spell_Struct spell;
|
||||||
|
|
||||||
|
for (const auto& s : spell_list) {
|
||||||
|
if (!IsValidSpell(s.spell_id)) {
|
||||||
|
LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
spell = spells[s.spell_id];
|
||||||
|
spell_id = spell.id;
|
||||||
|
|
||||||
|
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] >= 255) {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.minlevel) {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the min level is currently set to {}.",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id,
|
||||||
|
s.minlevel
|
||||||
|
);
|
||||||
|
|
||||||
|
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]",
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
spell_id,
|
||||||
|
s.npc_spells_id,
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
s.minlevel,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] < s.minlevel) {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] could be used starting at level {} for a {} [#{}] instead of the current min level of {}.",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id,
|
||||||
|
s.minlevel
|
||||||
|
);
|
||||||
|
|
||||||
|
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]",
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
spell_id,
|
||||||
|
s.npc_spells_id,
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
s.minlevel,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.maxlevel) {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the max level is currently set to {}.",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id,
|
||||||
|
s.maxlevel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 correct_type = GetCorrectBotSpellType(s.type, spell_id);
|
||||||
|
|
||||||
|
if (RuleB(Bots, UseParentSpellTypeForChecks)) {
|
||||||
|
uint16 parent_type = Bot::GetParentSpellType(correct_type);
|
||||||
|
|
||||||
|
if (s.type == parent_type || s.type == correct_type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (correct_type != parent_type) {
|
||||||
|
correct_type = parent_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (IsPetBotSpellType(s.type)) {
|
||||||
|
correct_type = GetPetBotSpellType(correct_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsPetBotSpellType(correct_type) && (spell.target_type != ST_Pet && spell.target_type != ST_SummonedPet)) {
|
||||||
|
correct_type = Bot::GetParentSpellType(correct_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (correct_type == s.type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (correct_type == UINT16_MAX) {
|
||||||
|
LogBotSpellTypeChecks(
|
||||||
|
"{} [#{}] is incorrect. It is currently set as {} [#{}] but the correct type is unknown.",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
Bot::GetSpellTypeNameByID(s.type),
|
||||||
|
s.type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
Bot::GetSpellTypeNameByID(s.type),
|
||||||
|
s.type,
|
||||||
|
Bot::GetSpellTypeNameByID(correct_type),
|
||||||
|
correct_type
|
||||||
|
);
|
||||||
|
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spell_id` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]",
|
||||||
|
correct_type,
|
||||||
|
spell_id,
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
Bot::GetSpellTypeNameByID(s.type),
|
||||||
|
s.type,
|
||||||
|
Bot::GetSpellTypeNameByID(correct_type),
|
||||||
|
correct_type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BotDatabase::MapCommandedSpellTypeMinLevels() {
|
void BotDatabase::MapCommandedSpellTypeMinLevels() {
|
||||||
commanded_spell_type_min_levels.clear();
|
commanded_spell_type_min_levels.clear();
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ public:
|
|||||||
bool SaveBotSettings(Mob* m);
|
bool SaveBotSettings(Mob* m);
|
||||||
bool DeleteBotSettings(const uint32 bot_id);
|
bool DeleteBotSettings(const uint32 bot_id);
|
||||||
|
|
||||||
|
void CheckBotSpells();
|
||||||
void MapCommandedSpellTypeMinLevels();
|
void MapCommandedSpellTypeMinLevels();
|
||||||
std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>> GetCommandedSpellTypesMinLevels() { return commanded_spell_type_min_levels; }
|
std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>> GetCommandedSpellTypesMinLevels() { return commanded_spell_type_min_levels; }
|
||||||
|
|
||||||
|
|||||||
+87
-198
@@ -41,7 +41,10 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chance < 100 && zone->random.Int(0, 100) > chance) {
|
if (
|
||||||
|
!IsCommandedSpell() &&
|
||||||
|
zone->random.Int(0, 100) > chance
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,10 +64,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ
|
|||||||
bot_spell.SpellIndex = 0;
|
bot_spell.SpellIndex = 0;
|
||||||
bot_spell.ManaCost = 0;
|
bot_spell.ManaCost = 0;
|
||||||
|
|
||||||
if (BotSpellTypeRequiresLoS(spell_type) && tar != this) {
|
if (!BotSpellTypeRequiresLoS(spell_type) || tar == this) {
|
||||||
SetHasLoS(DoLosChecks(tar));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SetHasLoS(true);
|
SetHasLoS(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,8 +218,11 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ
|
|||||||
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, (IsAEBotSpellType(spell_type) || sub_target_type == CommandedSubTypes::AETarget), sub_target_type, sub_type);
|
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, (IsAEBotSpellType(spell_type) || sub_target_type == CommandedSubTypes::AETarget), sub_target_type, sub_type);
|
||||||
|
|
||||||
for (const auto& s : bot_spell_list) {
|
for (const auto& s : bot_spell_list) {
|
||||||
|
if (!IsValidSpell(s.SpellId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) {
|
if (BotRequiresLoSToCast(spell_type, s.SpellId) && !HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +276,11 @@ bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
|
|||||||
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, IsAEBotSpellType(spell_type));
|
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, IsAEBotSpellType(spell_type));
|
||||||
|
|
||||||
for (const auto& s : bot_spell_list) {
|
for (const auto& s : bot_spell_list) {
|
||||||
if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) {
|
if (!IsValidSpell(s.SpellId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, s.SpellId) && !HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +288,7 @@ bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
|
|||||||
Mob* add_mob = GetFirstIncomingMobToMez(this, s.SpellId, spell_type, IsAEBotSpellType(spell_type));
|
Mob* add_mob = GetFirstIncomingMobToMez(this, s.SpellId, spell_type, IsAEBotSpellType(spell_type));
|
||||||
|
|
||||||
if (!add_mob) {
|
if (!add_mob) {
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
tar = add_mob;
|
tar = add_mob;
|
||||||
@@ -327,7 +334,11 @@ bool Bot::BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe
|
|||||||
|
|
||||||
bot_spell = GetBestBotSpellForCure(this, tar, spell_type);
|
bot_spell = GetBestBotSpellForCure(this, tar, spell_type);
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell.SpellId) && !HasLoS()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,7 +408,11 @@ bool Bot::BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
|
|||||||
bot_spell = GetFirstBotSpellBySpellType(this, spell_type);
|
bot_spell = GetFirstBotSpellBySpellType(this, spell_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell.SpellId) && !HasLoS()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,6 +433,10 @@ bool Bot::BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type) {
|
bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type) {
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell.SpellId) && !HasLoS()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (spell_type == BotSpellTypes::Stun || spell_type == BotSpellTypes::AEStun) {
|
if (spell_type == BotSpellTypes::Stun || spell_type == BotSpellTypes::AEStun) {
|
||||||
uint8 stun_chance = (tar->IsCasting() ? RuleI(Bots, StunCastChanceIfCasting) : RuleI(Bots, StunCastChanceNormal));
|
uint8 stun_chance = (tar->IsCasting() ? RuleI(Bots, StunCastChanceIfCasting) : RuleI(Bots, StunCastChanceNormal));
|
||||||
|
|
||||||
@@ -435,24 +454,24 @@ bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe
|
|||||||
bot_spell = GetBestBotSpellForStunByTargetType(this, ST_TargetOptional, spell_type, IsAEBotSpellType(spell_type), tar);
|
bot_spell = GetBestBotSpellForStunByTargetType(this, ST_TargetOptional, spell_type, IsAEBotSpellType(spell_type), tar);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||||
bot_spell = GetBestBotSpellForNukeByBodyType(this, tar->GetBodyType(), spell_type, IsAEBotSpellType(spell_type), tar);
|
bot_spell = GetBestBotSpellForNukeByBodyType(this, tar->GetBodyType(), spell_type, IsAEBotSpellType(spell_type), tar);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS()) && spell_type == BotSpellTypes::Nuke && bot_class == Class::Wizard) {
|
if (spell_type == BotSpellTypes::Nuke && bot_class == Class::Wizard && !IsValidSpell(bot_spell.SpellId)) {
|
||||||
bot_spell = GetBestBotWizardNukeSpellByTargetResists(this, tar, spell_type);
|
bot_spell = GetBestBotWizardNukeSpellByTargetResists(this, tar, spell_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||||
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, IsAEBotSpellType(spell_type));
|
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, IsAEBotSpellType(spell_type));
|
||||||
|
|
||||||
for (const auto& s : bot_spell_list) {
|
for (const auto& s : bot_spell_list) {
|
||||||
if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) {
|
if (!IsValidSpell(s.SpellId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,7 +581,9 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
|
|||||||
// Allow bots to cast buff spells even if they are out of mana
|
// Allow bots to cast buff spells even if they are out of mana
|
||||||
if (
|
if (
|
||||||
RuleB(Bots, FinishBuffing) &&
|
RuleB(Bots, FinishBuffing) &&
|
||||||
manaCost > hasMana && AIBot_spells[i].type == BotSpellTypes::Buff
|
manaCost > hasMana &&
|
||||||
|
!IsEngaged() &&
|
||||||
|
IsBotBuffSpellType(AIBot_spells[i].type)
|
||||||
) {
|
) {
|
||||||
SetMana(manaCost);
|
SetMana(manaCost);
|
||||||
}
|
}
|
||||||
@@ -908,7 +929,11 @@ std::list<BotSpell> Bot::GetBotSpellsForSpellEffect(Bot* caster, uint16 spell_ty
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -946,7 +971,11 @@ std::list<BotSpell> Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* caster, ui
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -987,7 +1016,11 @@ std::list<BotSpell> Bot::GetBotSpellsBySpellType(Bot* caster, uint16 spell_type)
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1016,7 +1049,11 @@ std::vector<BotSpell_wPriority> Bot::GetPrioritizedBotSpellsBySpellType(Bot* cas
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1103,7 +1140,11 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* caster, uint16 spell_type) {
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1135,7 +1176,6 @@ BotSpell Bot::GetBestBotSpellForVeryFastHeal(Bot* caster, Mob* tar, uint16 spell
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
||||||
|
|
||||||
for (auto bot_spell_list_itr : bot_spell_list) {
|
for (auto bot_spell_list_itr : bot_spell_list) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (
|
if (
|
||||||
IsVeryFastHealSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
IsVeryFastHealSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
||||||
result.SpellId = bot_spell_list_itr.SpellId;
|
result.SpellId = bot_spell_list_itr.SpellId;
|
||||||
@@ -1161,7 +1201,6 @@ BotSpell Bot::GetBestBotSpellForFastHeal(Bot* caster, Mob* tar, uint16 spell_typ
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
||||||
|
|
||||||
for (auto bot_spell_list_itr : bot_spell_list) {
|
for (auto bot_spell_list_itr : bot_spell_list) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsFastHealSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
if (IsFastHealSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
||||||
result.SpellId = bot_spell_list_itr.SpellId;
|
result.SpellId = bot_spell_list_itr.SpellId;
|
||||||
result.SpellIndex = bot_spell_list_itr.SpellIndex;
|
result.SpellIndex = bot_spell_list_itr.SpellIndex;
|
||||||
@@ -1186,7 +1225,6 @@ BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* caster, Mob* tar, uint16 spell
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_HealOverTime);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_HealOverTime);
|
||||||
|
|
||||||
for (auto bot_spell_list_itr : bot_spell_list) {
|
for (auto bot_spell_list_itr : bot_spell_list) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsHealOverTimeSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
if (IsHealOverTimeSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
||||||
result.SpellId = bot_spell_list_itr.SpellId;
|
result.SpellId = bot_spell_list_itr.SpellId;
|
||||||
result.SpellIndex = bot_spell_list_itr.SpellIndex;
|
result.SpellIndex = bot_spell_list_itr.SpellIndex;
|
||||||
@@ -1243,7 +1281,6 @@ BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* caster, Mob* tar, u
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsRegularSingleTargetHealSpell(bot_spell_list_itr->SpellId) && caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type)) {
|
if (IsRegularSingleTargetHealSpell(bot_spell_list_itr->SpellId) && caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type)) {
|
||||||
result.SpellId = bot_spell_list_itr->SpellId;
|
result.SpellId = bot_spell_list_itr->SpellId;
|
||||||
result.SpellIndex = bot_spell_list_itr->SpellIndex;
|
result.SpellIndex = bot_spell_list_itr->SpellIndex;
|
||||||
@@ -1268,7 +1305,6 @@ BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* caster, Mob* tar, uint16
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsRegularSingleTargetHealSpell(bot_spell_list_itr->SpellId) && caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type)) {
|
if (IsRegularSingleTargetHealSpell(bot_spell_list_itr->SpellId) && caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type)) {
|
||||||
result.SpellId = bot_spell_list_itr->SpellId;
|
result.SpellId = bot_spell_list_itr->SpellId;
|
||||||
result.SpellIndex = bot_spell_list_itr->SpellIndex;
|
result.SpellIndex = bot_spell_list_itr->SpellIndex;
|
||||||
@@ -1298,7 +1334,6 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* caster, Mob* tar, uint16 spell_ty
|
|||||||
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsRegularGroupHealSpell(bot_spell_list_itr->SpellId)) {
|
if (IsRegularGroupHealSpell(bot_spell_list_itr->SpellId)) {
|
||||||
uint16 spell_id = bot_spell_list_itr->SpellId;
|
uint16 spell_id = bot_spell_list_itr->SpellId;
|
||||||
|
|
||||||
@@ -1337,7 +1372,6 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* caster, Mob* tar, uint16
|
|||||||
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) {
|
if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) {
|
||||||
uint16 spell_id = bot_spell_list_itr->SpellId;
|
uint16 spell_id = bot_spell_list_itr->SpellId;
|
||||||
|
|
||||||
@@ -1376,7 +1410,6 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* caster, Mob* tar, uint16
|
|||||||
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsGroupCompleteHealSpell(bot_spell_list_itr->SpellId)) {
|
if (IsGroupCompleteHealSpell(bot_spell_list_itr->SpellId)) {
|
||||||
uint16 spell_id = bot_spell_list_itr->SpellId;
|
uint16 spell_id = bot_spell_list_itr->SpellId;
|
||||||
|
|
||||||
@@ -1410,7 +1443,6 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* caster, uint16 spell_type) {
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Mez);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Mez);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (
|
if (
|
||||||
IsMesmerizeSpell(bot_spell_list_itr->SpellId) &&
|
IsMesmerizeSpell(bot_spell_list_itr->SpellId) &&
|
||||||
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)
|
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)
|
||||||
@@ -1433,11 +1465,10 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
|
|||||||
if (caster && caster->GetOwner()) {
|
if (caster && caster->GetOwner()) {
|
||||||
int spell_range = caster->GetActSpellRange(spell_id, spells[spell_id].range);
|
int spell_range = caster->GetActSpellRange(spell_id, spells[spell_id].range);
|
||||||
int spell_ae_range = caster->GetAOERange(spell_id);
|
int spell_ae_range = caster->GetAOERange(spell_id);
|
||||||
int buff_count = 0;
|
bool is_pbae_spell = IsPBAESpell(spell_id);
|
||||||
NPC* npc = nullptr;
|
NPC* npc = nullptr;
|
||||||
|
|
||||||
for (auto& close_mob : caster->m_close_mobs) {
|
for (auto& close_mob : caster->m_close_mobs) {
|
||||||
buff_count = 0;
|
|
||||||
npc = close_mob.second->CastToNPC();
|
npc = close_mob.second->CastToNPC();
|
||||||
|
|
||||||
if (!npc) {
|
if (!npc) {
|
||||||
@@ -1448,30 +1479,30 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_pbae_spell) {
|
||||||
|
if (spell_ae_range < Distance(caster->GetPosition(), npc->GetPosition())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (spell_range < Distance(caster->GetPosition(), npc->GetPosition())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (AE) {
|
if (AE) {
|
||||||
int target_count = 0;
|
int target_count = 0;
|
||||||
|
|
||||||
for (auto& close_mob : caster->m_close_mobs) {
|
for (auto& close_mob : caster->m_close_mobs) {
|
||||||
Mob* m = close_mob.second;
|
Mob* m = close_mob.second;
|
||||||
|
|
||||||
if (npc == m) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!caster->IsValidMezTarget(caster->GetOwner(), m, spell_id)) {
|
if (!caster->IsValidMezTarget(caster->GetOwner(), m, spell_id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPBAESpell(spell_id)) {
|
if (spell_ae_range < Distance(npc->GetPosition(), m->GetPosition())) {
|
||||||
if (spell_ae_range < Distance(caster->GetPosition(), m->GetPosition())) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (spell_range < Distance(m->GetPosition(), npc->GetPosition())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (caster->CastChecks(spell_id, m, spell_type, true, true)) {
|
if (caster->CastChecks(spell_id, m, spell_type, true, true)) {
|
||||||
++target_count;
|
++target_count;
|
||||||
@@ -1486,11 +1517,6 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zone->random.Int(1, 100) < RuleI(Bots, AEMezChance)) {
|
|
||||||
caster->SetSpellTypeRecastTimer(spell_type, RuleI(Bots, MezFailDelay));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = npc;
|
result = npc;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1502,18 +1528,10 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zone->random.Int(1, 100) < RuleI(Bots, MezChance)) {
|
|
||||||
caster->SetSpellTypeRecastTimer(spell_type, RuleI(Bots, MezAEFailDelay));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = npc;
|
result = npc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
caster->SetHasLoS(true);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1534,7 +1552,6 @@ BotSpell Bot::GetBestBotMagicianPetSpell(Bot* caster, uint16 spell_type) {
|
|||||||
std::string pet_type = GetBotMagicianPetType(caster);
|
std::string pet_type = GetBotMagicianPetType(caster);
|
||||||
|
|
||||||
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (
|
if (
|
||||||
IsSummonPetSpell(bot_spell_list_itr->SpellId) &&
|
IsSummonPetSpell(bot_spell_list_itr->SpellId) &&
|
||||||
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId) &&
|
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId) &&
|
||||||
@@ -1716,7 +1733,6 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* caster, SpellTargetType ta
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffectAndTargetType(caster, spell_type, SE_CurrentHP, target_type);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffectAndTargetType(caster, spell_type, SE_CurrentHP, target_type);
|
||||||
|
|
||||||
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsPureNukeSpell(bot_spell_list_itr->SpellId) || IsDamageSpell(bot_spell_list_itr->SpellId)) {
|
if (IsPureNukeSpell(bot_spell_list_itr->SpellId) || IsDamageSpell(bot_spell_list_itr->SpellId)) {
|
||||||
if (!AE && IsAnyAESpell(bot_spell_list_itr->SpellId) && !IsGroupSpell(bot_spell_list_itr->SpellId)) {
|
if (!AE && IsAnyAESpell(bot_spell_list_itr->SpellId) && !IsGroupSpell(bot_spell_list_itr->SpellId)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1729,7 +1745,6 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* caster, SpellTargetType ta
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
caster->IsCommandedSpell() ||
|
caster->IsCommandedSpell() ||
|
||||||
!AE ||
|
!AE ||
|
||||||
@@ -1766,7 +1781,6 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* caster, SpellTargetType ta
|
|||||||
|
|
||||||
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr)
|
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr)
|
||||||
{
|
{
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsStunSpell(bot_spell_list_itr->SpellId)) {
|
if (IsStunSpell(bot_spell_list_itr->SpellId)) {
|
||||||
if (!AE && IsAnyAESpell(bot_spell_list_itr->SpellId) && !IsGroupSpell(bot_spell_list_itr->SpellId)) {
|
if (!AE && IsAnyAESpell(bot_spell_list_itr->SpellId) && !IsGroupSpell(bot_spell_list_itr->SpellId)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1830,7 +1844,6 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* caster, Mob* target,
|
|||||||
bool spell_selected = false;
|
bool spell_selected = false;
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (!caster->IsValidSpellRange(bot_spell_list_itr->SpellId, target)) {
|
if (!caster->IsValidSpellRange(bot_spell_list_itr->SpellId, target)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1887,8 +1900,6 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* caster, Mob* target,
|
|||||||
|
|
||||||
if (!spell_selected) {
|
if (!spell_selected) {
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
|
|
||||||
if (caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)) {
|
if (caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)) {
|
||||||
if (caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type)) {
|
if (caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type)) {
|
||||||
spell_selected = true;
|
spell_selected = true;
|
||||||
@@ -1926,7 +1937,11 @@ BotSpell Bot::GetDebuffBotSpell(Bot* caster, Mob *tar, uint16 spell_type) {
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1972,7 +1987,11 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* caster, Mob *tar, uint16 spell
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2091,7 +2110,6 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spell_type)
|
|||||||
case BotSpellTypes::AERains:
|
case BotSpellTypes::AERains:
|
||||||
case BotSpellTypes::AEStun:
|
case BotSpellTypes::AEStun:
|
||||||
case BotSpellTypes::AESnare:
|
case BotSpellTypes::AESnare:
|
||||||
case BotSpellTypes::AEMez:
|
|
||||||
case BotSpellTypes::AESlow:
|
case BotSpellTypes::AESlow:
|
||||||
case BotSpellTypes::AEDebuff:
|
case BotSpellTypes::AEDebuff:
|
||||||
case BotSpellTypes::AEFear:
|
case BotSpellTypes::AEFear:
|
||||||
@@ -2157,6 +2175,8 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spell_type)
|
|||||||
case BotSpellTypes::PetVeryFastHeals:
|
case BotSpellTypes::PetVeryFastHeals:
|
||||||
case BotSpellTypes::PetHoTHeals:
|
case BotSpellTypes::PetHoTHeals:
|
||||||
return RuleI(Bots, PercentChanceToCastHeal);
|
return RuleI(Bots, PercentChanceToCastHeal);
|
||||||
|
case BotSpellTypes::AEMez:
|
||||||
|
return RuleI(Bots, PercentChanceToCastAEMez);
|
||||||
default:
|
default:
|
||||||
return RuleI(Bots, PercentChanceToCastOtherType);
|
return RuleI(Bots, PercentChanceToCastOtherType);
|
||||||
}
|
}
|
||||||
@@ -2821,7 +2841,6 @@ BotSpell Bot::GetBestBotSpellForRez(Bot* caster, Mob* target, uint16 spell_type)
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Revive);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Revive);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (
|
if (
|
||||||
IsResurrectSpell(bot_spell_list_itr->SpellId) &&
|
IsResurrectSpell(bot_spell_list_itr->SpellId) &&
|
||||||
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)
|
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)
|
||||||
@@ -2849,7 +2868,6 @@ BotSpell Bot::GetBestBotSpellForCharm(Bot* caster, Mob* target, uint16 spell_typ
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Charm);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Charm);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (
|
if (
|
||||||
IsCharmSpell(bot_spell_list_itr->SpellId) &&
|
IsCharmSpell(bot_spell_list_itr->SpellId) &&
|
||||||
caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type)
|
caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type)
|
||||||
@@ -2865,132 +2883,3 @@ BotSpell Bot::GetBestBotSpellForCharm(Bot* caster, Mob* target, uint16 spell_typ
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Bot::CheckBotSpells() {
|
|
||||||
auto spell_list = BotSpellsEntriesRepository::All(content_db);
|
|
||||||
uint16 spell_id;
|
|
||||||
SPDat_Spell_Struct spell;
|
|
||||||
uint16 correct_type;
|
|
||||||
uint16 parent_type;
|
|
||||||
|
|
||||||
for (const auto& s : spell_list) {
|
|
||||||
if (!IsValidSpell(s.spell_id)) {
|
|
||||||
LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
spell = spells[s.spell_id];
|
|
||||||
spell_id = spell.id;
|
|
||||||
|
|
||||||
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] >= 255) {
|
|
||||||
LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.minlevel) {
|
|
||||||
LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the min level is currently set to {}."
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX)
|
|
||||||
, s.npc_spells_id
|
|
||||||
, s.minlevel
|
|
||||||
);
|
|
||||||
|
|
||||||
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]"
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, spell_id
|
|
||||||
, s.npc_spells_id
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, s.minlevel
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX)
|
|
||||||
, s.npc_spells_id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] < s.minlevel) {
|
|
||||||
LogBotSpellTypeChecks("{} [#{}] could be used starting at level {} for a {} [#{}] instead of the current min level of {}."
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX)
|
|
||||||
, s.npc_spells_id
|
|
||||||
, s.minlevel
|
|
||||||
);
|
|
||||||
|
|
||||||
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]"
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, spell_id
|
|
||||||
, s.npc_spells_id
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, s.minlevel
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX)
|
|
||||||
, s.npc_spells_id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.maxlevel) {
|
|
||||||
LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the max level is currently set to {}."
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX)
|
|
||||||
, s.npc_spells_id
|
|
||||||
, s.maxlevel
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
correct_type = GetCorrectBotSpellType(s.type, spell_id);
|
|
||||||
parent_type = GetParentSpellType(correct_type);
|
|
||||||
|
|
||||||
if (RuleB(Bots, UseParentSpellTypeForChecks)) {
|
|
||||||
if (s.type == parent_type || s.type == correct_type) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (IsPetBotSpellType(s.type)) {
|
|
||||||
correct_type = GetPetBotSpellType(correct_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (correct_type == s.type) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (correct_type == UINT16_MAX) {
|
|
||||||
LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] but the correct type is unknown."
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, GetSpellTypeNameByID(s.type)
|
|
||||||
, s.type
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]"
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, GetSpellTypeNameByID(s.type)
|
|
||||||
, s.type
|
|
||||||
, GetSpellTypeNameByID(correct_type)
|
|
||||||
, correct_type
|
|
||||||
);
|
|
||||||
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spell_id` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]"
|
|
||||||
, correct_type
|
|
||||||
, spell_id
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, GetSpellTypeNameByID(s.type)
|
|
||||||
, s.type
|
|
||||||
, GetSpellTypeNameByID(correct_type)
|
|
||||||
, correct_type
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#include "../../zone.h"
|
||||||
|
|
||||||
|
inline void RunTest(const std::string &test_name, const std::string &expected, const std::string &actual)
|
||||||
|
{
|
||||||
|
if (expected == actual) {
|
||||||
|
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||||
|
} else {
|
||||||
|
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||||
|
std::cerr << " 📌 Expected: " << expected << "\n";
|
||||||
|
std::cerr << " ❌ Got: " << actual << "\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void RunTest(const std::string &test_name, bool expected, bool actual)
|
||||||
|
{
|
||||||
|
if (expected == actual) {
|
||||||
|
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||||
|
std::cerr << " 📌 Expected: " << (expected ? "true" : "false") << "\n";
|
||||||
|
std::cerr << " ❌ Got: " << (actual ? "true" : "false") << "\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void RunTest(const std::string &test_name, int expected, int actual)
|
||||||
|
{
|
||||||
|
if (expected == actual) {
|
||||||
|
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||||
|
std::cerr << " 📌 Expected: " << expected << "\n";
|
||||||
|
std::cerr << " ❌ Got: " << actual << "\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern Zone *zone;
|
||||||
|
|
||||||
|
inline void SetupZone(std::string zone_short_name, uint32 instance_id = 0) {
|
||||||
|
LogSys.SilenceConsoleLogging();
|
||||||
|
|
||||||
|
LogSys.log_settings[Logs::ZoneState].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||||
|
LogSys.log_settings[Logs::Info].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||||
|
LogSys.log_settings[Logs::Spawns].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||||
|
|
||||||
|
// boot shell zone for testing
|
||||||
|
Zone::Bootup(ZoneID(zone_short_name), 0, false);
|
||||||
|
zone->StopShutdownTimer();
|
||||||
|
entity_list.Process();
|
||||||
|
entity_list.MobProcess();
|
||||||
|
|
||||||
|
LogSys.EnableConsoleLogging();
|
||||||
|
}
|
||||||
@@ -1,25 +1,13 @@
|
|||||||
#include "../../common/http/httplib.h"
|
#include "../../common/http/httplib.h"
|
||||||
#include "../../common/eqemu_logsys.h"
|
#include "../../common/eqemu_logsys.h"
|
||||||
#include "../../common/platform.h"
|
#include "../../common/platform.h"
|
||||||
#include "../zone.h"
|
#include "../../zone.h"
|
||||||
#include "../client.h"
|
#include "../../client.h"
|
||||||
#include "../../common/net/eqstream.h"
|
#include "../../common/net/eqstream.h"
|
||||||
|
|
||||||
extern Zone *zone;
|
extern Zone *zone;
|
||||||
|
|
||||||
void RunTest(const std::string &test_name, const std::string &expected, const std::string &actual)
|
void ZoneCLI::TestDataBuckets(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||||
{
|
|
||||||
if (expected == actual) {
|
|
||||||
std::cout << "[✅] " << test_name << " PASSED\n";
|
|
||||||
} else {
|
|
||||||
std::cerr << "[❌] " << test_name << " FAILED\n";
|
|
||||||
std::cerr << " 📌 Expected: " << expected << "\n";
|
|
||||||
std::cerr << " ❌ Got: " << actual << "\n";
|
|
||||||
std::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ZoneCLI::DataBuckets(int argc, char **argv, argh::parser &cmd, std::string &description)
|
|
||||||
{
|
{
|
||||||
if (cmd[{"-h", "--help"}]) {
|
if (cmd[{"-h", "--help"}]) {
|
||||||
return;
|
return;
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
#include "../../common/http/httplib.h"
|
#include "../../common/http/httplib.h"
|
||||||
#include "../../common/eqemu_logsys.h"
|
#include "../../common/eqemu_logsys.h"
|
||||||
#include "../../common/platform.h"
|
#include "../../common/platform.h"
|
||||||
#include "../zone.h"
|
#include "../../zone.h"
|
||||||
#include "../client.h"
|
#include "../../client.h"
|
||||||
#include "../../common/net/eqstream.h"
|
#include "../../common/net/eqstream.h"
|
||||||
#include "../../common/json/json.hpp"
|
#include "../../common/json/json.hpp"
|
||||||
|
|
||||||
@@ -36,19 +36,6 @@ struct TestCase {
|
|||||||
bool handin_check_result;
|
bool handin_check_result;
|
||||||
};
|
};
|
||||||
|
|
||||||
void RunTest(const std::string &test_name, bool expected, bool actual)
|
|
||||||
{
|
|
||||||
if (expected == actual) {
|
|
||||||
std::cout << "[✅] " << test_name << " PASSED\n";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
std::cerr << "[❌] " << test_name << " FAILED\n";
|
|
||||||
std::cerr << " 📌 Expected: " << (expected ? "true" : "false") << "\n";
|
|
||||||
std::cerr << " ❌ Got: " << (actual ? "true" : "false") << "\n";
|
|
||||||
std::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RunSerializedTest(const std::string &test_name, const std::string &expected, const std::string &actual)
|
void RunSerializedTest(const std::string &test_name, const std::string &expected, const std::string &actual)
|
||||||
{
|
{
|
||||||
if (expected == actual) {
|
if (expected == actual) {
|
||||||
@@ -75,7 +62,7 @@ std::string SerializeHandin(const std::map<std::string, uint32> &items, const Ha
|
|||||||
return j.dump();
|
return j.dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &description)
|
void ZoneCLI::TestNpcHandins(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||||
{
|
{
|
||||||
if (cmd[{"-h", "--help"}]) {
|
if (cmd[{"-h", "--help"}]) {
|
||||||
return;
|
return;
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
#include "../../common/http/httplib.h"
|
#include "../../common/http/httplib.h"
|
||||||
#include "../../common/eqemu_logsys.h"
|
#include "../../common/eqemu_logsys.h"
|
||||||
#include "../../common/platform.h"
|
#include "../../common/platform.h"
|
||||||
#include "../zone.h"
|
#include "../../zone.h"
|
||||||
#include "../client.h"
|
#include "../../client.h"
|
||||||
#include "../../common/net/eqstream.h"
|
#include "../../common/net/eqstream.h"
|
||||||
|
|
||||||
extern Zone *zone;
|
extern Zone *zone;
|
||||||
|
|
||||||
void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std::string &description)
|
void ZoneCLI::TestNpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||||
{
|
{
|
||||||
if (cmd[{"-h", "--help"}]) {
|
if (cmd[{"-h", "--help"}]) {
|
||||||
return;
|
return;
|
||||||
File diff suppressed because it is too large
Load Diff
+90
-16
@@ -882,9 +882,13 @@ void Client::SendZoneInPackets()
|
|||||||
//SendGuildMembers();
|
//SendGuildMembers();
|
||||||
SendGuildURL();
|
SendGuildURL();
|
||||||
SendGuildChannel();
|
SendGuildChannel();
|
||||||
|
if (RuleB(Guild, EnableLFGuild)) {
|
||||||
SendGuildLFGuildStatus();
|
SendGuildLFGuildStatus();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (RuleB(Guild, EnableLFGuild)) {
|
||||||
SendLFGuildStatus();
|
SendLFGuildStatus();
|
||||||
|
}
|
||||||
|
|
||||||
//No idea why live sends this if even were not in a guild
|
//No idea why live sends this if even were not in a guild
|
||||||
SendGuildMOTD();
|
SendGuildMOTD();
|
||||||
@@ -2549,40 +2553,59 @@ void Client::ChangeLastName(std::string last_name) {
|
|||||||
safe_delete(outapp);
|
safe_delete(outapp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::ChangeFirstName(const char* in_firstname, const char* gmname)
|
// Deprecated, this packet does not actually work in ROF2
|
||||||
|
bool Client::ChangeFirstName(const std::string in_firstname, const std::string gmname)
|
||||||
{
|
{
|
||||||
// check duplicate name
|
if (!ChangeFirstName(in_firstname)) {
|
||||||
bool used_name = database.IsNameUsed((const char*) in_firstname);
|
|
||||||
if (used_name) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update character_
|
|
||||||
if(!database.UpdateName(GetName(), in_firstname))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// update pp
|
|
||||||
memset(m_pp.name, 0, sizeof(m_pp.name));
|
|
||||||
snprintf(m_pp.name, sizeof(m_pp.name), "%s", in_firstname);
|
|
||||||
strcpy(name, m_pp.name);
|
|
||||||
Save();
|
|
||||||
|
|
||||||
// send name update packet
|
// send name update packet
|
||||||
auto outapp = new EQApplicationPacket(OP_GMNameChange, sizeof(GMName_Struct));
|
auto outapp = new EQApplicationPacket(OP_GMNameChange, sizeof(GMName_Struct));
|
||||||
GMName_Struct* gmn=(GMName_Struct*)outapp->pBuffer;
|
GMName_Struct* gmn=(GMName_Struct*)outapp->pBuffer;
|
||||||
strn0cpy(gmn->gmname,gmname,64);
|
strn0cpy(gmn->gmname,gmname.c_str(),64);
|
||||||
strn0cpy(gmn->oldname,GetName(),64);
|
strn0cpy(gmn->oldname,GetName(),64);
|
||||||
strn0cpy(gmn->newname,in_firstname,64);
|
strn0cpy(gmn->newname,in_firstname.c_str(),64);
|
||||||
gmn->unknown[0] = 1;
|
gmn->unknown[0] = 1;
|
||||||
gmn->unknown[1] = 1;
|
gmn->unknown[1] = 1;
|
||||||
gmn->unknown[2] = 1;
|
gmn->unknown[2] = 1;
|
||||||
entity_list.QueueClients(this, outapp, false);
|
entity_list.QueueClients(this, outapp, false);
|
||||||
safe_delete(outapp);
|
safe_delete(outapp);
|
||||||
|
|
||||||
|
// success
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::ChangeFirstName(const std::string in_firstname)
|
||||||
|
{
|
||||||
|
// check duplicate name
|
||||||
|
bool used_name = database.IsNameUsed(in_firstname) || database.IsPetNameUsed(in_firstname);
|
||||||
|
if (used_name || !database.CheckNameFilter(in_firstname, false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update character_
|
||||||
|
if(!database.UpdateNameByID(CharacterID(), in_firstname))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Send Name Update to Clients
|
||||||
|
SendRename(this, GetName(), in_firstname.c_str());
|
||||||
|
SetName(in_firstname.c_str());
|
||||||
|
|
||||||
|
// update pp
|
||||||
|
memset(m_pp.name, 0, sizeof(m_pp.name));
|
||||||
|
snprintf(m_pp.name, sizeof(m_pp.name), "%s", in_firstname.c_str());
|
||||||
|
strcpy(name, m_pp.name);
|
||||||
|
Save();
|
||||||
|
|
||||||
|
// Update the active char in account table
|
||||||
|
database.UpdateLiveChar(in_firstname, AccountID());
|
||||||
|
|
||||||
// finally, update the /who list
|
// finally, update the /who list
|
||||||
UpdateWho();
|
UpdateWho();
|
||||||
|
|
||||||
// success
|
// success
|
||||||
|
ClearNameChange();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4737,6 +4760,57 @@ bool Client::KeyRingRemove(uint32 item_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Client::IsNameChangeAllowed() {
|
||||||
|
if (RuleB(Character, AlwaysAllowNameChange)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto k = GetScopedBucketKeys();
|
||||||
|
k.key = "name_change_allowed";
|
||||||
|
|
||||||
|
auto b = DataBucket::GetData(k);
|
||||||
|
if (!b.value.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::ClearNameChange() {
|
||||||
|
if (!IsNameChangeAllowed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto k = GetScopedBucketKeys();
|
||||||
|
k.key = "name_change_allowed";
|
||||||
|
|
||||||
|
DataBucket::DeleteData(k);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::InvokeChangeNameWindow(bool immediate) {
|
||||||
|
if (!IsNameChangeAllowed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto packet_op = immediate ? OP_InvokeNameChangeImmediate : OP_InvokeNameChangeLazy;
|
||||||
|
|
||||||
|
auto outapp = new EQApplicationPacket(packet_op, 0);
|
||||||
|
QueuePacket(outapp);
|
||||||
|
safe_delete(outapp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::GrantNameChange() {
|
||||||
|
|
||||||
|
auto k = GetScopedBucketKeys();
|
||||||
|
k.key = "name_change_allowed";
|
||||||
|
k.value = "allowed"; // potentially put a timestamp here
|
||||||
|
DataBucket::SetData(k);
|
||||||
|
|
||||||
|
InvokeChangeNameWindow(true);
|
||||||
|
}
|
||||||
|
|
||||||
bool Client::IsPetNameChangeAllowed() {
|
bool Client::IsPetNameChangeAllowed() {
|
||||||
if (RuleB(Pets, AlwaysAllowPetRename)) {
|
if (RuleB(Pets, AlwaysAllowPetRename)) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+8
-1
@@ -332,6 +332,10 @@ public:
|
|||||||
bool KeyRingClear();
|
bool KeyRingClear();
|
||||||
bool KeyRingRemove(uint32 item_id);
|
bool KeyRingRemove(uint32 item_id);
|
||||||
void KeyRingList();
|
void KeyRingList();
|
||||||
|
bool IsNameChangeAllowed();
|
||||||
|
void InvokeChangeNameWindow(bool immediate = true);
|
||||||
|
bool ClearNameChange();
|
||||||
|
void GrantNameChange();
|
||||||
bool IsPetNameChangeAllowed();
|
bool IsPetNameChangeAllowed();
|
||||||
void GrantPetNameChange();
|
void GrantPetNameChange();
|
||||||
void ClearPetNameChange();
|
void ClearPetNameChange();
|
||||||
@@ -443,6 +447,8 @@ public:
|
|||||||
int64 ValidateBuyLineCost(std::map<uint32, BuylineItemDetails_Struct>& item_map);
|
int64 ValidateBuyLineCost(std::map<uint32, BuylineItemDetails_Struct>& item_map);
|
||||||
bool DoBarterBuyerChecks(BuyerLineSellItem_Struct& sell_line);
|
bool DoBarterBuyerChecks(BuyerLineSellItem_Struct& sell_line);
|
||||||
bool DoBarterSellerChecks(BuyerLineSellItem_Struct& sell_line);
|
bool DoBarterSellerChecks(BuyerLineSellItem_Struct& sell_line);
|
||||||
|
void CancelBuyerTradeWindow();
|
||||||
|
void CancelTraderTradeWindow();
|
||||||
|
|
||||||
void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho);
|
void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho);
|
||||||
bool ShouldISpawnFor(Client *c) { return !GMHideMe(c) && !IsHoveringForRespawn(); }
|
bool ShouldISpawnFor(Client *c) { return !GMHideMe(c) && !IsHoveringForRespawn(); }
|
||||||
@@ -509,7 +515,8 @@ public:
|
|||||||
bool AutoAttackEnabled() const { return auto_attack; }
|
bool AutoAttackEnabled() const { return auto_attack; }
|
||||||
bool AutoFireEnabled() const { return auto_fire; }
|
bool AutoFireEnabled() const { return auto_fire; }
|
||||||
|
|
||||||
bool ChangeFirstName(const char* in_firstname,const char* gmname);
|
bool ChangeFirstName(const std::string in_firstname,const std::string gmname);
|
||||||
|
bool ChangeFirstName(const std::string in_firstname);
|
||||||
|
|
||||||
void Duck();
|
void Duck();
|
||||||
void Stand();
|
void Stand();
|
||||||
|
|||||||
+28
-26
@@ -120,48 +120,46 @@ int Client::GetBotSpawnLimit(uint8 class_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto& zones_list = Strings::Split(RuleS(Bots, ZonesWithSpawnLimits), ",");
|
const auto& zones_list = Strings::Split(RuleS(Bots, ZonesWithSpawnLimits), ",");
|
||||||
|
|
||||||
|
if (!zones_list.empty()) {
|
||||||
|
auto it = std::find(zones_list.begin(), zones_list.end(), std::to_string(zone->GetZoneID()));
|
||||||
|
|
||||||
|
if (it != zones_list.end()) {
|
||||||
const auto& zones_limits_list = Strings::Split(RuleS(Bots, ZoneSpawnLimits), ",");
|
const auto& zones_limits_list = Strings::Split(RuleS(Bots, ZoneSpawnLimits), ",");
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (const auto& result : zones_list) {
|
if (zones_list.size() == zones_limits_list.size()) {
|
||||||
try {
|
try {
|
||||||
if (
|
auto new_limit = std::stoul(zones_limits_list[std::distance(zones_list.begin(), it)]);
|
||||||
std::stoul(result) == zone->GetZoneID() &&
|
|
||||||
std::stoul(zones_limits_list[i]) < bot_spawn_limit
|
|
||||||
) {
|
|
||||||
bot_spawn_limit = std::stoul(zones_limits_list[i]);
|
|
||||||
|
|
||||||
break;
|
if (new_limit < bot_spawn_limit) {
|
||||||
|
bot_spawn_limit = new_limit;
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LogInfo("Invalid entry in Rule Bots:ZoneSpawnLimits: [{}]", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (const std::exception& e) {
|
|
||||||
LogInfo("Invalid entry in Rule VegasScaling:SpecialScalingZones or SpecialScalingZonesVersions: [{}]", e.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& zones_forced_list = Strings::Split(RuleS(Bots, ZonesWithForcedSpawnLimits), ",");
|
const auto& zones_forced_list = Strings::Split(RuleS(Bots, ZonesWithForcedSpawnLimits), ",");
|
||||||
|
|
||||||
|
if (!zones_forced_list.empty()) {
|
||||||
|
auto it = std::find(zones_forced_list.begin(), zones_forced_list.end(), std::to_string(zone->GetZoneID()));
|
||||||
|
|
||||||
|
if (it != zones_forced_list.end()) {
|
||||||
const auto& zones_forced_limits_list = Strings::Split(RuleS(Bots, ZoneForcedSpawnLimits), ",");
|
const auto& zones_forced_limits_list = Strings::Split(RuleS(Bots, ZoneForcedSpawnLimits), ",");
|
||||||
i = 0;
|
|
||||||
|
|
||||||
for (const auto& result : zones_forced_list) {
|
if (zones_forced_list.size() == zones_forced_limits_list.size()) {
|
||||||
try {
|
try {
|
||||||
if (
|
auto new_limit = std::stoul(zones_forced_limits_list[std::distance(zones_forced_list.begin(), it)]);
|
||||||
std::stoul(result) == zone->GetZoneID() &&
|
|
||||||
std::stoul(zones_forced_limits_list[i]) != bot_spawn_limit
|
|
||||||
) {
|
|
||||||
bot_spawn_limit = std::stoul(zones_forced_limits_list[i]);
|
|
||||||
|
|
||||||
break;
|
if (new_limit != bot_spawn_limit) {
|
||||||
|
bot_spawn_limit = new_limit;
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LogInfo("Invalid entry in Rule Bots:ZoneForcedSpawnLimits: [{}]", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (const std::exception& e) {
|
|
||||||
LogInfo("Invalid entry in Rule VegasScaling:SpecialScalingZones or SpecialScalingZonesVersions: [{}]", e.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,6 +256,8 @@ int Client::GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting) {
|
|||||||
return GetDefaultSpellTypeMinThreshold(bot_setting);
|
return GetDefaultSpellTypeMinThreshold(bot_setting);
|
||||||
case BotSettingCategories::SpellMaxThreshold:
|
case BotSettingCategories::SpellMaxThreshold:
|
||||||
return GetDefaultSpellTypeMaxThreshold(bot_setting);
|
return GetDefaultSpellTypeMaxThreshold(bot_setting);
|
||||||
|
default:
|
||||||
|
return 0; // default return for any unsupported setting type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,6 +271,8 @@ int Client::GetBotSetting(uint8 setting_type, uint16 bot_setting) {
|
|||||||
return GetSpellTypeMinThreshold(bot_setting);
|
return GetSpellTypeMinThreshold(bot_setting);
|
||||||
case BotSettingCategories::SpellMaxThreshold:
|
case BotSettingCategories::SpellMaxThreshold:
|
||||||
return GetSpellTypeMaxThreshold(bot_setting);
|
return GetSpellTypeMaxThreshold(bot_setting);
|
||||||
|
default:
|
||||||
|
return 0; // default return for any unsupported setting type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+42
-14
@@ -73,6 +73,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "../common/repositories/character_stats_record_repository.h"
|
#include "../common/repositories/character_stats_record_repository.h"
|
||||||
#include "dialogue_window.h"
|
#include "dialogue_window.h"
|
||||||
#include "../common/rulesys.h"
|
#include "../common/rulesys.h"
|
||||||
|
#include "../common/repositories/adventure_members_repository.h"
|
||||||
|
|
||||||
extern QueryServ* QServ;
|
extern QueryServ* QServ;
|
||||||
extern Zone* zone;
|
extern Zone* zone;
|
||||||
@@ -826,6 +827,10 @@ void Client::CompleteConnect()
|
|||||||
if (IsPetNameChangeAllowed() && !RuleB(Pets, AlwaysAllowPetRename)) {
|
if (IsPetNameChangeAllowed() && !RuleB(Pets, AlwaysAllowPetRename)) {
|
||||||
InvokeChangePetName(false);
|
InvokeChangePetName(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsNameChangeAllowed() && !RuleB(Character, AlwaysAllowNameChange)) {
|
||||||
|
InvokeChangeNameWindow(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
|
if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
|
||||||
@@ -909,11 +914,14 @@ void Client::CompleteConnect()
|
|||||||
|
|
||||||
SendDynamicZoneUpdates();
|
SendDynamicZoneUpdates();
|
||||||
|
|
||||||
/** Request adventure info **/
|
// Request adventure info
|
||||||
|
auto members = AdventureMembersRepository::GetWhere(database, fmt::format("charid = {}", CharacterID()));
|
||||||
|
if (!members.empty()) {
|
||||||
auto pack = new ServerPacket(ServerOP_AdventureDataRequest, 64);
|
auto pack = new ServerPacket(ServerOP_AdventureDataRequest, 64);
|
||||||
strcpy((char*)pack->pBuffer, GetName());
|
strcpy((char*)pack->pBuffer, GetName());
|
||||||
worldserver.SendPacket(pack);
|
worldserver.SendPacket(pack);
|
||||||
delete pack;
|
delete pack;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskUFAndLater) {
|
if (IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskUFAndLater) {
|
||||||
EQApplicationPacket *outapp = MakeBuffsPacket(false);
|
EQApplicationPacket *outapp = MakeBuffsPacket(false);
|
||||||
@@ -4548,14 +4556,14 @@ void Client::Handle_OP_ChangePetName(const EQApplicationPacket *app) {
|
|||||||
|
|
||||||
auto p = (ChangePetName_Struct *) app->pBuffer;
|
auto p = (ChangePetName_Struct *) app->pBuffer;
|
||||||
if (!IsPetNameChangeAllowed()) {
|
if (!IsPetNameChangeAllowed()) {
|
||||||
p->response_code = ChangePetNameResponse::NotEligible;
|
p->response_code = ChangeNameResponse::Ineligible;
|
||||||
QueuePacket(app);
|
QueuePacket(app);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
p->response_code = ChangePetNameResponse::Denied;
|
p->response_code = ChangeNameResponse::Denied;
|
||||||
if (ChangePetName(p->new_pet_name)) {
|
if (ChangePetName(p->new_pet_name)) {
|
||||||
p->response_code = ChangePetNameResponse::Accepted;
|
p->response_code = ChangeNameResponse::Accepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
QueuePacket(app);
|
QueuePacket(app);
|
||||||
@@ -6776,6 +6784,21 @@ void Client::Handle_OP_GMLastName(const EQApplicationPacket *app)
|
|||||||
|
|
||||||
void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app)
|
void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
|
if (app->size == sizeof(AltChangeName_Struct)) {
|
||||||
|
auto p = (AltChangeName_Struct *) app->pBuffer;
|
||||||
|
|
||||||
|
if (!IsNameChangeAllowed()) {
|
||||||
|
p->response_code = ChangeNameResponse::Ineligible;
|
||||||
|
QueuePacket(app);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->response_code = ChangeFirstName(p->new_name) ? ChangeNameResponse::Accepted : ChangeNameResponse::Denied;
|
||||||
|
QueuePacket(app);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (app->size != sizeof(GMName_Struct)) {
|
if (app->size != sizeof(GMName_Struct)) {
|
||||||
LogError("Wrong size: OP_GMNameChange, size=[{}], expected [{}]", app->size, sizeof(GMName_Struct));
|
LogError("Wrong size: OP_GMNameChange, size=[{}], expected [{}]", app->size, sizeof(GMName_Struct));
|
||||||
return;
|
return;
|
||||||
@@ -7653,7 +7676,11 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app)
|
|||||||
log.char_id = CharacterID();
|
log.char_id = CharacterID();
|
||||||
log.guild_id = GuildID();
|
log.guild_id = GuildID();
|
||||||
log.item_id = inst->GetID();
|
log.item_id = inst->GetID();
|
||||||
|
log.quantity = 1;
|
||||||
|
if (inst->GetCharges() > 0 || inst->IsStackable() || inst->GetItem()->MaxCharges > 0) {
|
||||||
log.quantity = inst->GetCharges();
|
log.quantity = inst->GetCharges();
|
||||||
|
}
|
||||||
|
|
||||||
if (inst->IsAugmented()) {
|
if (inst->IsAugmented()) {
|
||||||
auto augs = inst->GetAugmentIDs();
|
auto augs = inst->GetAugmentIDs();
|
||||||
log.aug_slot_one = augs.at(0);
|
log.aug_slot_one = augs.at(0);
|
||||||
@@ -7737,7 +7764,11 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app)
|
|||||||
item.guild_id = GuildID();
|
item.guild_id = GuildID();
|
||||||
item.area = GuildBankDepositArea;
|
item.area = GuildBankDepositArea;
|
||||||
item.item_id = cursor_item->ID;
|
item.item_id = cursor_item->ID;
|
||||||
|
item.quantity = 1;
|
||||||
|
if (cursor_item_inst->GetCharges() > 0 || cursor_item_inst->IsStackable() || cursor_item->MaxCharges > 0) {
|
||||||
item.quantity = cursor_item_inst->GetCharges();
|
item.quantity = cursor_item_inst->GetCharges();
|
||||||
|
}
|
||||||
|
|
||||||
item.donator = GetCleanName();
|
item.donator = GetCleanName();
|
||||||
item.permissions = GuildBankBankerOnly;
|
item.permissions = GuildBankBankerOnly;
|
||||||
if (cursor_item_inst->IsAugmented()) {
|
if (cursor_item_inst->IsAugmented()) {
|
||||||
@@ -7821,12 +7852,9 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst->GetCharges() > 0) {
|
|
||||||
gbwis->Quantity = inst->GetCharges();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst->GetCharges() < 0) {
|
|
||||||
gbwis->Quantity = 1;
|
gbwis->Quantity = 1;
|
||||||
|
if (inst->GetCharges() > 0 || inst->IsStackable() || inst->GetItem()->MaxCharges > 0) {
|
||||||
|
gbwis->Quantity = inst->GetCharges();
|
||||||
}
|
}
|
||||||
|
|
||||||
PushItemOnCursor(*inst.get());
|
PushItemOnCursor(*inst.get());
|
||||||
@@ -7956,7 +7984,7 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetGuildID(new_guild_id);
|
SetGuildID(new_guild_id);
|
||||||
SendGuildList();
|
UpdateWho();
|
||||||
guild_mgr.MemberAdd(new_guild_id, CharacterID(), GetLevel(), GetClass(), GUILD_LEADER, GetZoneID(), GetName());
|
guild_mgr.MemberAdd(new_guild_id, CharacterID(), GetLevel(), GetClass(), GUILD_LEADER, GetZoneID(), GetName());
|
||||||
guild_mgr.SendGuildRefresh(new_guild_id, true, true, true, true);
|
guild_mgr.SendGuildRefresh(new_guild_id, true, true, true, true);
|
||||||
guild_mgr.SendToWorldSendGuildList();
|
guild_mgr.SendToWorldSendGuildList();
|
||||||
@@ -8125,7 +8153,7 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app)
|
|||||||
if (!invitee) {
|
if (!invitee) {
|
||||||
Message(
|
Message(
|
||||||
Chat::Red,
|
Chat::Red,
|
||||||
"Prospective guild member %s must be in zone to preform guild operations on them.",
|
"Prospective guild member %s must be in zone to perform guild operations on them.",
|
||||||
gc->othername
|
gc->othername
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@@ -11038,7 +11066,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
|
|||||||
if (!target)
|
if (!target)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (RuleB(Map, CheckForLoSCheat) && (!DoLosChecks(target) || !CheckLosCheat(target))) {
|
if (RuleB(Pets, PetsRequireLoS) && !DoLosChecks(target)) {
|
||||||
mypet->SayString(this, NOT_LEGAL_TARGET);
|
mypet->SayString(this, NOT_LEGAL_TARGET);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -11106,7 +11134,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RuleB(Map, CheckForLoSCheat) && (!DoLosChecks(GetTarget()) || !CheckLosCheat(GetTarget()))) {
|
if (RuleB(Pets, PetsRequireLoS) && !DoLosChecks(GetTarget())) {
|
||||||
mypet->SayString(this, NOT_LEGAL_TARGET);
|
mypet->SayString(this, NOT_LEGAL_TARGET);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -15458,7 +15486,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
|||||||
);
|
);
|
||||||
Message(
|
Message(
|
||||||
Chat::Yellow,
|
Chat::Yellow,
|
||||||
"Direct inventory delivey is not yet implemented. Please visit the vendor directly or purchase via parcel delivery."
|
"Direct inventory delivery is not yet implemented. Please visit the vendor directly or purchase via parcel delivery."
|
||||||
);
|
);
|
||||||
in->method = BazaarByDirectToInventory;
|
in->method = BazaarByDirectToInventory;
|
||||||
in->sub_action = Failed;
|
in->sub_action = Failed;
|
||||||
|
|||||||
+1
-1
@@ -245,7 +245,7 @@ int command_init(void)
|
|||||||
command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", AccountStatus::GMLeadAdmin, command_zonebootup) ||
|
command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", AccountStatus::GMLeadAdmin, command_zonebootup) ||
|
||||||
command_add("zoneinstance", "[Instance ID] [X] [Y] [Z] - Teleport to specified Instance by ID (coordinates are optional)", AccountStatus::Guide, command_zone_instance) ||
|
command_add("zoneinstance", "[Instance ID] [X] [Y] [Z] - Teleport to specified Instance by ID (coordinates are optional)", AccountStatus::Guide, command_zone_instance) ||
|
||||||
command_add("zoneshard", "[zone] [instance_id] - Teleport explicitly to a zone shard", AccountStatus::Player, command_zone_shard) ||
|
command_add("zoneshard", "[zone] [instance_id] - Teleport explicitly to a zone shard", AccountStatus::Player, command_zone_shard) ||
|
||||||
command_add("zoneshutdown", "[shortname] - Shut down a zone server", AccountStatus::GMLeadAdmin, command_zoneshutdown) ||
|
command_add("zoneshutdown", "[instance|zone] [Instance ID|Zone ID|Zone Short Name] - Shut down a zone server by Instance ID, Zone ID, or Zone Short Name", AccountStatus::GMLeadAdmin, command_zoneshutdown) ||
|
||||||
command_add("zsave", " Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave)
|
command_add("zsave", " Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave)
|
||||||
) {
|
) {
|
||||||
command_deinit();
|
command_deinit();
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ void command_ipban(Client *c, const Seperator *sep);
|
|||||||
void command_kick(Client *c, const Seperator *sep);
|
void command_kick(Client *c, const Seperator *sep);
|
||||||
void command_killallnpcs(Client *c, const Seperator *sep);
|
void command_killallnpcs(Client *c, const Seperator *sep);
|
||||||
void command_kill(Client *c, const Seperator *sep);
|
void command_kill(Client *c, const Seperator *sep);
|
||||||
void command_level(Client *c, const Seperator *sep);
|
|
||||||
void command_list(Client *c, const Seperator *sep);
|
void command_list(Client *c, const Seperator *sep);
|
||||||
void command_lootsim(Client *c, const Seperator *sep);
|
void command_lootsim(Client *c, const Seperator *sep);
|
||||||
void command_load_shared_memory(Client *c, const Seperator *sep);
|
void command_load_shared_memory(Client *c, const Seperator *sep);
|
||||||
@@ -139,7 +138,6 @@ void command_nudge(Client *c, const Seperator *sep);
|
|||||||
void command_nukebuffs(Client *c, const Seperator *sep);
|
void command_nukebuffs(Client *c, const Seperator *sep);
|
||||||
void command_nukeitem(Client *c, const Seperator *sep);
|
void command_nukeitem(Client *c, const Seperator *sep);
|
||||||
void command_object(Client *c, const Seperator *sep);
|
void command_object(Client *c, const Seperator *sep);
|
||||||
void command_oocmute(Client *c, const Seperator *sep);
|
|
||||||
void command_parcels(Client *c, const Seperator *sep);
|
void command_parcels(Client *c, const Seperator *sep);
|
||||||
void command_path(Client *c, const Seperator *sep);
|
void command_path(Client *c, const Seperator *sep);
|
||||||
void command_peqzone(Client *c, const Seperator *sep);
|
void command_peqzone(Client *c, const Seperator *sep);
|
||||||
@@ -147,7 +145,6 @@ void command_petitems(Client *c, const Seperator *sep);
|
|||||||
void command_picklock(Client *c, const Seperator *sep);
|
void command_picklock(Client *c, const Seperator *sep);
|
||||||
void command_profanity(Client *c, const Seperator *sep);
|
void command_profanity(Client *c, const Seperator *sep);
|
||||||
void command_push(Client *c, const Seperator *sep);
|
void command_push(Client *c, const Seperator *sep);
|
||||||
void command_pvp(Client *c, const Seperator *sep);
|
|
||||||
void command_raidloot(Client* c, const Seperator* sep);
|
void command_raidloot(Client* c, const Seperator* sep);
|
||||||
void command_randomfeatures(Client *c, const Seperator *sep);
|
void command_randomfeatures(Client *c, const Seperator *sep);
|
||||||
void command_refreshgroup(Client *c, const Seperator *sep);
|
void command_refreshgroup(Client *c, const Seperator *sep);
|
||||||
@@ -201,8 +198,6 @@ void command_zone_instance(Client *c, const Seperator *sep);
|
|||||||
void command_zone_shard(Client *c, const Seperator *sep);
|
void command_zone_shard(Client *c, const Seperator *sep);
|
||||||
void command_zonebootup(Client *c, const Seperator *sep);
|
void command_zonebootup(Client *c, const Seperator *sep);
|
||||||
void command_zoneshutdown(Client *c, const Seperator *sep);
|
void command_zoneshutdown(Client *c, const Seperator *sep);
|
||||||
void command_zopp(Client *c, const Seperator *sep);
|
|
||||||
void command_zsafecoords(Client *c, const Seperator *sep);
|
|
||||||
void command_zsave(Client *c, const Seperator *sep);
|
void command_zsave(Client *c, const Seperator *sep);
|
||||||
|
|
||||||
#include "bot.h"
|
#include "bot.h"
|
||||||
|
|||||||
+2
-2
@@ -290,7 +290,7 @@ Corpse::Corpse(Client *c, int32 rez_exp, KilledByTypes in_killed_by) : Mob(
|
|||||||
m_corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS));
|
m_corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS));
|
||||||
m_loot_cooldown_timer.SetTimer(10);
|
m_loot_cooldown_timer.SetTimer(10);
|
||||||
m_check_rezzable_timer.SetTimer(1000);
|
m_check_rezzable_timer.SetTimer(1000);
|
||||||
m_check_owner_online_timer.SetTimer(RuleI(Character, CorpseOwnerOnlineTime));
|
m_check_owner_online_timer.SetTimer(RuleI(Character, CorpseOwnerOnlineCheckTime) * 1000);
|
||||||
|
|
||||||
m_corpse_rezzable_timer.Disable();
|
m_corpse_rezzable_timer.Disable();
|
||||||
SetRezTimer(true);
|
SetRezTimer(true);
|
||||||
@@ -583,7 +583,7 @@ Corpse::Corpse(
|
|||||||
m_corpse_delay_timer.SetTimer(RuleI(NPC, CorpseUnlockTimer));
|
m_corpse_delay_timer.SetTimer(RuleI(NPC, CorpseUnlockTimer));
|
||||||
m_corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS));
|
m_corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS));
|
||||||
m_loot_cooldown_timer.SetTimer(10);
|
m_loot_cooldown_timer.SetTimer(10);
|
||||||
m_check_owner_online_timer.SetTimer(RuleI(Character, CorpseOwnerOnlineTime));
|
m_check_owner_online_timer.SetTimer(RuleI(Character, CorpseOwnerOnlineCheckTime) * 1000);
|
||||||
m_check_rezzable_timer.SetTimer(1000);
|
m_check_rezzable_timer.SetTimer(1000);
|
||||||
m_corpse_rezzable_timer.Disable();
|
m_corpse_rezzable_timer.Disable();
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,9 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <glm/ext/matrix_transform.hpp>
|
||||||
|
#include <numbers>
|
||||||
|
|
||||||
#define OPEN_DOOR 0x02
|
#define OPEN_DOOR 0x02
|
||||||
#define CLOSE_DOOR 0x03
|
#define CLOSE_DOOR 0x03
|
||||||
#define OPEN_INVDOOR 0x03
|
#define OPEN_INVDOOR 0x03
|
||||||
@@ -970,3 +973,68 @@ bool Doors::GetIsDoorBlacklisted()
|
|||||||
bool Doors::IsDoorBlacklisted() {
|
bool Doors::IsDoorBlacklisted() {
|
||||||
return m_is_blacklisted_to_open;
|
return m_is_blacklisted_to_open;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Doors::IsDoorBetween(glm::vec4 loc_a, glm::vec4 loc_c, uint16 door_size, float door_depth, bool draw_box) {
|
||||||
|
glm::vec4 door_loc = GetPosition();
|
||||||
|
float half_size = door_size * 0.5f;
|
||||||
|
float half_depth = door_depth * 0.5f;
|
||||||
|
float normalized_heading = std::fmod(door_loc.w, 512.0f);
|
||||||
|
float heading_radians = normalized_heading * (std::numbers::pi / 256.0f);
|
||||||
|
glm::mat4 door_rotation = glm::rotate(glm::mat4(1.0f), -heading_radians, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||||
|
glm::vec3 box_corner_one = glm::vec3(door_size, -half_depth, 0.0f);
|
||||||
|
glm::vec3 box_corner_two = glm::vec3(-door_size, -half_depth, 0.0f);
|
||||||
|
glm::vec3 box_corner_three = glm::vec3(-door_size, half_depth, 0.0f);
|
||||||
|
glm::vec3 box_corner_four = glm::vec3(door_size, half_depth, 0.0f);
|
||||||
|
glm::vec3 door_center_offset = glm::vec3(-(door_size * 0.75f), half_depth * 0.5f, 0.0f);
|
||||||
|
glm::vec3 door_center = glm::vec3(door_loc) + glm::vec3(door_rotation * glm::vec4(door_center_offset, 1.0f));
|
||||||
|
glm::mat4 transform = glm::translate(glm::mat4(1.0f), door_center) * door_rotation;
|
||||||
|
|
||||||
|
box_corner_one = glm::vec3(transform * glm::vec4(box_corner_one, 1.0f));
|
||||||
|
box_corner_two = glm::vec3(transform * glm::vec4(box_corner_two, 1.0f));
|
||||||
|
box_corner_three = glm::vec3(transform * glm::vec4(box_corner_three, 1.0f));
|
||||||
|
box_corner_four = glm::vec3(transform * glm::vec4(box_corner_four, 1.0f));
|
||||||
|
|
||||||
|
if (draw_box) {
|
||||||
|
NPC::SpawnZonePointNodeNPC("loc_a", loc_a);
|
||||||
|
NPC::SpawnZonePointNodeNPC("door_anchor", door_loc);
|
||||||
|
NPC::SpawnZonePointNodeNPC("loc_c", loc_c);
|
||||||
|
NPC::SpawnZonePointNodeNPC("box_corner_one", glm::vec4(box_corner_one.x, box_corner_one.y, box_corner_one.z, 0));
|
||||||
|
NPC::SpawnZonePointNodeNPC("box_corner_two", glm::vec4(box_corner_two.x, box_corner_two.y, box_corner_two.z, 0));
|
||||||
|
NPC::SpawnZonePointNodeNPC("box_corner_three", glm::vec4(box_corner_three.x, box_corner_three.y, box_corner_three.z, 0));
|
||||||
|
NPC::SpawnZonePointNodeNPC("box_corner_four", glm::vec4(box_corner_four.x, box_corner_four.y, box_corner_four.z, 0));
|
||||||
|
NPC::SpawnZonePointNodeNPC("box_center", glm::vec4(door_center.x, door_center.y, door_center.z, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if LoS intersects box
|
||||||
|
auto intersects_box = [](const glm::vec3& a, const glm::vec3& b, const glm::vec3& p1, const glm::vec3& p2) {
|
||||||
|
glm::vec3 ab = b - a;
|
||||||
|
glm::vec3 p1p2 = p2 - p1;
|
||||||
|
|
||||||
|
glm::vec3 cross = glm::cross(ab, p1p2);
|
||||||
|
float cross_magnitude_squared = glm::dot(cross, cross);
|
||||||
|
|
||||||
|
if (cross_magnitude_squared < 1e-6f) {
|
||||||
|
return false; // Lines are parallel or coincident
|
||||||
|
}
|
||||||
|
|
||||||
|
float t = glm::dot(glm::cross(p1 - a, p1p2), cross) / cross_magnitude_squared;
|
||||||
|
float u = glm::dot(glm::cross(p1 - a, ab), cross) / cross_magnitude_squared;
|
||||||
|
|
||||||
|
return (t >= 0.0f && t <= 1.0f && u >= 0.0f && u <= 1.0f);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check intersection with each edge of the door bounding box
|
||||||
|
glm::vec3 loc_a_vec3(loc_a.x, loc_a.y, loc_a.z);
|
||||||
|
glm::vec3 loc_c_vec3(loc_c.x, loc_c.y, loc_c.z);
|
||||||
|
|
||||||
|
if (
|
||||||
|
intersects_box(loc_a_vec3, loc_c_vec3, box_corner_one, box_corner_two) ||
|
||||||
|
intersects_box(loc_a_vec3, loc_c_vec3, box_corner_two, box_corner_three) ||
|
||||||
|
intersects_box(loc_a_vec3, loc_c_vec3, box_corner_three, box_corner_four) ||
|
||||||
|
intersects_box(loc_a_vec3, loc_c_vec3, box_corner_four, box_corner_one)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ public:
|
|||||||
bool IsDestinationZoneSame() const;
|
bool IsDestinationZoneSame() const;
|
||||||
|
|
||||||
bool IsDoorBlacklisted();
|
bool IsDoorBlacklisted();
|
||||||
|
bool IsDoorBetween(glm::vec4 loc_a, glm::vec4 loc_c, uint16 door_size = 15, float door_depth = 5.0f, bool draw_box = false);
|
||||||
|
|
||||||
const char* GetDoorZone() const { return m_zone_name; }
|
const char* GetDoorZone() const { return m_zone_name; }
|
||||||
|
|
||||||
|
|||||||
+19
-5
@@ -3143,20 +3143,23 @@ void EntityList::Depop(bool StartSpawnTimer)
|
|||||||
{
|
{
|
||||||
for (auto it = npc_list.begin(); it != npc_list.end(); ++it) {
|
for (auto it = npc_list.begin(); it != npc_list.end(); ++it) {
|
||||||
NPC *pnpc = it->second;
|
NPC *pnpc = it->second;
|
||||||
|
|
||||||
if (pnpc) {
|
if (pnpc) {
|
||||||
Mob *own = pnpc->GetOwner();
|
Mob *own = pnpc->GetOwner();
|
||||||
//do not depop player's pets...
|
//do not depop player/bot pets...
|
||||||
if (own && own->IsClient())
|
if (own && own->IsOfClientBot()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (pnpc->IsHorse())
|
if (pnpc->IsHorse()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (pnpc->IsFindable())
|
if (pnpc->IsFindable()) {
|
||||||
UpdateFindableNPCState(pnpc, true);
|
UpdateFindableNPCState(pnpc, true);
|
||||||
|
}
|
||||||
|
|
||||||
pnpc->WipeHateList();
|
pnpc->WipeHateList();
|
||||||
|
|
||||||
pnpc->Depop(StartSpawnTimer);
|
pnpc->Depop(StartSpawnTimer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5988,3 +5991,14 @@ void EntityList::SendMerchantInventory(Mob* m, int32 slot_id, bool is_delete)
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityList::RestoreCorpse(NPC *npc, uint32_t decay_time)
|
||||||
|
{
|
||||||
|
uint16 corpse_id = npc->GetID();
|
||||||
|
npc->Death(npc, npc->GetHP() + 1, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand);
|
||||||
|
auto c = entity_list.GetCorpseByID(corpse_id);
|
||||||
|
if (c) {
|
||||||
|
c->UnLock();
|
||||||
|
c->SetDecayTimer(decay_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -580,6 +580,7 @@ public:
|
|||||||
|
|
||||||
void SendMerchantEnd(Mob* merchant);
|
void SendMerchantEnd(Mob* merchant);
|
||||||
void SendMerchantInventory(Mob* m, int32 slot_id = -1, bool is_delete = false);
|
void SendMerchantInventory(Mob* m, int32 slot_id = -1, bool is_delete = false);
|
||||||
|
void RestoreCorpse(NPC* npc, uint32_t decay_time);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Zone;
|
friend class Zone;
|
||||||
|
|||||||
@@ -35,6 +35,26 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg1 == "drawbox") {
|
||||||
|
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
|
||||||
|
|
||||||
|
if (door) {
|
||||||
|
uint16 door_size = 15;
|
||||||
|
float door_depth = 5.0f;
|
||||||
|
|
||||||
|
if (sep->IsNumber(2) && atof(sep->arg[2]) > 0) {
|
||||||
|
door_size = atof(sep->arg[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sep->IsNumber(3) && atof(sep->arg[3]) > 0) {
|
||||||
|
door_depth = atof(sep->arg[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
door->IsDoorBetween(c->GetPosition(), (c->GetTarget() ? c->GetTarget()->GetPosition() : c->GetPosition()), door_size, door_depth, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// edit menu
|
// edit menu
|
||||||
if (arg1 == "edit") {
|
if (arg1 == "edit") {
|
||||||
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
|
Doors *door = entity_list.GetDoorsByID(c->GetDoorToolEntityId());
|
||||||
@@ -544,6 +564,13 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep)
|
|||||||
c->Message(Chat::White, "#door setinvertstate [0|1] | Sets selected door invert state");
|
c->Message(Chat::White, "#door setinvertstate [0|1] | Sets selected door invert state");
|
||||||
c->Message(Chat::White, "#door setincline <incline> | Sets selected door incline");
|
c->Message(Chat::White, "#door setincline <incline> | Sets selected door incline");
|
||||||
c->Message(Chat::White, "#door opentype <opentype> | Sets selected door opentype");
|
c->Message(Chat::White, "#door opentype <opentype> | Sets selected door opentype");
|
||||||
|
c->Message(
|
||||||
|
Chat::White,
|
||||||
|
fmt::format(
|
||||||
|
"{} <door_size> <door_depth> | Draws a box for the door, default size = 15, depth = 5 if none defined",
|
||||||
|
Saylink::Silent("#door drawbox")
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
c->Message(
|
c->Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ void SetName(Client *c, const Seperator *sep)
|
|||||||
std::string new_name = sep->arg[2];
|
std::string new_name = sep->arg[2];
|
||||||
std::string old_name = t->GetCleanName();
|
std::string old_name = t->GetCleanName();
|
||||||
|
|
||||||
if (t->ChangeFirstName(new_name.c_str(), c->GetCleanName())) {
|
if (t->ChangeFirstName(new_name, c->GetCleanName())) {
|
||||||
c->Message(
|
c->Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -24,17 +24,13 @@ void SetName(Client *c, const Seperator *sep)
|
|||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
c->Message(Chat::White, "Sending player to char select.");
|
|
||||||
|
|
||||||
t->Kick("Name was changed");
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
c->Message(
|
c->Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"Unable to rename {}. Check that the new name '{}' isn't already taken.",
|
"Unable to rename {}. Check that the new name '{}' isn't already taken (Including Pet Names), or isn't invalid",
|
||||||
old_name,
|
old_name,
|
||||||
new_name
|
new_name
|
||||||
).c_str()
|
).c_str()
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
#include "show/zone_loot.cpp"
|
#include "show/zone_loot.cpp"
|
||||||
#include "show/zone_points.cpp"
|
#include "show/zone_points.cpp"
|
||||||
#include "show/zone_status.cpp"
|
#include "show/zone_status.cpp"
|
||||||
|
#include "show/zone_variables.cpp"
|
||||||
|
|
||||||
void command_show(Client *c, const Seperator *sep)
|
void command_show(Client *c, const Seperator *sep)
|
||||||
{
|
{
|
||||||
@@ -110,6 +111,7 @@ void command_show(Client *c, const Seperator *sep)
|
|||||||
Cmd{.cmd = "zone_loot", .u = "zone_loot", .fn = ShowZoneLoot, .a = {"#viewzoneloot"}},
|
Cmd{.cmd = "zone_loot", .u = "zone_loot", .fn = ShowZoneLoot, .a = {"#viewzoneloot"}},
|
||||||
Cmd{.cmd = "zone_points", .u = "zone_points", .fn = ShowZonePoints, .a = {"#showzonepoints"}},
|
Cmd{.cmd = "zone_points", .u = "zone_points", .fn = ShowZonePoints, .a = {"#showzonepoints"}},
|
||||||
Cmd{.cmd = "zone_status", .u = "zone_status", .fn = ShowZoneStatus, .a = {"#zonestatus"}},
|
Cmd{.cmd = "zone_status", .u = "zone_status", .fn = ShowZoneStatus, .a = {"#zonestatus"}},
|
||||||
|
Cmd{.cmd = "zone_variables", .u = "zone_variables", .fn = ShowZoneVariables},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check for arguments
|
// Check for arguments
|
||||||
|
|||||||
@@ -81,11 +81,6 @@ void ShowZoneData(Client *c, const Seperator *sep)
|
|||||||
DialogueWindow::TableCell(std::to_string(zone->newzone_data.time_type))
|
DialogueWindow::TableCell(std::to_string(zone->newzone_data.time_type))
|
||||||
);
|
);
|
||||||
|
|
||||||
popup_table += DialogueWindow::TableRow(
|
|
||||||
DialogueWindow::TableCell("Time Type") +
|
|
||||||
DialogueWindow::TableCell(std::to_string(zone->newzone_data.time_type))
|
|
||||||
);
|
|
||||||
|
|
||||||
popup_table += DialogueWindow::TableRow(
|
popup_table += DialogueWindow::TableRow(
|
||||||
DialogueWindow::TableCell("Experience Multiplier") +
|
DialogueWindow::TableCell("Experience Multiplier") +
|
||||||
DialogueWindow::TableCell(
|
DialogueWindow::TableCell(
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include "../../client.h"
|
||||||
|
#include "../../zone.h"
|
||||||
|
|
||||||
|
extern Zone* zone;
|
||||||
|
|
||||||
|
void ShowZoneVariables(Client *c, const Seperator *sep)
|
||||||
|
{
|
||||||
|
if (!zone->GetVariables().empty()) {
|
||||||
|
c->Message(Chat::White, "Zone Variables:");
|
||||||
|
for (auto &key: zone->GetVariables()) {
|
||||||
|
c->Message(Chat::White, fmt::format("{}: {}", key, zone->GetVariable(key)).c_str());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c->Message(Chat::White, "No zone variables set.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,18 @@ extern WorldServer worldserver;
|
|||||||
void command_zoneshutdown(Client *c, const Seperator *sep)
|
void command_zoneshutdown(Client *c, const Seperator *sep)
|
||||||
{
|
{
|
||||||
const int arguments = sep->argnum;
|
const int arguments = sep->argnum;
|
||||||
if (!arguments) {
|
if (arguments < 2) {
|
||||||
c->Message(Chat::White, "Usage: #zoneshutdown [Zone ID|Zone Short Name]");
|
c->Message(Chat::White, "Usage: #zoneshutdown instance [Instance ID]");
|
||||||
|
c->Message(Chat::White, "Usage: #zoneshutdown zone [Zone ID|Zone Short Name]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_instance = !strcasecmp(sep->arg[1], "instance");
|
||||||
|
bool is_zone = !strcasecmp(sep->arg[1], "zone");
|
||||||
|
|
||||||
|
if (!is_instance && !is_zone) {
|
||||||
|
c->Message(Chat::White, "Usage: #zoneshutdown instance [Instance ID]");
|
||||||
|
c->Message(Chat::White, "Usage: #zoneshutdown zone [Zone ID|Zone Short Name]");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -16,7 +26,27 @@ void command_zoneshutdown(Client *c, const Seperator *sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32 zone_id = sep->IsNumber(1) ? Strings::ToUnsignedInt(sep->arg[1]) : ZoneID(sep->arg[1]);
|
uint32 zone_id = 0;
|
||||||
|
uint16 instance_id = 0;
|
||||||
|
std::string message = "";
|
||||||
|
|
||||||
|
if (is_instance) {
|
||||||
|
instance_id = sep->IsNumber(2) ? Strings::ToUnsignedInt(sep->arg[2]) : 0;
|
||||||
|
|
||||||
|
if (!database.CheckInstanceExists(instance_id)) {
|
||||||
|
c->Message(
|
||||||
|
Chat::White,
|
||||||
|
fmt::format(
|
||||||
|
"Instance ID '{}' does not exist.",
|
||||||
|
instance_id
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
message = fmt::format("Instance ID {}", instance_id);
|
||||||
|
} else if (is_zone) {
|
||||||
|
zone_id = sep->IsNumber(2) ? Strings::ToUnsignedInt(sep->arg[2]) : ZoneID(sep->arg[2]);
|
||||||
|
|
||||||
if (!zone_id) {
|
if (!zone_id) {
|
||||||
c->Message(
|
c->Message(
|
||||||
@@ -29,10 +59,22 @@ void command_zoneshutdown(Client *c, const Seperator *sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message = fmt::format("{} (ID {})", ZoneLongName(zone_id), zone_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
c->Message(
|
||||||
|
Chat::White,
|
||||||
|
fmt::format(
|
||||||
|
"Attempting to shut down {}.",
|
||||||
|
message
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
|
|
||||||
auto pack = new ServerPacket(ServerOP_ZoneShutdown, sizeof(ServerZoneStateChange_Struct));
|
auto pack = new ServerPacket(ServerOP_ZoneShutdown, sizeof(ServerZoneStateChange_Struct));
|
||||||
auto *s = (ServerZoneStateChange_Struct *) pack->pBuffer;
|
auto *s = (ServerZoneStateChange_Struct *) pack->pBuffer;
|
||||||
|
|
||||||
s->zone_id = zone_id;
|
s->zone_id = zone_id;
|
||||||
|
s->instance_id = instance_id;
|
||||||
|
|
||||||
strn0cpy(s->admin_name, c->GetName(), sizeof(s->admin_name));
|
strn0cpy(s->admin_name, c->GetName(), sizeof(s->admin_name));
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -195,7 +195,7 @@ void Client::SendGuildList()
|
|||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
cereal::BinaryOutputArchive ar(ss);
|
cereal::BinaryOutputArchive ar(ss);
|
||||||
ar(guilds_list);
|
{ ar(guilds_list); }
|
||||||
|
|
||||||
uint32 packet_size = ss.str().length();
|
uint32 packet_size = ss.str().length();
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -23,6 +23,11 @@ void NPC::AddLootTable(uint32 loottable_id, bool is_global)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_resumed_from_zone_suspend) {
|
||||||
|
LogZoneState("NPC [{}] is resuming from zone suspend, skipping", GetCleanName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!is_global) {
|
if (!is_global) {
|
||||||
m_loot_copper = 0;
|
m_loot_copper = 0;
|
||||||
m_loot_silver = 0;
|
m_loot_silver = 0;
|
||||||
@@ -277,7 +282,7 @@ void NPC::AddLootDrop(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (m_resumed_from_zone_suspend) {
|
if (m_resumed_from_zone_suspend) {
|
||||||
LogZoneState("NPC [{}] is resuming from zone suspend, skipping AddItem", GetCleanName());
|
LogZoneState("NPC [{}] is resuming from zone suspend, skipping", GetCleanName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3496,6 +3496,24 @@ std::string Lua_Client::GetAccountBucketRemaining(std::string bucket_name)
|
|||||||
return self->GetAccountBucketRemaining(bucket_name);
|
return self->GetAccountBucketRemaining(bucket_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Lua_Client::GrantNameChange()
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->GrantNameChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lua_Client::IsNameChangeAllowed()
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Bool();
|
||||||
|
return self->IsNameChangeAllowed();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lua_Client::ClearNameChange()
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Bool();
|
||||||
|
return self->ClearNameChange();
|
||||||
|
}
|
||||||
|
|
||||||
std::string Lua_Client::GetBandolierName(uint8 bandolier_slot)
|
std::string Lua_Client::GetBandolierName(uint8 bandolier_slot)
|
||||||
{
|
{
|
||||||
Lua_Safe_Call_String();
|
Lua_Safe_Call_String();
|
||||||
@@ -3635,6 +3653,7 @@ luabind::scope lua_register_client() {
|
|||||||
.def("CashReward", &Lua_Client::CashReward)
|
.def("CashReward", &Lua_Client::CashReward)
|
||||||
.def("ChangeLastName", (void(Lua_Client::*)(std::string))&Lua_Client::ChangeLastName)
|
.def("ChangeLastName", (void(Lua_Client::*)(std::string))&Lua_Client::ChangeLastName)
|
||||||
.def("GrantPetNameChange", &Lua_Client::GrantPetNameChange)
|
.def("GrantPetNameChange", &Lua_Client::GrantPetNameChange)
|
||||||
|
.def("ClearNameChange", &Lua_Client::ClearNameChange)
|
||||||
.def("CharacterID", (uint32(Lua_Client::*)(void))&Lua_Client::CharacterID)
|
.def("CharacterID", (uint32(Lua_Client::*)(void))&Lua_Client::CharacterID)
|
||||||
.def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob))&Lua_Client::CheckIncreaseSkill)
|
.def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob))&Lua_Client::CheckIncreaseSkill)
|
||||||
.def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob,int))&Lua_Client::CheckIncreaseSkill)
|
.def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob,int))&Lua_Client::CheckIncreaseSkill)
|
||||||
@@ -3851,6 +3870,7 @@ luabind::scope lua_register_client() {
|
|||||||
.def("GrantAllAAPoints", (void(Lua_Client::*)(uint8,bool))&Lua_Client::GrantAllAAPoints)
|
.def("GrantAllAAPoints", (void(Lua_Client::*)(uint8,bool))&Lua_Client::GrantAllAAPoints)
|
||||||
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int))&Lua_Client::GrantAlternateAdvancementAbility)
|
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int))&Lua_Client::GrantAlternateAdvancementAbility)
|
||||||
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int, bool))&Lua_Client::GrantAlternateAdvancementAbility)
|
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int, bool))&Lua_Client::GrantAlternateAdvancementAbility)
|
||||||
|
.def("GrantNameChange", &Lua_Client::GrantNameChange)
|
||||||
.def("GuildID", (uint32(Lua_Client::*)(void))&Lua_Client::GuildID)
|
.def("GuildID", (uint32(Lua_Client::*)(void))&Lua_Client::GuildID)
|
||||||
.def("GuildRank", (int(Lua_Client::*)(void))&Lua_Client::GuildRank)
|
.def("GuildRank", (int(Lua_Client::*)(void))&Lua_Client::GuildRank)
|
||||||
.def("HasAugmentEquippedByID", (bool(Lua_Client::*)(uint32))&Lua_Client::HasAugmentEquippedByID)
|
.def("HasAugmentEquippedByID", (bool(Lua_Client::*)(uint32))&Lua_Client::HasAugmentEquippedByID)
|
||||||
@@ -3881,6 +3901,7 @@ luabind::scope lua_register_client() {
|
|||||||
.def("IsInAGuild", (bool(Lua_Client::*)(void))&Lua_Client::IsInAGuild)
|
.def("IsInAGuild", (bool(Lua_Client::*)(void))&Lua_Client::IsInAGuild)
|
||||||
.def("IsLD", (bool(Lua_Client::*)(void))&Lua_Client::IsLD)
|
.def("IsLD", (bool(Lua_Client::*)(void))&Lua_Client::IsLD)
|
||||||
.def("IsMedding", (bool(Lua_Client::*)(void))&Lua_Client::IsMedding)
|
.def("IsMedding", (bool(Lua_Client::*)(void))&Lua_Client::IsMedding)
|
||||||
|
.def("IsNameChangeAllowed", &Lua_Client::IsNameChangeAllowed)
|
||||||
.def("IsRaidGrouped", (bool(Lua_Client::*)(void))&Lua_Client::IsRaidGrouped)
|
.def("IsRaidGrouped", (bool(Lua_Client::*)(void))&Lua_Client::IsRaidGrouped)
|
||||||
.def("IsSitting", (bool(Lua_Client::*)(void))&Lua_Client::IsSitting)
|
.def("IsSitting", (bool(Lua_Client::*)(void))&Lua_Client::IsSitting)
|
||||||
.def("IsStanding", (bool(Lua_Client::*)(void))&Lua_Client::IsStanding)
|
.def("IsStanding", (bool(Lua_Client::*)(void))&Lua_Client::IsStanding)
|
||||||
|
|||||||
@@ -609,6 +609,10 @@ public:
|
|||||||
void ShowZoneShardMenu();
|
void ShowZoneShardMenu();
|
||||||
void GrantPetNameChange();
|
void GrantPetNameChange();
|
||||||
|
|
||||||
|
void GrantNameChange();
|
||||||
|
bool IsNameChangeAllowed();
|
||||||
|
bool ClearNameChange();
|
||||||
|
|
||||||
Lua_Expedition CreateExpedition(luabind::object expedition_info);
|
Lua_Expedition CreateExpedition(luabind::object expedition_info);
|
||||||
Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players);
|
Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players);
|
||||||
Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players, bool disable_messages);
|
Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players, bool disable_messages);
|
||||||
|
|||||||
@@ -500,6 +500,9 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Timer InterserverTimer(INTERSERVER_TIMER); // does MySQL pings and auto-reconnect
|
Timer InterserverTimer(INTERSERVER_TIMER); // does MySQL pings and auto-reconnect
|
||||||
|
Timer UpdateWhoTimer(RuleI(Zone, UpdateWhoTimer) * 1000); // updates who list every 2 minutes
|
||||||
|
Timer WorldserverProcess(1000);
|
||||||
|
|
||||||
#ifdef EQPROFILE
|
#ifdef EQPROFILE
|
||||||
#ifdef PROFILE_DUMP_TIME
|
#ifdef PROFILE_DUMP_TIME
|
||||||
Timer profile_dump_timer(PROFILE_DUMP_TIME * 1000);
|
Timer profile_dump_timer(PROFILE_DUMP_TIME * 1000);
|
||||||
@@ -615,6 +618,10 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (WorldserverProcess.Check()) {
|
||||||
|
worldserver.Process();
|
||||||
|
}
|
||||||
|
|
||||||
if (is_zone_loaded) {
|
if (is_zone_loaded) {
|
||||||
{
|
{
|
||||||
entity_list.GroupProcess();
|
entity_list.GroupProcess();
|
||||||
@@ -647,8 +654,11 @@ int main(int argc, char **argv)
|
|||||||
InterserverTimer.Start();
|
InterserverTimer.Start();
|
||||||
database.ping();
|
database.ping();
|
||||||
content_db.ping();
|
content_db.ping();
|
||||||
|
if (UpdateWhoTimer.Check()) {
|
||||||
|
UpdateWhoTimer.SetTimer(RuleI(Zone, UpdateWhoTimer) * 1000); // in-case it was changed
|
||||||
entity_list.UpdateWho();
|
entity_list.UpdateWho();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
EQ::Timer process_timer(loop_fn);
|
EQ::Timer process_timer(loop_fn);
|
||||||
|
|||||||
+30
-37
@@ -131,7 +131,8 @@ Mob::Mob(
|
|||||||
m_scan_close_mobs_timer(6000),
|
m_scan_close_mobs_timer(6000),
|
||||||
m_see_close_mobs_timer(1000),
|
m_see_close_mobs_timer(1000),
|
||||||
m_mob_check_moving_timer(1000),
|
m_mob_check_moving_timer(1000),
|
||||||
bot_attack_flag_timer(10000)
|
bot_attack_flag_timer(10000),
|
||||||
|
m_destroying(false)
|
||||||
{
|
{
|
||||||
mMovementManager = &MobMovementManager::Get();
|
mMovementManager = &MobMovementManager::Get();
|
||||||
mMovementManager->AddMob(this);
|
mMovementManager->AddMob(this);
|
||||||
@@ -531,6 +532,8 @@ Mob::Mob(
|
|||||||
|
|
||||||
Mob::~Mob()
|
Mob::~Mob()
|
||||||
{
|
{
|
||||||
|
m_destroying = true;
|
||||||
|
|
||||||
entity_list.RemoveMobFromCloseLists(this);
|
entity_list.RemoveMobFromCloseLists(this);
|
||||||
m_close_mobs.clear();
|
m_close_mobs.clear();
|
||||||
|
|
||||||
@@ -8672,85 +8675,75 @@ bool Mob::IsInGroupOrRaid(Mob* other, bool same_raid_group) {
|
|||||||
|
|
||||||
bool Mob::DoLosChecks(Mob* other) {
|
bool Mob::DoLosChecks(Mob* other) {
|
||||||
if (!CheckLosFN(other) || !CheckWaterLoS(other)) {
|
if (!CheckLosFN(other) || !CheckWaterLoS(other)) {
|
||||||
if (CheckLosCheatExempt(other)) {
|
if (RuleB(Map, EnableLoSCheatExemptions) && CheckLosCheatExempt(other)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CheckLosCheat(other)) {
|
if (RuleB(Map, CheckForDoorLoSCheat) && !CheckDoorLoSCheat(other)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mob::CheckLosCheat(Mob* other) {
|
bool Mob::CheckDoorLoSCheat(Mob* other) {
|
||||||
if (RuleB(Map, CheckForLoSCheat)) {
|
if (!other->IsOfClientBotMerc() && other->CastToNPC()->IsOnHatelist(this)) {
|
||||||
for (auto itr : entity_list.GetDoorsList()) {
|
return true;
|
||||||
Doors* d = itr.second;
|
}
|
||||||
|
|
||||||
|
const std::string& zones_to_check = RuleS(Map, ZonesToCheckDoorCheat);
|
||||||
|
|
||||||
|
if (zones_to_check.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& v = Strings::Split(zones_to_check, ",");
|
||||||
|
|
||||||
|
if (zones_to_check == "all" || std::find(v.begin(), v.end(), std::to_string(zone->GetZoneID())) != v.end()) {
|
||||||
|
for (auto itr: entity_list.GetDoorsList()) {
|
||||||
|
Doors *d = itr.second;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!d->IsDoorOpen() &&
|
!d->IsDoorOpen() &&
|
||||||
(
|
(
|
||||||
d->GetKeyItem() ||
|
d->GetKeyItem() ||
|
||||||
d->GetLockpick() ||
|
d->GetLockpick() ||
|
||||||
d->IsDoorOpen() ||
|
|
||||||
d->IsDoorBlacklisted() ||
|
d->IsDoorBlacklisted() ||
|
||||||
d->GetNoKeyring() != 0 ||
|
d->GetNoKeyring() != 0
|
||||||
d->GetDoorParam() > 0
|
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
// If the door is a trigger door, check if the trigger door is open
|
float distance = Distance(m_Position, d->GetPosition());
|
||||||
if (d->GetTriggerDoorID() > 0) {
|
|
||||||
auto td = entity_list.GetDoorsByDoorID(d->GetTriggerDoorID());
|
|
||||||
|
|
||||||
if (td) {
|
if (distance > RuleR(Map, RangeCheckForDoorLoSCheat) || !CheckLosFN(d->GetX(), d->GetY(), d->GetZ(), GetSize())) {
|
||||||
if (Strings::RemoveNumbers(d->GetDoorName()) != Strings::RemoveNumbers(td->GetDoorName())) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DistanceNoZ(GetPosition(), d->GetPosition()) <= 50) {
|
if (d->IsDoorBetween(GetPosition(), other->GetPosition(), d->GetSize())) {
|
||||||
auto who_to_door = DistanceNoZ(GetPosition(), d->GetPosition());
|
|
||||||
auto other_to_door = DistanceNoZ(other->GetPosition(), d->GetPosition());
|
|
||||||
auto who_to_other = DistanceNoZ(GetPosition(), other->GetPosition());
|
|
||||||
auto distance_difference = who_to_other - (who_to_door + other_to_door);
|
|
||||||
|
|
||||||
if (distance_difference >= (-1 * RuleR(Maps, RangeCheckForLoSCheat)) && distance_difference <= RuleR(Maps, RangeCheckForLoSCheat)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mob::CheckLosCheatExempt(Mob* other)
|
bool Mob::CheckLosCheatExempt(Mob* other) {
|
||||||
{
|
|
||||||
if (RuleB(Map, EnableLoSCheatExemptions)) {
|
|
||||||
/* This is an exmaple of how to configure exemptions for LoS checks.
|
|
||||||
glm::vec4 exempt_check_who;
|
glm::vec4 exempt_check_who;
|
||||||
glm::vec4 exempt_check_other;
|
|
||||||
|
|
||||||
switch (zone->GetZoneID()) {
|
switch (zone->GetZoneID()) {
|
||||||
case POEARTHB:
|
case Zones::POEARTHB:
|
||||||
exempt_check_who.x = 2051; exempt_check_who.y = 407; exempt_check_who.z = -219; //Middle of councilman spawns
|
exempt_check_who.x = 2053; exempt_check_who.y = 408; exempt_check_who.z = -219; //Middle of councilman spawns
|
||||||
//exempt_check_other.x = 1455; exempt_check_other.y = 415; exempt_check_other.z = -242;
|
//if the player is inside the cove they cannot be higher than the ceiling (no exploiting from uptop) --- 800 from center of council to furthest corner in cove
|
||||||
//check to be sure the player and the target are outside of the councilman area
|
|
||||||
//if the player is inside the cove they cannot be higher than the ceiling (no exploiting from uptop)
|
|
||||||
if (GetZ() <= -171 && other->GetZ() <= -171 && DistanceNoZ(other->GetPosition(), exempt_check_who) <= 800 && DistanceNoZ(GetPosition(), exempt_check_who) <= 800) {
|
if (GetZ() <= -171 && other->GetZ() <= -171 && DistanceNoZ(other->GetPosition(), exempt_check_who) <= 800 && DistanceNoZ(GetPosition(), exempt_check_who) <= 800) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -798,7 +798,7 @@ public:
|
|||||||
static bool CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarget, float sizeTarget);
|
static bool CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarget, float sizeTarget);
|
||||||
virtual bool CheckWaterLoS(Mob* m);
|
virtual bool CheckWaterLoS(Mob* m);
|
||||||
bool CheckPositioningLosFN(Mob* other, float posX, float posY, float posZ);
|
bool CheckPositioningLosFN(Mob* other, float posX, float posY, float posZ);
|
||||||
bool CheckLosCheat(Mob* other); //door skipping checks for LoS
|
bool CheckDoorLoSCheat(Mob* other); //door skipping checks for LoS
|
||||||
bool CheckLosCheatExempt(Mob* other); //exemptions to bypass los
|
bool CheckLosCheatExempt(Mob* other); //exemptions to bypass los
|
||||||
bool DoLosChecks(Mob* other);
|
bool DoLosChecks(Mob* other);
|
||||||
inline void SetLastLosState(bool value) { last_los_check = value; }
|
inline void SetLastLosState(bool value) { last_los_check = value; }
|
||||||
@@ -1125,7 +1125,7 @@ public:
|
|||||||
|
|
||||||
virtual void SetAttackTimer();
|
virtual void SetAttackTimer();
|
||||||
inline void SetInvul(bool invul) { invulnerable=invul; }
|
inline void SetInvul(bool invul) { invulnerable=invul; }
|
||||||
inline bool GetInvul(void) { return invulnerable; }
|
inline bool GetInvul() { return invulnerable; }
|
||||||
void SetExtraHaste(int haste, bool need_to_save = true);
|
void SetExtraHaste(int haste, bool need_to_save = true);
|
||||||
inline int GetExtraHaste() { return extra_haste; }
|
inline int GetExtraHaste() { return extra_haste; }
|
||||||
virtual int GetHaste();
|
virtual int GetHaste();
|
||||||
@@ -1510,6 +1510,7 @@ public:
|
|||||||
|
|
||||||
void ClearDataBucketCache();
|
void ClearDataBucketCache();
|
||||||
bool IsGuildmaster() const;
|
bool IsGuildmaster() const;
|
||||||
|
bool IsDestroying() const { return m_destroying; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void CommonDamage(Mob* other, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None);
|
void CommonDamage(Mob* other, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None);
|
||||||
@@ -1932,6 +1933,7 @@ private:
|
|||||||
EQ::InventoryProfile m_inv;
|
EQ::InventoryProfile m_inv;
|
||||||
std::shared_ptr<HealRotation> m_target_of_heal_rotation;
|
std::shared_ptr<HealRotation> m_target_of_heal_rotation;
|
||||||
bool m_manual_follow;
|
bool m_manual_follow;
|
||||||
|
bool m_destroying;
|
||||||
|
|
||||||
void SetHeroicStrBonuses(StatBonuses* n);
|
void SetHeroicStrBonuses(StatBonuses* n);
|
||||||
void SetHeroicStaBonuses(StatBonuses* n);
|
void SetHeroicStaBonuses(StatBonuses* n);
|
||||||
|
|||||||
@@ -626,6 +626,9 @@ inline void NPCCommandsMenu(Client* client, NPC* npc)
|
|||||||
|
|
||||||
if (npc->GetLoottableID() > 0) {
|
if (npc->GetLoottableID() > 0) {
|
||||||
menu_commands += "[" + Saylink::Silent("#npcloot show", "Loot") + "] ";
|
menu_commands += "[" + Saylink::Silent("#npcloot show", "Loot") + "] ";
|
||||||
|
if (npc) {
|
||||||
|
menu_commands += fmt::format(" Item(s) ({}) ", npc->GetLootItems().size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (npc->IsProximitySet()) {
|
if (npc->IsProximitySet()) {
|
||||||
|
|||||||
+1
-36
@@ -132,9 +132,7 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
|
|||||||
),
|
),
|
||||||
attacked_timer(CombatEventTimer_expire),
|
attacked_timer(CombatEventTimer_expire),
|
||||||
swarm_timer(100),
|
swarm_timer(100),
|
||||||
m_corpse_queue_timer(1000),
|
m_resumed_from_zone_suspend_shutoff_timer(10000),
|
||||||
m_corpse_queue_shutoff_timer(30000),
|
|
||||||
m_resumed_from_zone_suspend_shutoff_timer(30000),
|
|
||||||
classattack_timer(1000),
|
classattack_timer(1000),
|
||||||
monkattack_timer(1000),
|
monkattack_timer(1000),
|
||||||
knightattack_timer(1000),
|
knightattack_timer(1000),
|
||||||
@@ -624,28 +622,6 @@ bool NPC::Process()
|
|||||||
|
|
||||||
// zone state corpse creation timer
|
// zone state corpse creation timer
|
||||||
if (RuleB(Zone, StateSavingOnShutdown)) {
|
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||||
// creates a corpse if the NPC is queued for corpse creation
|
|
||||||
if (m_corpse_queue_timer.Check()) {
|
|
||||||
if (IsQueuedForCorpse()) {
|
|
||||||
auto decay_timer = m_corpse_decay_time;
|
|
||||||
uint16 corpse_id = GetID();
|
|
||||||
Death(this, GetHP() + 1, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand);
|
|
||||||
auto c = entity_list.GetCorpseByID(corpse_id);
|
|
||||||
if (c) {
|
|
||||||
c->UnLock();
|
|
||||||
c->SetDecayTimer(decay_timer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_corpse_queue_timer.Disable();
|
|
||||||
m_corpse_queue_shutoff_timer.Disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
// shuts off the corpse queue timer if it is still running
|
|
||||||
if (m_corpse_queue_shutoff_timer.Check()) {
|
|
||||||
m_corpse_queue_timer.Disable();
|
|
||||||
m_corpse_queue_shutoff_timer.Disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
// shuts off the temporary spawn protected state of the NPC
|
// shuts off the temporary spawn protected state of the NPC
|
||||||
if (m_resumed_from_zone_suspend_shutoff_timer.Check()) {
|
if (m_resumed_from_zone_suspend_shutoff_timer.Check()) {
|
||||||
m_resumed_from_zone_suspend_shutoff_timer.Disable();
|
m_resumed_from_zone_suspend_shutoff_timer.Disable();
|
||||||
@@ -654,17 +630,6 @@ bool NPC::Process()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (tic_timer.Check()) {
|
if (tic_timer.Check()) {
|
||||||
if (RuleB(Zone, StateSavingOnShutdown) && IsQueuedForCorpse()) {
|
|
||||||
auto decay_timer = m_corpse_decay_time;
|
|
||||||
uint16 corpse_id = GetID();
|
|
||||||
Death(this, GetHP() + 1, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand);
|
|
||||||
auto c = entity_list.GetCorpseByID(corpse_id);
|
|
||||||
if (c) {
|
|
||||||
c->UnLock();
|
|
||||||
c->SetDecayTimer(decay_timer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_TICK)) {
|
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_TICK)) {
|
||||||
parse->EventNPC(EVENT_TICK, this, nullptr, "", 0);
|
parse->EventNPC(EVENT_TICK, this, nullptr, "", 0);
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-8
@@ -603,10 +603,9 @@ public:
|
|||||||
|
|
||||||
// zone state save
|
// zone state save
|
||||||
inline void SetQueuedToCorpse() { m_queued_for_corpse = true; }
|
inline void SetQueuedToCorpse() { m_queued_for_corpse = true; }
|
||||||
inline bool IsQueuedForCorpse() { return m_queued_for_corpse; }
|
inline bool IsQueuedForCorpse() const { return m_queued_for_corpse; }
|
||||||
inline uint32_t SetCorpseDecayTime(uint32_t decay_time) { return m_corpse_decay_time = decay_time; }
|
|
||||||
inline void SetResumedFromZoneSuspend(bool state = true) { m_resumed_from_zone_suspend = state; }
|
inline void SetResumedFromZoneSuspend(bool state = true) { m_resumed_from_zone_suspend = state; }
|
||||||
inline bool IsResumedFromZoneSuspend() { return m_resumed_from_zone_suspend; }
|
inline bool IsResumedFromZoneSuspend() const { return m_resumed_from_zone_suspend; }
|
||||||
|
|
||||||
inline void LoadBuffsFromState(std::vector<Buffs_Struct> in_buffs) {
|
inline void LoadBuffsFromState(std::vector<Buffs_Struct> in_buffs) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -660,13 +659,10 @@ protected:
|
|||||||
// zone state
|
// zone state
|
||||||
bool m_resumed_from_zone_suspend = false;
|
bool m_resumed_from_zone_suspend = false;
|
||||||
bool m_queued_for_corpse = false; // this is to check for corpse creation on zone state restore
|
bool m_queued_for_corpse = false; // this is to check for corpse creation on zone state restore
|
||||||
uint32_t m_corpse_decay_time = 0; // decay time set on zone state restore
|
|
||||||
Timer m_corpse_queue_timer = {}; // this is to check for corpse creation on zone state restore
|
|
||||||
Timer m_corpse_queue_shutoff_timer = {};
|
|
||||||
|
|
||||||
// this is a 30-second timer that protects a NPC from having double assignment of loot
|
// this is a timer that protects a NPC from having double assignment of loot
|
||||||
// this is to prevent a player from killing a NPC and then zoning out and back in to get loot again
|
// this is to prevent a player from killing a NPC and then zoning out and back in to get loot again
|
||||||
// if loot was to be assigned via script again, this protects double assignment for 30 seconds
|
// if loot was to be assigned via script again, this protects double assignment for a short time
|
||||||
Timer m_resumed_from_zone_suspend_shutoff_timer = {};
|
Timer m_resumed_from_zone_suspend_shutoff_timer = {};
|
||||||
|
|
||||||
std::list<NpcFactionEntriesRepository::NpcFactionEntries> faction_list;
|
std::list<NpcFactionEntriesRepository::NpcFactionEntries> faction_list;
|
||||||
|
|||||||
@@ -3261,6 +3261,21 @@ std::string Perl_Client_GetAccountBucketRemaining(Client* self, std::string buck
|
|||||||
return self->GetAccountBucketRemaining(bucket_name);
|
return self->GetAccountBucketRemaining(bucket_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Perl_Client_GrantNameChange(Client* self)
|
||||||
|
{
|
||||||
|
self->GrantNameChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Perl_Client_IsNameChangeAllowed(Client* self)
|
||||||
|
{
|
||||||
|
return self->IsNameChangeAllowed();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Perl_Client_ClearNameChange(Client* self)
|
||||||
|
{
|
||||||
|
return self->ClearNameChange();
|
||||||
|
}
|
||||||
|
|
||||||
std::string Perl_Client_GetBandolierName(Client* self, uint8 bandolier_slot)
|
std::string Perl_Client_GetBandolierName(Client* self, uint8 bandolier_slot)
|
||||||
{
|
{
|
||||||
return self->GetBandolierName(bandolier_slot);
|
return self->GetBandolierName(bandolier_slot);
|
||||||
@@ -3393,6 +3408,7 @@ void perl_register_client()
|
|||||||
package.add("CashReward", &Perl_Client_CashReward);
|
package.add("CashReward", &Perl_Client_CashReward);
|
||||||
package.add("ChangeLastName", &Perl_Client_ChangeLastName);
|
package.add("ChangeLastName", &Perl_Client_ChangeLastName);
|
||||||
package.add("GrantPetNameChange", &Perl_Client_GrantPetNameChange);
|
package.add("GrantPetNameChange", &Perl_Client_GrantPetNameChange);
|
||||||
|
package.add("ClearNameChange", (bool(*)(Client*))&Perl_Client_ClearNameChange);
|
||||||
package.add("CharacterID", &Perl_Client_CharacterID);
|
package.add("CharacterID", &Perl_Client_CharacterID);
|
||||||
package.add("CheckIncreaseSkill", (bool(*)(Client*, int))&Perl_Client_CheckIncreaseSkill);
|
package.add("CheckIncreaseSkill", (bool(*)(Client*, int))&Perl_Client_CheckIncreaseSkill);
|
||||||
package.add("CheckIncreaseSkill", (bool(*)(Client*, int, int))&Perl_Client_CheckIncreaseSkill);
|
package.add("CheckIncreaseSkill", (bool(*)(Client*, int, int))&Perl_Client_CheckIncreaseSkill);
|
||||||
@@ -3607,6 +3623,7 @@ void perl_register_client()
|
|||||||
package.add("GrantAllAAPoints", (void(*)(Client*, uint8, bool))&Perl_Client_GrantAllAAPoints);
|
package.add("GrantAllAAPoints", (void(*)(Client*, uint8, bool))&Perl_Client_GrantAllAAPoints);
|
||||||
package.add("GrantAlternateAdvancementAbility", (bool(*)(Client*, int, int))&Perl_Client_GrantAlternateAdvancementAbility);
|
package.add("GrantAlternateAdvancementAbility", (bool(*)(Client*, int, int))&Perl_Client_GrantAlternateAdvancementAbility);
|
||||||
package.add("GrantAlternateAdvancementAbility", (bool(*)(Client*, int, int, bool))&Perl_Client_GrantAlternateAdvancementAbility);
|
package.add("GrantAlternateAdvancementAbility", (bool(*)(Client*, int, int, bool))&Perl_Client_GrantAlternateAdvancementAbility);
|
||||||
|
package.add("GrantNameChange", (void(*)(Client*))&Perl_Client_GrantNameChange);
|
||||||
package.add("GuildID", &Perl_Client_GuildID);
|
package.add("GuildID", &Perl_Client_GuildID);
|
||||||
package.add("GuildRank", &Perl_Client_GuildRank);
|
package.add("GuildRank", &Perl_Client_GuildRank);
|
||||||
package.add("HasAugmentEquippedByID", &Perl_Client_HasAugmentEquippedByID);
|
package.add("HasAugmentEquippedByID", &Perl_Client_HasAugmentEquippedByID);
|
||||||
@@ -3637,6 +3654,7 @@ void perl_register_client()
|
|||||||
package.add("IsInAGuild", &Perl_Client_IsInAGuild);
|
package.add("IsInAGuild", &Perl_Client_IsInAGuild);
|
||||||
package.add("IsLD", &Perl_Client_IsLD);
|
package.add("IsLD", &Perl_Client_IsLD);
|
||||||
package.add("IsMedding", &Perl_Client_IsMedding);
|
package.add("IsMedding", &Perl_Client_IsMedding);
|
||||||
|
package.add("IsNameChangeAllowed", (bool(*)(Client*))&Perl_Client_IsNameChangeAllowed);
|
||||||
package.add("IsRaidGrouped", &Perl_Client_IsRaidGrouped);
|
package.add("IsRaidGrouped", &Perl_Client_IsRaidGrouped);
|
||||||
package.add("IsSitting", &Perl_Client_IsSitting);
|
package.add("IsSitting", &Perl_Client_IsSitting);
|
||||||
package.add("IsStanding", &Perl_Client_IsStanding);
|
package.add("IsStanding", &Perl_Client_IsStanding);
|
||||||
|
|||||||
@@ -435,11 +435,9 @@ int QuestParserCollection::EventNPC(
|
|||||||
std::vector<std::any>* extra_pointers
|
std::vector<std::any>* extra_pointers
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (npc->IsResumedFromZoneSuspend()) {
|
if (npc->IsResumedFromZoneSuspend() && npc->IsQueuedForCorpse()) {
|
||||||
if (event_id == EVENT_DEATH_COMPLETE || event_id == EVENT_DEATH) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const int local_return = EventNPCLocal(event_id, npc, init, data, extra_data, extra_pointers);
|
const int local_return = EventNPCLocal(event_id, npc, init, data, extra_data, extra_pointers);
|
||||||
const int global_return = EventNPCGlobal(event_id, npc, init, data, extra_data, extra_pointers);
|
const int global_return = EventNPCGlobal(event_id, npc, init, data, extra_data, extra_pointers);
|
||||||
@@ -941,6 +939,12 @@ QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::strin
|
|||||||
|
|
||||||
Strings::FindReplace(npc_name, "`", "-");
|
Strings::FindReplace(npc_name, "`", "-");
|
||||||
|
|
||||||
|
const std::string& npc_id_and_name = fmt::format(
|
||||||
|
"{}_{}",
|
||||||
|
npc_name,
|
||||||
|
npc_id
|
||||||
|
);
|
||||||
|
|
||||||
const std::string& global_path = fmt::format(
|
const std::string& global_path = fmt::format(
|
||||||
"{}/{}",
|
"{}/{}",
|
||||||
path.GetQuestsPath(),
|
path.GetQuestsPath(),
|
||||||
@@ -961,13 +965,16 @@ QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::strin
|
|||||||
);
|
);
|
||||||
|
|
||||||
std::vector<std::string> file_names = {
|
std::vector<std::string> file_names = {
|
||||||
fmt::format("{}/{}", zone_versioned_path, npc_id), // Local versioned by NPC ID ./quests/zone/v0/10.ext
|
fmt::format("{}/{}", zone_versioned_path, npc_id), // Local versioned by NPC ID (./quests/zone/v0/10.ext)
|
||||||
fmt::format("{}/{}", zone_versioned_path, npc_name), // Local versioned by NPC Name ./quests/zone/v0/npc.ext
|
fmt::format("{}/{}", zone_versioned_path, npc_name), // Local versioned by NPC Name (./quests/zone/v0/name.ext)
|
||||||
|
fmt::format("{}/{}", zone_versioned_path, npc_id_and_name), // Local versioned by NPC ID and NPC Name (./quests/zone/v0/10_name.ext)
|
||||||
fmt::format("{}/{}", zone_path, npc_id), // Local by NPC ID
|
fmt::format("{}/{}", zone_path, npc_id), // Local by NPC ID
|
||||||
fmt::format("{}/{}", zone_path, npc_name), // Local by NPC Name
|
fmt::format("{}/{}", zone_path, npc_name), // Local by NPC Name
|
||||||
|
fmt::format("{}/{}", zone_path, npc_id_and_name), // Local by NPC ID and NPC Name
|
||||||
fmt::format("{}/{}", global_path, npc_id), // Global by NPC ID
|
fmt::format("{}/{}", global_path, npc_id), // Global by NPC ID
|
||||||
fmt::format("{}/{}", global_path, npc_name), // Global by NPC ID
|
fmt::format("{}/{}", global_path, npc_name), // Global by NPC ID
|
||||||
fmt::format("{}/default", zone_versioned_path), // Zone Default ./quests/zone/v0/default.ext
|
fmt::format("{}/{}", global_path, npc_id_and_name), // Global by NPC ID and NPC Name
|
||||||
|
fmt::format("{}/default", zone_versioned_path), // Zone Versioned Default (./quests/zone/v0/default.ext)
|
||||||
fmt::format("{}/default", zone_path), // Zone Default
|
fmt::format("{}/default", zone_path), // Zone Default
|
||||||
fmt::format("{}/default", global_path), // Global Default
|
fmt::format("{}/default", global_path), // Global Default
|
||||||
};
|
};
|
||||||
|
|||||||
+21
-3
@@ -208,6 +208,15 @@ void QuestManager::write(const char *file, const char *str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Mob* QuestManager::spawn2(int npc_id, int grid, int unused, const glm::vec4& position) {
|
Mob* QuestManager::spawn2(int npc_id, int grid, int unused, const glm::vec4& position) {
|
||||||
|
QuestManagerCurrentQuestVars();
|
||||||
|
if (owner && owner->IsNPC()) {
|
||||||
|
auto n = owner->CastToNPC();
|
||||||
|
if (n->IsResumedFromZoneSuspend()) {
|
||||||
|
LogZoneState("NPC [{}] is resuming from zone suspend, skipping quest call", n->GetCleanName());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const NPCType* t = 0;
|
const NPCType* t = 0;
|
||||||
if (t = content_db.LoadNPCTypesData(npc_id)) {
|
if (t = content_db.LoadNPCTypesData(npc_id)) {
|
||||||
auto npc = new NPC(t, nullptr, position, GravityBehavior::Water);
|
auto npc = new NPC(t, nullptr, position, GravityBehavior::Water);
|
||||||
@@ -228,6 +237,15 @@ Mob* QuestManager::spawn2(int npc_id, int grid, int unused, const glm::vec4& pos
|
|||||||
}
|
}
|
||||||
|
|
||||||
Mob* QuestManager::unique_spawn(int npc_type, int grid, int unused, const glm::vec4& position) {
|
Mob* QuestManager::unique_spawn(int npc_type, int grid, int unused, const glm::vec4& position) {
|
||||||
|
QuestManagerCurrentQuestVars();
|
||||||
|
if (owner && owner->IsNPC()) {
|
||||||
|
auto n = owner->CastToNPC();
|
||||||
|
if (n->IsResumedFromZoneSuspend()) {
|
||||||
|
LogZoneState("NPC [{}] is resuming from zone suspend, skipping quest call", n->GetCleanName());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Mob *other = entity_list.GetMobByNpcTypeID(npc_type);
|
Mob *other = entity_list.GetMobByNpcTypeID(npc_type);
|
||||||
if(other != nullptr) {
|
if(other != nullptr) {
|
||||||
return other;
|
return other;
|
||||||
@@ -1292,15 +1310,14 @@ void QuestManager::rename(std::string name) {
|
|||||||
QuestManagerCurrentQuestVars();
|
QuestManagerCurrentQuestVars();
|
||||||
if (initiator) {
|
if (initiator) {
|
||||||
std::string current_name = initiator->GetName();
|
std::string current_name = initiator->GetName();
|
||||||
if (initiator->ChangeFirstName(name.c_str(), current_name.c_str())) {
|
if (initiator->ChangeFirstName(name)) {
|
||||||
initiator->Message(
|
initiator->Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"Successfully renamed to {}, kicking to character select.",
|
"Successfully renamed to {}.",
|
||||||
name
|
name
|
||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
initiator->Kick("Name was changed.");
|
|
||||||
} else {
|
} else {
|
||||||
initiator->Message(
|
initiator->Message(
|
||||||
Chat::Red,
|
Chat::Red,
|
||||||
@@ -3489,6 +3506,7 @@ void QuestManager::UpdateInstanceTimer(uint16 instance_id, uint32 new_duration)
|
|||||||
|
|
||||||
e.duration = new_duration;
|
e.duration = new_duration;
|
||||||
e.start_time = std::time(nullptr);
|
e.start_time = std::time(nullptr);
|
||||||
|
e.expire_at = e.start_time + e.duration;
|
||||||
|
|
||||||
const int updated = InstanceListRepository::UpdateOne(database, e);
|
const int updated = InstanceListRepository::UpdateOne(database, e);
|
||||||
|
|
||||||
|
|||||||
+19
-5
@@ -191,16 +191,20 @@ bool Spawn2::Process() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 condition_value=1;
|
uint16 condition_value = 1;
|
||||||
|
|
||||||
if (condition_id > 0) {
|
if (condition_id > 0) {
|
||||||
condition_value = zone->spawn_conditions.GetCondition(zone->GetShortName(), zone->GetInstanceID(), condition_id);
|
condition_value = zone->spawn_conditions.GetCondition(
|
||||||
|
zone->GetShortName(),
|
||||||
|
zone->GetInstanceID(),
|
||||||
|
condition_id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//have the spawn group pick an NPC for us
|
//have the spawn group pick an NPC for us
|
||||||
uint32 npcid = 0;
|
uint32 npcid = 0;
|
||||||
if (RuleB(Zone, StateSavingOnShutdown) && currentnpcid && currentnpcid > 0) {
|
if (m_resumed_npc_id > 0) {
|
||||||
npcid = currentnpcid;
|
npcid = m_resumed_npc_id;
|
||||||
|
m_resumed_npc_id = 0;
|
||||||
} else {
|
} else {
|
||||||
npcid = spawn_group->GetNPCType(condition_value);
|
npcid = spawn_group->GetNPCType(condition_value);
|
||||||
}
|
}
|
||||||
@@ -277,6 +281,16 @@ bool Spawn2::Process() {
|
|||||||
|
|
||||||
npcthis = npc;
|
npcthis = npc;
|
||||||
|
|
||||||
|
if (!m_entity_variables.empty()) {
|
||||||
|
for (auto &var : m_entity_variables) {
|
||||||
|
npc->SetEntityVariable(var.first, var.second);
|
||||||
|
}
|
||||||
|
m_entity_variables = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
npc->SetResumedFromZoneSuspend(m_resumed_from_zone_suspend);
|
||||||
|
m_resumed_from_zone_suspend = false;
|
||||||
|
|
||||||
npc->AddLootTable();
|
npc->AddLootTable();
|
||||||
if (npc->DropsGlobalLoot()) {
|
if (npc->DropsGlobalLoot()) {
|
||||||
npc->CheckGlobalLootTables();
|
npc->CheckGlobalLootTables();
|
||||||
|
|||||||
@@ -75,6 +75,10 @@ public:
|
|||||||
int16 GetConditionMinValue() const { return condition_min_value; }
|
int16 GetConditionMinValue() const { return condition_min_value; }
|
||||||
int16 GetAnimation () { return anim; }
|
int16 GetAnimation () { return anim; }
|
||||||
inline NPC *GetNPC() const { return npcthis; }
|
inline NPC *GetNPC() const { return npcthis; }
|
||||||
|
inline bool IsResumedFromZoneSuspend() const { return m_resumed_from_zone_suspend; }
|
||||||
|
inline void SetResumedFromZoneSuspend(bool resumed) { m_resumed_from_zone_suspend = resumed; }
|
||||||
|
inline void SetEntityVariables(std::map<std::string, std::string> vars) { m_entity_variables = vars; }
|
||||||
|
inline void SetResumedNPCID(uint32 npc_id) { m_resumed_npc_id = npc_id; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Zone;
|
friend class Zone;
|
||||||
@@ -101,6 +105,9 @@ private:
|
|||||||
EmuAppearance anim;
|
EmuAppearance anim;
|
||||||
bool IsDespawned;
|
bool IsDespawned;
|
||||||
uint32 killcount;
|
uint32 killcount;
|
||||||
|
bool m_resumed_from_zone_suspend = false;
|
||||||
|
uint32 m_resumed_npc_id = 0;
|
||||||
|
std::map<std::string, std::string> m_entity_variables = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
class SpawnCondition {
|
class SpawnCondition {
|
||||||
|
|||||||
@@ -1894,6 +1894,13 @@ void Client::SellToBuyer(const EQApplicationPacket *app)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sell_line.purchase_method == BarterInBazaar && buyer->IsThereACustomer()) {
|
||||||
|
auto customer = entity_list.GetClientByID(buyer->GetCustomerID());
|
||||||
|
if (customer) {
|
||||||
|
customer->CancelBuyerTradeWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!DoBarterBuyerChecks(sell_line)) {
|
if (!DoBarterBuyerChecks(sell_line)) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -3825,3 +3832,18 @@ bool Client::DoBarterSellerChecks(BuyerLineSellItem_Struct &sell_line)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::CancelBuyerTradeWindow()
|
||||||
|
{
|
||||||
|
auto end_session = new EQApplicationPacket(OP_Barter, sizeof(BuyerRemoveItemFromMerchantWindow_Struct));
|
||||||
|
auto data = reinterpret_cast<BuyerRemoveItemFromMerchantWindow_Struct *>(end_session->pBuffer);
|
||||||
|
data->action = Barter_BuyerInspectBegin;
|
||||||
|
|
||||||
|
FastQueuePacket(&end_session);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::CancelTraderTradeWindow()
|
||||||
|
{
|
||||||
|
auto end_session = new EQApplicationPacket(OP_ShopEnd);
|
||||||
|
FastQueuePacket(&end_session);
|
||||||
|
}
|
||||||
|
|||||||
+18
-1
@@ -81,7 +81,6 @@ WorldServer::WorldServer()
|
|||||||
cur_groupid = 0;
|
cur_groupid = 0;
|
||||||
last_groupid = 0;
|
last_groupid = 0;
|
||||||
oocmuted = false;
|
oocmuted = false;
|
||||||
m_process_timer = std::make_unique<EQ::Timer>(1000, true, std::bind(&WorldServer::Process, this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldServer::~WorldServer() {
|
WorldServer::~WorldServer() {
|
||||||
@@ -95,6 +94,7 @@ void WorldServer::Process()
|
|||||||
if (it->second.reload_at_unix < std::time(nullptr)) {
|
if (it->second.reload_at_unix < std::time(nullptr)) {
|
||||||
ProcessReload(it->second);
|
ProcessReload(it->second);
|
||||||
it = m_reload_queue.erase(it);
|
it = m_reload_queue.erase(it);
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
@@ -3786,6 +3786,13 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trader_pc->IsThereACustomer()) {
|
||||||
|
auto customer = entity_list.GetClientByID(trader_pc->GetCustomerID());
|
||||||
|
if (customer) {
|
||||||
|
customer->CancelTraderTradeWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto item_sn = Strings::ToUnsignedBigInt(in->trader_buy_struct.serial_number);
|
auto item_sn = Strings::ToUnsignedBigInt(in->trader_buy_struct.serial_number);
|
||||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_Trader, sizeof(TraderBuy_Struct));
|
auto outapp = std::make_unique<EQApplicationPacket>(OP_Trader, sizeof(TraderBuy_Struct));
|
||||||
auto data = (TraderBuy_Struct *) outapp->pBuffer;
|
auto data = (TraderBuy_Struct *) outapp->pBuffer;
|
||||||
@@ -3981,6 +3988,12 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
|||||||
worldserver.SendPacket(pack);
|
worldserver.SendPacket(pack);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (buyer->IsThereACustomer()) {
|
||||||
|
auto customer = entity_list.GetClientByID(buyer->GetCustomerID());
|
||||||
|
if (customer) {
|
||||||
|
customer->CancelBuyerTradeWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BuyerLineSellItem_Struct sell_line{};
|
BuyerLineSellItem_Struct sell_line{};
|
||||||
sell_line.item_id = in->buy_item_id;
|
sell_line.item_id = in->buy_item_id;
|
||||||
@@ -4586,6 +4599,10 @@ void WorldServer::ProcessReload(const ServerReload::Request& request)
|
|||||||
zone->ReloadLootTables();
|
zone->ReloadLootTables();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ServerReload::Type::Maps:
|
||||||
|
zone->ReloadMaps();
|
||||||
|
break;
|
||||||
|
|
||||||
case ServerReload::Type::Merchants:
|
case ServerReload::Type::Merchants:
|
||||||
entity_list.ReloadMerchants();
|
entity_list.ReloadMerchants();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ private:
|
|||||||
ZoneEventScheduler *m_zone_scheduler;
|
ZoneEventScheduler *m_zone_scheduler;
|
||||||
|
|
||||||
// server reload queue
|
// server reload queue
|
||||||
std::unique_ptr<EQ::Timer> m_process_timer;
|
|
||||||
std::mutex m_reload_mutex = {};
|
std::mutex m_reload_mutex = {};
|
||||||
std::map<int, ServerReload::Request> m_reload_queue = {};
|
std::map<int, ServerReload::Request> m_reload_queue = {};
|
||||||
public:
|
public:
|
||||||
|
|||||||
+8
-3
@@ -887,7 +887,7 @@ void Zone::Shutdown(bool quiet)
|
|||||||
c.second->WorldKick();
|
c.second->WorldKick();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RuleB(Zone, StateSavingOnShutdown)) {
|
if (RuleB(Zone, StateSavingOnShutdown) && zone && zone->IsLoaded()) {
|
||||||
SaveZoneState();
|
SaveZoneState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1534,7 +1534,6 @@ bool Zone::Process() {
|
|||||||
spawn_conditions.Process();
|
spawn_conditions.Process();
|
||||||
|
|
||||||
if (spawn2_timer.Check()) {
|
if (spawn2_timer.Check()) {
|
||||||
|
|
||||||
LinkedListIterator<Spawn2 *> iterator(spawn2_list);
|
LinkedListIterator<Spawn2 *> iterator(spawn2_list);
|
||||||
|
|
||||||
EQ::InventoryProfile::CleanDirty();
|
EQ::InventoryProfile::CleanDirty();
|
||||||
@@ -3285,5 +3284,11 @@ bool Zone::VariableExists(const std::string& variable_name)
|
|||||||
return m_zone_variables.find(variable_name) != m_zone_variables.end();
|
return m_zone_variables.find(variable_name) != m_zone_variables.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "zone_save_state.cpp"
|
void Zone::ReloadMaps()
|
||||||
|
{
|
||||||
|
zonemap = Map::LoadMapFile(map_name);
|
||||||
|
watermap = WaterMap::LoadWaterMapfile(map_name);
|
||||||
|
pathing = IPathfinder::Load(map_name);
|
||||||
|
}
|
||||||
|
|
||||||
#include "zone_loot.cpp"
|
#include "zone_loot.cpp"
|
||||||
|
|||||||
@@ -479,6 +479,7 @@ public:
|
|||||||
);
|
);
|
||||||
void SaveZoneState();
|
void SaveZoneState();
|
||||||
static void ClearZoneState(uint32 zone_id, uint32 instance_id);
|
static void ClearZoneState(uint32 zone_id, uint32 instance_id);
|
||||||
|
void ReloadMaps();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool allow_mercs;
|
bool allow_mercs;
|
||||||
|
|||||||
+12
-6
@@ -31,15 +31,21 @@ void ZoneCLI::CommandHandler(int argc, char **argv)
|
|||||||
// Register commands
|
// Register commands
|
||||||
function_map["benchmark:databuckets"] = &ZoneCLI::BenchmarkDatabuckets;
|
function_map["benchmark:databuckets"] = &ZoneCLI::BenchmarkDatabuckets;
|
||||||
function_map["sidecar:serve-http"] = &ZoneCLI::SidecarServeHttp;
|
function_map["sidecar:serve-http"] = &ZoneCLI::SidecarServeHttp;
|
||||||
function_map["tests:databuckets"] = &ZoneCLI::DataBuckets;
|
function_map["tests:databuckets"] = &ZoneCLI::TestDataBuckets;
|
||||||
function_map["tests:npc-handins"] = &ZoneCLI::NpcHandins;
|
function_map["tests:npc-handins"] = &ZoneCLI::TestNpcHandins;
|
||||||
function_map["tests:npc-handins-multiquest"] = &ZoneCLI::NpcHandinsMultiQuest;
|
function_map["tests:npc-handins-multiquest"] = &ZoneCLI::TestNpcHandinsMultiQuest;
|
||||||
|
function_map["tests:zone-state"] = &ZoneCLI::TestZoneState;
|
||||||
|
|
||||||
EQEmuCommand::HandleMenu(function_map, cmd, argc, argv);
|
EQEmuCommand::HandleMenu(function_map, cmd, argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "cli/databuckets.cpp"
|
// cli
|
||||||
#include "cli/benchmark_databuckets.cpp"
|
#include "cli/benchmark_databuckets.cpp"
|
||||||
#include "cli/sidecar_serve_http.cpp"
|
#include "cli/sidecar_serve_http.cpp"
|
||||||
#include "cli/npc_handins.cpp"
|
|
||||||
#include "cli/npc_handins_multiquest.cpp"
|
// tests
|
||||||
|
#include "cli/tests/_test_util.cpp"
|
||||||
|
#include "cli/tests/databuckets.cpp"
|
||||||
|
#include "cli/tests/npc_handins.cpp"
|
||||||
|
#include "cli/tests/npc_handins_multiquest.cpp"
|
||||||
|
#include "cli/tests/zone_state.cpp"
|
||||||
|
|||||||
+5
-4
@@ -1,6 +1,7 @@
|
|||||||
#ifndef EQEMU_ZONE_CLI_H
|
#ifndef EQEMU_ZONE_CLI_H
|
||||||
#define EQEMU_ZONE_CLI_H
|
#define EQEMU_ZONE_CLI_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include "../common/cli/argh.h"
|
#include "../common/cli/argh.h"
|
||||||
|
|
||||||
class ZoneCLI {
|
class ZoneCLI {
|
||||||
@@ -11,10 +12,10 @@ public:
|
|||||||
static bool RanConsoleCommand(int argc, char **argv);
|
static bool RanConsoleCommand(int argc, char **argv);
|
||||||
static bool RanSidecarCommand(int argc, char **argv);
|
static bool RanSidecarCommand(int argc, char **argv);
|
||||||
static bool RanTestCommand(int argc, char **argv);
|
static bool RanTestCommand(int argc, char **argv);
|
||||||
static void DataBuckets(int argc, char **argv, argh::parser &cmd, std::string &description);
|
static void TestDataBuckets(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||||
static void NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &description);
|
static void TestNpcHandins(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||||
static void NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std::string &description);
|
static void TestNpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||||
|
static void TestZoneState(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif //EQEMU_ZONE_CLI_H
|
#endif //EQEMU_ZONE_CLI_H
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user