Compare commits

...

29 Commits

Author SHA1 Message Date
Chris Miles aa6421afdf [Release] 22.24.0 (#3534)
* [Release] 22.24.0

* Update CHANGELOG.md
2023-08-05 13:58:51 -05:00
Chris Miles 6a7eaae122 [Fix] Bugs table should not target content database (#3535) 2023-08-05 13:53:54 -05:00
Chris Miles 714b474d2c [Character] Record character stats to character_stats_record table (#3522)
* [Character] Record character stats to

* Record stats on disconnect as well

* Record later in connect process

* Move enter zone code path so we're after bonuses

* Ok this spot for real

* Adjust recording

* Update client_packet.cpp

* Timestamps

* Update database_update_manifest.cpp

* Update client_packet.cpp

* Fix stat bonuses
2023-08-05 12:55:59 -05:00
Mitch Freeman e24d82f0fe [Bug Fix] Bug fix for raid mark NPC across zones (#3525)
* Fix for GENERIC_9_STRINGS

* Add Bot Heal Message Display

Creates a new rule to display Bot heal messages to the Bot Owner

* 2021-03-25 11L04pm

Spell and Heal Rule added to allow for Bot spell and heal damage to be sent to the Bot Owner's Group.  Also added a check to remove duplicate message for #damage on self.

* Update .gitignore

* BOT work

Added BOT logging damage/heals to owner
Added BOT message to owner for harmony fails
Made var Critical global to remove duplicate crit messages
Added a NULL check to Mob:GetCleanname()

* Bot Group Work

Fixed botid=charid spawn on zone issue
Added a group_list update on zone to refresh from database to fix a dangling pointer to a Bot object that was camped but was previously in a group within the zone being entered.
Modified Bot::ProcessBotGroupInvite to use the client of the bot when doing the Bot initialization so that a leader can invite another owner's Bot

* Jan 4

Basic structure in place for Raid::AddBot though not working

* Basement Jan 5

* End of day Jan 5
Working Raid Invite to a Bot.

* Update to Client::QueuePacket to not attempt to send a packet to a BoT.  Not clean, but a broad solution.

* Updated Raid::VerifyRaid

* Some Bot Raid working

* Before VS Crash

* Use Case 1, 2, 3,4,7 working.
Need to fix 5, 6, 8

* Work on usecase 5

* A few more use cases working

* New work on Raid invite with a invitor having a group

* Bot Raid inviting working for all use cases

* A few changes

* end of day jan 10

* Jan 11

* end of day Jan 11

* Bot Invite/Accept cleanup

* Start of moving raid bot functions to their own methods

* More bot raid changes

* More raid spell work

* end of day Jan 16

* spawn work

* Spawn on login working

* End of Day Jan 18

* Raid leader and mana/hp updates fixed

* Spell Tracking

* Issue with Bot Death in raid when casted upon.  1741 raid.cpp

* Bot Death fixed and few other crashes

* Working on botgroup removal

* Bot Disbanding Work 90%

* Looks like BOTs are working

* Fixed a bot crash

* bug tracing on entity list mismatch

* safe_delete resoves problem.  No to track down leak

* seems to be working

* Memory corruption found - sending packets to BoTs using Client class

* added Raid::IsRaidMemberBot()

* Update p_raid_instance

* g3

* Final - Bot Raid Working

* Fixed IsRaidMemberBot to remove memory leak
Fixed altcombat crash though RaidMainAssist (428) needs fixing

* add RaidMember.IsBot

* Repaired IsBot function to be more preformant.  Now works on standard performance machine

* Fixed Bard AE Target Spells
Removed assert for buffs

* updated based on Feb 2022 master updates

* Added bot_db_updates and version increment

* Cleanup of bot raid work and inclusion of bot_raid in cmake

* Fix repop crash

* Bot databse change to not use view

* Revert "Merge branch 'master' of https://github.com/neckkola/Server"

This reverts commit 1826830637, reversing
changes made to 7c1a139991.

* Updated syntax for 9230

Updated this syntax as to run on maria 10.1

* Revert "Updated syntax for 9230"

This reverts commit ffdd46c8b2.

* Fix for cross zone mark npc when entity ids are reused.

* Fixed and tested

added db change
and tested across zones

* Transition to direct attributes instead of encode

Update the pattern to utilize direct raid object attributes for entity id, zone id and instance id instead of an encoded single field.
2023-08-05 12:23:33 -05:00
Chris Miles 33a375677e [Fix] Fix issue with mob scanning when trying to use EVENT_SPAWN (#3529) 2023-08-04 13:05:33 -05:00
Chris Miles 8fce86c396 [Quest API] Adjust GetCloseMobList calls internally (#3530)
* [Quest API] Adjust GetCloseMobList calls internally

* Tweaks
2023-08-04 13:05:22 -05:00
Alex King 68b40f0239 [Cleanup] Cleanup #view zone_loot Command (#3523)
* [Cleanup] Cleanup #view zone_loot Command

# Notes
- An item ID was being required, we don't want to do that.
- Cleanup messages to only show item name/item ID

* Update zone_loot.cpp
2023-08-02 19:58:25 -04:00
Alex King 2dc2bac456 [Cleanup] Remove Strings::Commify from all identifier values (#3528)
# Notes
- Removes commification from unique identifiers.
2023-08-01 22:59:47 -05:00
Alex King 00a8a0cf88 [Bug Fix] Fix +/- 0.1 XYZ Door Manipulation (#3527)
# Notes
- We were checking if this was a number, not a float.
2023-08-01 22:16:29 -05:00
Alex King 5a466da96c [Feature] Add Support for Drakkin Heritage Illusions (#3521)
# Notes
- Fixes an issue where Drakkin illusions that used different Drakkin Heritages were not properly applying the Drakkin Heritage due to not sending the Face Appearance.
2023-08-01 14:28:13 -05:00
Akkadius de4f5ae491 [Release] 22.23.0 2023-07-31 20:17:17 -05:00
Alex King fb20d92166 [Bug Fix] Fix Appearance Issues (#3520)
* [Bug Fix] Fix Appearance Issues

# Notes
- Changing race, gender, or texture of a Mob could result in it changing sizes due to use not sending the size as part of the appearance packet.
- Also converts the parameterized method to a struct parameter so that we can optionally send things without back-filling multiple arguments.

* Gender cleanup.

* Fix.

* Formatting.
2023-07-31 20:15:13 -05:00
Paul Coene 6cff433d23 [Scaling/Bug Fix] Scaling where min and max damage was bugged (#3514)
* [Scaling/Bug Fix] Scaling where min and max damage were both 0 tossed out min_dmg

* Clamp values so independant calls dont leave us in odd state
2023-07-31 20:00:48 -05:00
Alex King 2da7ddad57 [Bug Fix] Fix NPC Cast Events not parsing properly. (#3518)
* [Bug Fix] Fix NPC Cast Events not parsing properly.

# Notes
- We were not using separated values.

* Update lua_parser_events.cpp
2023-07-31 19:59:56 -05:00
Chris Miles 55161e18c8 [Databuckets] Improvements to distributed cache, reload commands (#3519)
* [Databuckets] Improvements to distributed cache, reload commands

* Add to reload_types
2023-07-31 19:58:57 -05:00
Alex King 063d4fbd1a [Bug Fix] Fix #gm top level alias for #set gm (#3517)
# Notes
- Typo lead to `#gm on` and `#gm off` not functioning.
2023-07-30 13:20:17 -04:00
Akkadius c25cb0cc23 [Release] 22.22.1 2023-07-30 01:36:49 -05:00
Chris Miles ddac326239 [Doors] Add door blacklist (#3516)
* [Doors] Add door blacklist

* Renaming to simplify
2023-07-30 01:35:44 -05:00
Akkadius 14fe396510 [Database] Hotfix: Add command_subsettings to server tables 2023-07-28 22:22:43 -05:00
Chris Miles c968a0acdc [Release] 22.22.0 (#3513)
* [Release] 22.22.0

* Version other areas
2023-07-28 11:35:40 -05:00
Alex King 8c4cd34e01 [Quest API] Add GetMobTypeIdentifier() to Perl/Lua (#3512)
# Perl
- Add `$mob->GetMobTypeIdentifier()`.

# Lua
- Add `mob:GetMobTypeIdentifier()`.

# Notes
- Gets unique identifier independent of mob type.
2023-07-27 23:16:41 -05:00
Akkadius 0dbcf83a11 [Database] Fix console output in database:dump --dump-output-to-console 2023-07-25 11:45:26 -05:00
Alex King a75648f73f [Data Buckets] Distributed Databucket Caching (#3500)
* [Data Buckets] Zone-Based Data Bucket Caching

# Notes
- Adds a data bucket cache so we're not needlessly hitting the database every time we need to read a data bucket value.

* Cleanup and unify GetData access patterns

* Cache work

* Push

* Add to cache when we fetch and do a db hit

* Handle bucket misses in cache

* Formatting

* Logging

* [Data Buckets] Zone-Based Data Bucket Caching

- Adds a data bucket cache so we're not needlessly hitting the database every time we need to read a data bucket value.

* Cleanup and unify GetData access patterns

* Cache work

* Push

* Add to cache when we fetch and do a db hit

* Handle bucket misses in cache

* Formatting

* Remove redundant fetches from cache since GetData does the same thing

* Push progress

* Distributed cache work

* Logging

* Fix issue with scoping where same named keys could return overlapping results

* Misses cache tweak, logging, comments

* Add bot, client, and NPC bucket methods to Lua.

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2023-07-24 12:22:50 -05:00
Alex King 6c2886a71d [Cleanup] Fix casing in corpse money and decay time. (#3511)
# Notes
- These were uppercase and should be lowercase.
2023-07-23 16:19:07 -04:00
Alex King 1d96ddb60d [Bug Fix] Escape search string in #find item (#3510) 2023-07-22 18:20:00 -04:00
Vayle c30074be66 [Crash Fix] Guard against Spells:MaxTotalSlotsPET being set above client allowed maximum. (#3507)
* Guard against MaxTotalSlotsPET being set too high

This prevents a crash from MaxTotalSlotsPET being set too high.

* Tweak
2023-07-22 10:20:22 -04:00
Chris Miles b5652e6010 [Saylink] Fix cases where saylinks were not being cached (#3508)
* [Saylink] Fix cases where saylinks were not being cached

* Update say_link.cpp
2023-07-19 23:57:04 -05:00
Akkadius 202d2ed496 [Release] 22.21.2 2023-07-19 23:50:47 -05:00
Chris Miles 81cee49ea1 [Databuckets] Fix rarer same bucket name scoping overlap issue (#3509) 2023-07-19 23:49:11 -05:00
93 changed files with 3636 additions and 989 deletions
+88
View File
@@ -1,3 +1,91 @@
## [22.24.0] - 08/05/2023
### Character
* Record character stats to `character_stats_record` table ([#3522](https://github.com/EQEmu/Server/pull/3522)) @Akkadius 2023-08-05
### Code
* Cleanup #view zone_loot Command ([#3523](https://github.com/EQEmu/Server/pull/3523)) @Kinglykrab 2023-08-02
* Remove Strings::Commify from all identifier values ([#3528](https://github.com/EQEmu/Server/pull/3528)) @Kinglykrab 2023-08-02
### Feature
* Add Support for Drakkin Heritage Illusions ([#3521](https://github.com/EQEmu/Server/pull/3521)) @Kinglykrab 2023-08-01
### Fixes
* Bug fix for raid mark NPC across zones ([#3525](https://github.com/EQEmu/Server/pull/3525)) @neckkola 2023-08-05
* Bugs table should not target content database ([#3535](https://github.com/EQEmu/Server/pull/3535)) @Akkadius 2023-08-05
* Fix +/- 0.1 XYZ Door Manipulation ([#3527](https://github.com/EQEmu/Server/pull/3527)) @Kinglykrab 2023-08-02
* Fix issue with mob scanning when trying to use EVENT_SPAWN ([#3529](https://github.com/EQEmu/Server/pull/3529)) @Akkadius 2023-08-04
### Quest API
* Adjust GetCloseMobList calls internally ([#3530](https://github.com/EQEmu/Server/pull/3530)) @Akkadius 2023-08-04
## [22.23.0] - 07/31/2023
### Databuckets
* Improvements to distributed cache, reload commands ([#3519](https://github.com/EQEmu/Server/pull/3519)) @Akkadius 2023-08-01
### Fixes
* Fix #gm top level alias for #set gm ([#3517](https://github.com/EQEmu/Server/pull/3517)) @Kinglykrab 2023-07-30
* Fix Appearance Issues ([#3520](https://github.com/EQEmu/Server/pull/3520)) @Kinglykrab 2023-08-01
* Fix NPC Cast Events not parsing properly. ([#3518](https://github.com/EQEmu/Server/pull/3518)) @Kinglykrab 2023-08-01
### Scaling/Bug Fix
* Scaling where min and max damage was bugged ([#3514](https://github.com/EQEmu/Server/pull/3514)) @noudess 2023-08-01
## [22.22.1] - 07/30/2023
### Database
* Hotfix: Add command_subsettings to server tables @Akkadius 2023-07-29
### Doors
* Add door blacklist ([#3516](https://github.com/EQEmu/Server/pull/3516)) @Akkadius 2023-07-30
## [22.22.0] - 07/27/2023
### Code
* Fix casing in corpse money and decay time. ([#3511](https://github.com/EQEmu/Server/pull/3511)) @Kinglykrab 2023-07-23
### Crash Fix
* Guard against Spells:MaxTotalSlotsPET being set above client allowed maximum. ([#3507](https://github.com/EQEmu/Server/pull/3507)) @Valorith 2023-07-22
### Data Buckets
* Distributed Databucket Caching ([#3500](https://github.com/EQEmu/Server/pull/3500)) @Kinglykrab 2023-07-24
### Database
* Fix console output in database:dump --dump-output-to-console @Akkadius 2023-07-25
### Fixes
* Escape search string in #find item ([#3510](https://github.com/EQEmu/Server/pull/3510)) @Kinglykrab 2023-07-22
### Quest API
* Add GetMobTypeIdentifier() to Perl/Lua ([#3512](https://github.com/EQEmu/Server/pull/3512)) @Kinglykrab 2023-07-28
### Saylink
* Fix cases where saylinks were not being cached ([#3508](https://github.com/EQEmu/Server/pull/3508)) @Akkadius 2023-07-20
## [22.21.2] - 07/19/2023
### Databuckets
* Fix rarer same bucket name scoping overlap issue ([#3509](https://github.com/EQEmu/Server/pull/3509)) @Akkadius 2023-07-20
## [22.21.1] - 07/18/2023
### Bug
+3 -1
View File
@@ -324,7 +324,9 @@ void DatabaseDumpService::DatabaseDump()
}
}
LogSys.LoadLogSettingsDefaults();
if (!IsDumpOutputToConsole()) {
LogSys.LoadLogSettingsDefaults();
}
if (!pipe_file.empty()) {
std::string file = fmt::format("{}.sql", GetDumpFileNameWithPath());
@@ -4825,6 +4825,111 @@ UPDATE data_buckets SET character_id = SUBSTRING_INDEX(SUBSTRING_INDEX( `key`, '
UPDATE data_buckets SET npc_id = SUBSTRING_INDEX(SUBSTRING_INDEX( `key`, '-', 2 ), '-', -1), `key` = SUBSTR(SUBSTRING_INDEX(`key`, SUBSTRING_INDEX( `key`, '-', 2 ), -1), 2) WHERE `key` LIKE 'npc-%';
UPDATE data_buckets SET bot_id = SUBSTRING_INDEX(SUBSTRING_INDEX( `key`, '-', 2 ), '-', -1), `key` = SUBSTR(SUBSTRING_INDEX(`key`, SUBSTRING_INDEX( `key`, '-', 2 ), -1), 2) WHERE `key` LIKE 'bot-%';
)"
},
ManifestEntry{
.version = 9234,
.description = "2023_07_27_update_raid_details.sql",
.check = "SHOW COLUMNS FROM `raid_details` LIKE 'marked_npc_1_entity_id';",
.condition = "empty",
.match = "",
.sql = R"(ALTER TABLE `raid_details`
CHANGE COLUMN `marked_npc_1` `marked_npc_1_entity_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `motd`,
ADD COLUMN `marked_npc_1_zone_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `marked_npc_1_entity_id`,
ADD COLUMN `marked_npc_1_instance_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `marked_npc_1_zone_id`,
CHANGE COLUMN `marked_npc_2` `marked_npc_2_entity_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `marked_npc_1_instance_id`,
ADD COLUMN `marked_npc_2_zone_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `marked_npc_2_entity_id`,
ADD COLUMN `marked_npc_2_instance_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `marked_npc_2_zone_id`,
CHANGE COLUMN `marked_npc_3` `marked_npc_3_entity_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `marked_npc_2_instance_id`,
ADD COLUMN `marked_npc_3_zone_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `marked_npc_3_entity_id`,
ADD COLUMN `marked_npc_3_instance_id` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `marked_npc_3_zone_id`;
)"
},
ManifestEntry{
.version = 9235,
.description = "2023_07_31_character_stats_record.sql",
.check = "SHOW TABLES LIKE 'character_stats_record'",
.condition = "empty",
.match = "",
.sql = R"(
CREATE TABLE `character_stats_record` (
`character_id` int NULL,
`name` varchar(100) NULL,
`status` int NULL DEFAULT 0,
`level` int NULL DEFAULT 0,
`class` int NULL DEFAULT 0,
`race` int NULL DEFAULT 0,
`aa_points` int NULL DEFAULT 0,
`hp` bigint NULL DEFAULT 0,
`mana` bigint NULL DEFAULT 0,
`endurance` bigint NULL DEFAULT 0,
`ac` int NULL DEFAULT 0,
`strength` int NULL DEFAULT 0,
`stamina` int NULL DEFAULT 0,
`dexterity` int NULL DEFAULT 0,
`agility` int NULL DEFAULT 0,
`intelligence` int NULL DEFAULT 0,
`wisdom` int NULL DEFAULT 0,
`charisma` int NULL DEFAULT 0,
`magic_resist` int NULL DEFAULT 0,
`fire_resist` int NULL DEFAULT 0,
`cold_resist` int NULL DEFAULT 0,
`poison_resist` int NULL DEFAULT 0,
`disease_resist` int NULL DEFAULT 0,
`corruption_resist` int NULL DEFAULT 0,
`heroic_strength` int NULL DEFAULT 0,
`heroic_stamina` int NULL DEFAULT 0,
`heroic_dexterity` int NULL DEFAULT 0,
`heroic_agility` int NULL DEFAULT 0,
`heroic_intelligence` int NULL DEFAULT 0,
`heroic_wisdom` int NULL DEFAULT 0,
`heroic_charisma` int NULL DEFAULT 0,
`heroic_magic_resist` int NULL DEFAULT 0,
`heroic_fire_resist` int NULL DEFAULT 0,
`heroic_cold_resist` int NULL DEFAULT 0,
`heroic_poison_resist` int NULL DEFAULT 0,
`heroic_disease_resist` int NULL DEFAULT 0,
`heroic_corruption_resist` int NULL DEFAULT 0,
`haste` int NULL DEFAULT 0,
`accuracy` int NULL DEFAULT 0,
`attack` int NULL DEFAULT 0,
`avoidance` int NULL DEFAULT 0,
`clairvoyance` int NULL DEFAULT 0,
`combat_effects` int NULL DEFAULT 0,
`damage_shield_mitigation` int NULL DEFAULT 0,
`damage_shield` int NULL DEFAULT 0,
`dot_shielding` int NULL DEFAULT 0,
`hp_regen` int NULL DEFAULT 0,
`mana_regen` int NULL DEFAULT 0,
`endurance_regen` int NULL DEFAULT 0,
`shielding` int NULL DEFAULT 0,
`spell_damage` int NULL DEFAULT 0,
`spell_shielding` int NULL DEFAULT 0,
`strikethrough` int NULL DEFAULT 0,
`stun_resist` int NULL DEFAULT 0,
`backstab` int NULL DEFAULT 0,
`wind` int NULL DEFAULT 0,
`brass` int NULL DEFAULT 0,
`string` int NULL DEFAULT 0,
`percussion` int NULL DEFAULT 0,
`singing` int NULL DEFAULT 0,
`baking` int NULL DEFAULT 0,
`alchemy` int NULL DEFAULT 0,
`tailoring` int NULL DEFAULT 0,
`blacksmithing` int NULL DEFAULT 0,
`fletching` int NULL DEFAULT 0,
`brewing` int NULL DEFAULT 0,
`jewelry` int NULL DEFAULT 0,
`pottery` int NULL DEFAULT 0,
`research` int NULL DEFAULT 0,
`alcohol` int NULL DEFAULT 0,
`fishing` int NULL DEFAULT 0,
`tinkering` int NULL DEFAULT 0,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`character_id`)
);
)"
},
+2
View File
@@ -66,6 +66,7 @@ namespace DatabaseSchema {
{"character_potionbelt", "id"},
{"character_skills", "id"},
{"character_spells", "id"},
{"character_stats_record", "character_id"},
{"character_task_timers", "character_id"},
{"character_tasks", "charid"},
{"character_tribute", "character_id"},
@@ -258,6 +259,7 @@ namespace DatabaseSchema {
"chatchannels",
"chatchannel_reserved_names",
"command_settings",
"command_subsettings",
"content_flags",
"db_str",
"eqtime",
+2
View File
@@ -137,6 +137,7 @@ namespace Logs {
Bugs,
QuestErrors,
PlayerEvents,
DataBuckets,
MaxCategoryID /* Don't Remove this */
};
@@ -233,6 +234,7 @@ namespace Logs {
"Bugs",
"QuestErrors",
"PlayerEvents",
"DataBuckets",
};
}
+10
View File
@@ -794,6 +794,16 @@
OutF(LogSys, Logs::Detail, Logs::PlayerEvents, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogDataBuckets(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::General, Logs::DataBuckets))\
OutF(LogSys, Logs::General, Logs::DataBuckets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogDataBucketsDetail(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::Detail, Logs::DataBuckets))\
OutF(LogSys, Logs::Detail, Logs::DataBuckets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define Log(debug_level, log_category, message, ...) do {\
if (LogSys.IsLogEnabled(debug_level, log_category))\
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
+4 -2
View File
@@ -1594,11 +1594,13 @@ float GetRaceGenderDefaultHeight(int race, int gender)
const auto size = sizeof(male_height) / sizeof(male_height[0]);
if (race >= size)
if (race >= size) {
return 6.0f;
}
if (gender == 1)
if (gender == FEMALE) {
return female_height[race];
}
return male_height[race];
}
File diff suppressed because it is too large Load Diff
@@ -15,7 +15,7 @@
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
#include <cereal/cereal.hpp>
class BaseDataBucketsRepository {
public:
@@ -27,6 +27,21 @@ public:
int64_t character_id;
int64_t npc_id;
int64_t bot_id;
// cereal
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(id),
CEREAL_NVP(key_),
CEREAL_NVP(value),
CEREAL_NVP(expires),
CEREAL_NVP(character_id),
CEREAL_NVP(npc_id),
CEREAL_NVP(bot_id)
);
}
};
static std::string PrimaryKey()
@@ -24,9 +24,15 @@ public:
int32_t loottype;
int8_t locked;
std::string motd;
uint16_t marked_npc_1;
uint16_t marked_npc_2;
uint16_t marked_npc_3;
uint32_t marked_npc_1_entity_id;
uint32_t marked_npc_1_zone_id;
uint32_t marked_npc_1_instance_id;
uint32_t marked_npc_2_entity_id;
uint32_t marked_npc_2_zone_id;
uint32_t marked_npc_2_instance_id;
uint32_t marked_npc_3_entity_id;
uint32_t marked_npc_3_zone_id;
uint32_t marked_npc_3_instance_id;
};
static std::string PrimaryKey()
@@ -41,9 +47,15 @@ public:
"loottype",
"locked",
"motd",
"marked_npc_1",
"marked_npc_2",
"marked_npc_3",
"marked_npc_1_entity_id",
"marked_npc_1_zone_id",
"marked_npc_1_instance_id",
"marked_npc_2_entity_id",
"marked_npc_2_zone_id",
"marked_npc_2_instance_id",
"marked_npc_3_entity_id",
"marked_npc_3_zone_id",
"marked_npc_3_instance_id",
};
}
@@ -54,9 +66,15 @@ public:
"loottype",
"locked",
"motd",
"marked_npc_1",
"marked_npc_2",
"marked_npc_3",
"marked_npc_1_entity_id",
"marked_npc_1_zone_id",
"marked_npc_1_instance_id",
"marked_npc_2_entity_id",
"marked_npc_2_zone_id",
"marked_npc_2_instance_id",
"marked_npc_3_entity_id",
"marked_npc_3_zone_id",
"marked_npc_3_instance_id",
};
}
@@ -97,13 +115,19 @@ public:
{
RaidDetails e{};
e.raidid = 0;
e.loottype = 0;
e.locked = 0;
e.motd = "";
e.marked_npc_1 = 0;
e.marked_npc_2 = 0;
e.marked_npc_3 = 0;
e.raidid = 0;
e.loottype = 0;
e.locked = 0;
e.motd = "";
e.marked_npc_1_entity_id = 0;
e.marked_npc_1_zone_id = 0;
e.marked_npc_1_instance_id = 0;
e.marked_npc_2_entity_id = 0;
e.marked_npc_2_zone_id = 0;
e.marked_npc_2_instance_id = 0;
e.marked_npc_3_entity_id = 0;
e.marked_npc_3_zone_id = 0;
e.marked_npc_3_instance_id = 0;
return e;
}
@@ -140,13 +164,19 @@ public:
if (results.RowCount() == 1) {
RaidDetails e{};
e.raidid = static_cast<int32_t>(atoi(row[0]));
e.loottype = static_cast<int32_t>(atoi(row[1]));
e.locked = static_cast<int8_t>(atoi(row[2]));
e.motd = row[3] ? row[3] : "";
e.marked_npc_1 = static_cast<uint16_t>(strtoul(row[4], nullptr, 10));
e.marked_npc_2 = static_cast<uint16_t>(strtoul(row[5], nullptr, 10));
e.marked_npc_3 = static_cast<uint16_t>(strtoul(row[6], nullptr, 10));
e.raidid = static_cast<int32_t>(atoi(row[0]));
e.loottype = static_cast<int32_t>(atoi(row[1]));
e.locked = static_cast<int8_t>(atoi(row[2]));
e.motd = row[3] ? row[3] : "";
e.marked_npc_1_entity_id = static_cast<uint32_t>(strtoul(row[4], nullptr, 10));
e.marked_npc_1_zone_id = static_cast<uint32_t>(strtoul(row[5], nullptr, 10));
e.marked_npc_1_instance_id = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
e.marked_npc_2_entity_id = static_cast<uint32_t>(strtoul(row[7], nullptr, 10));
e.marked_npc_2_zone_id = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
e.marked_npc_2_instance_id = static_cast<uint32_t>(strtoul(row[9], nullptr, 10));
e.marked_npc_3_entity_id = static_cast<uint32_t>(strtoul(row[10], nullptr, 10));
e.marked_npc_3_zone_id = static_cast<uint32_t>(strtoul(row[11], nullptr, 10));
e.marked_npc_3_instance_id = static_cast<uint32_t>(strtoul(row[12], nullptr, 10));
return e;
}
@@ -184,9 +214,15 @@ public:
v.push_back(columns[1] + " = " + std::to_string(e.loottype));
v.push_back(columns[2] + " = " + std::to_string(e.locked));
v.push_back(columns[3] + " = '" + Strings::Escape(e.motd) + "'");
v.push_back(columns[4] + " = " + std::to_string(e.marked_npc_1));
v.push_back(columns[5] + " = " + std::to_string(e.marked_npc_2));
v.push_back(columns[6] + " = " + std::to_string(e.marked_npc_3));
v.push_back(columns[4] + " = " + std::to_string(e.marked_npc_1_entity_id));
v.push_back(columns[5] + " = " + std::to_string(e.marked_npc_1_zone_id));
v.push_back(columns[6] + " = " + std::to_string(e.marked_npc_1_instance_id));
v.push_back(columns[7] + " = " + std::to_string(e.marked_npc_2_entity_id));
v.push_back(columns[8] + " = " + std::to_string(e.marked_npc_2_zone_id));
v.push_back(columns[9] + " = " + std::to_string(e.marked_npc_2_instance_id));
v.push_back(columns[10] + " = " + std::to_string(e.marked_npc_3_entity_id));
v.push_back(columns[11] + " = " + std::to_string(e.marked_npc_3_zone_id));
v.push_back(columns[12] + " = " + std::to_string(e.marked_npc_3_instance_id));
auto results = db.QueryDatabase(
fmt::format(
@@ -212,9 +248,15 @@ public:
v.push_back(std::to_string(e.loottype));
v.push_back(std::to_string(e.locked));
v.push_back("'" + Strings::Escape(e.motd) + "'");
v.push_back(std::to_string(e.marked_npc_1));
v.push_back(std::to_string(e.marked_npc_2));
v.push_back(std::to_string(e.marked_npc_3));
v.push_back(std::to_string(e.marked_npc_1_entity_id));
v.push_back(std::to_string(e.marked_npc_1_zone_id));
v.push_back(std::to_string(e.marked_npc_1_instance_id));
v.push_back(std::to_string(e.marked_npc_2_entity_id));
v.push_back(std::to_string(e.marked_npc_2_zone_id));
v.push_back(std::to_string(e.marked_npc_2_instance_id));
v.push_back(std::to_string(e.marked_npc_3_entity_id));
v.push_back(std::to_string(e.marked_npc_3_zone_id));
v.push_back(std::to_string(e.marked_npc_3_instance_id));
auto results = db.QueryDatabase(
fmt::format(
@@ -248,9 +290,15 @@ public:
v.push_back(std::to_string(e.loottype));
v.push_back(std::to_string(e.locked));
v.push_back("'" + Strings::Escape(e.motd) + "'");
v.push_back(std::to_string(e.marked_npc_1));
v.push_back(std::to_string(e.marked_npc_2));
v.push_back(std::to_string(e.marked_npc_3));
v.push_back(std::to_string(e.marked_npc_1_entity_id));
v.push_back(std::to_string(e.marked_npc_1_zone_id));
v.push_back(std::to_string(e.marked_npc_1_instance_id));
v.push_back(std::to_string(e.marked_npc_2_entity_id));
v.push_back(std::to_string(e.marked_npc_2_zone_id));
v.push_back(std::to_string(e.marked_npc_2_instance_id));
v.push_back(std::to_string(e.marked_npc_3_entity_id));
v.push_back(std::to_string(e.marked_npc_3_zone_id));
v.push_back(std::to_string(e.marked_npc_3_instance_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -284,13 +332,19 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
RaidDetails e{};
e.raidid = static_cast<int32_t>(atoi(row[0]));
e.loottype = static_cast<int32_t>(atoi(row[1]));
e.locked = static_cast<int8_t>(atoi(row[2]));
e.motd = row[3] ? row[3] : "";
e.marked_npc_1 = static_cast<uint16_t>(strtoul(row[4], nullptr, 10));
e.marked_npc_2 = static_cast<uint16_t>(strtoul(row[5], nullptr, 10));
e.marked_npc_3 = static_cast<uint16_t>(strtoul(row[6], nullptr, 10));
e.raidid = static_cast<int32_t>(atoi(row[0]));
e.loottype = static_cast<int32_t>(atoi(row[1]));
e.locked = static_cast<int8_t>(atoi(row[2]));
e.motd = row[3] ? row[3] : "";
e.marked_npc_1_entity_id = static_cast<uint32_t>(strtoul(row[4], nullptr, 10));
e.marked_npc_1_zone_id = static_cast<uint32_t>(strtoul(row[5], nullptr, 10));
e.marked_npc_1_instance_id = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
e.marked_npc_2_entity_id = static_cast<uint32_t>(strtoul(row[7], nullptr, 10));
e.marked_npc_2_zone_id = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
e.marked_npc_2_instance_id = static_cast<uint32_t>(strtoul(row[9], nullptr, 10));
e.marked_npc_3_entity_id = static_cast<uint32_t>(strtoul(row[10], nullptr, 10));
e.marked_npc_3_zone_id = static_cast<uint32_t>(strtoul(row[11], nullptr, 10));
e.marked_npc_3_instance_id = static_cast<uint32_t>(strtoul(row[12], nullptr, 10));
all_entries.push_back(e);
}
@@ -315,13 +369,19 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
RaidDetails e{};
e.raidid = static_cast<int32_t>(atoi(row[0]));
e.loottype = static_cast<int32_t>(atoi(row[1]));
e.locked = static_cast<int8_t>(atoi(row[2]));
e.motd = row[3] ? row[3] : "";
e.marked_npc_1 = static_cast<uint16_t>(strtoul(row[4], nullptr, 10));
e.marked_npc_2 = static_cast<uint16_t>(strtoul(row[5], nullptr, 10));
e.marked_npc_3 = static_cast<uint16_t>(strtoul(row[6], nullptr, 10));
e.raidid = static_cast<int32_t>(atoi(row[0]));
e.loottype = static_cast<int32_t>(atoi(row[1]));
e.locked = static_cast<int8_t>(atoi(row[2]));
e.motd = row[3] ? row[3] : "";
e.marked_npc_1_entity_id = static_cast<uint32_t>(strtoul(row[4], nullptr, 10));
e.marked_npc_1_zone_id = static_cast<uint32_t>(strtoul(row[5], nullptr, 10));
e.marked_npc_1_instance_id = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
e.marked_npc_2_entity_id = static_cast<uint32_t>(strtoul(row[7], nullptr, 10));
e.marked_npc_2_zone_id = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
e.marked_npc_2_instance_id = static_cast<uint32_t>(strtoul(row[9], nullptr, 10));
e.marked_npc_3_entity_id = static_cast<uint32_t>(strtoul(row[10], nullptr, 10));
e.marked_npc_3_zone_id = static_cast<uint32_t>(strtoul(row[11], nullptr, 10));
e.marked_npc_3_instance_id = static_cast<uint32_t>(strtoul(row[12], nullptr, 10));
all_entries.push_back(e);
}
@@ -0,0 +1,50 @@
#ifndef EQEMU_CHARACTER_STATS_RECORD_REPOSITORY_H
#define EQEMU_CHARACTER_STATS_RECORD_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_character_stats_record_repository.h"
class CharacterStatsRecordRepository: public BaseCharacterStatsRecordRepository {
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
*
* CharacterStatsRecordRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* CharacterStatsRecordRepository::GetWhereNeverExpires()
* CharacterStatsRecordRepository::GetWhereXAndY()
* CharacterStatsRecordRepository::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
};
#endif //EQEMU_CHARACTER_STATS_RECORD_REPOSITORY_H
+1 -1
View File
@@ -53,7 +53,7 @@ public:
auto query = fmt::format(
"SELECT `id` FROM {} WHERE LOWER(`name`) LIKE '%%{}%%' ORDER BY id ASC",
TableName(),
search_string
Strings::Escape(search_string)
);
if (query_limit >= 1) {
+10 -6
View File
@@ -47,17 +47,21 @@ public:
static int UpdateRaidMarkedNPC(
Database& db,
int32_t raid_id,
uint8_t marked_npc_number,
uint8_t value
uint32_t marked_npc_entity_id,
uint32_t marked_npc_zone_id,
uint32_t marked_npc_instance_id,
uint32_t slot_number
) {
auto results = db.QueryDatabase(
fmt::format(
"UPDATE `{}` SET `marked_npc_{}` = '{}' WHERE raidid = '{}';",
"UPDATE `{0}` SET `marked_npc_{4}_entity_id` = '{1}',`marked_npc_{4}_zone_id` = '{2}',`marked_npc_{4}_instance_id` = '{3}' WHERE raidid = '{5}';",
TableName(),
marked_npc_number,
value,
marked_npc_entity_id,
marked_npc_zone_id,
marked_npc_instance_id,
slot_number,
raid_id
)
)
);
return results.Success() ? results.RowsAffected() : 0;
+2 -1
View File
@@ -376,7 +376,7 @@ std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message)
void EQ::SayLinkEngine::LoadCachedSaylinks()
{
auto saylinks = SaylinkRepository::GetWhere(database, "phrase not like '%#%'");
auto saylinks = SaylinkRepository::GetWhere(database, "phrase not REGEXP BINARY '[A-Z]' and phrase not REGEXP '[0-9]'");
LogSaylink("Loaded [{}] saylinks into cache", saylinks.size());
g_cached_saylinks = saylinks;
}
@@ -399,6 +399,7 @@ SaylinkRepository::Saylink EQ::SayLinkEngine::GetOrSaveSaylink(std::string sayli
// return if found from the database
if (!saylinks.empty()) {
g_cached_saylinks.emplace_back(saylinks[0]);
return saylinks[0];
}
+8
View File
@@ -249,6 +249,7 @@
#define ServerOP_ReloadZonePoints 0x4122
#define ServerOP_ReloadDzTemplates 0x4123
#define ServerOP_ReloadZoneData 0x4124
#define ServerOP_ReloadDataBucketsCache 0x4125
#define ServerOP_CZDialogueWindow 0x4500
#define ServerOP_CZLDoNUpdate 0x4501
@@ -286,6 +287,8 @@
// player events
#define ServerOP_PlayerEvent 0x5100
#define ServerOP_DataBucketCacheUpdate 0x5200
enum {
CZUpdateType_Character,
CZUpdateType_Group,
@@ -1820,6 +1823,11 @@ struct ServerSendPlayerEvent_Struct {
char cereal_data[0];
};
struct ServerDataBucketCacheUpdate_Struct {
uint32_t cereal_size;
char cereal_data[0];
};
struct ServerFlagUpdate_Struct {
uint32 account_id;
int16 admin;
+2 -2
View File
@@ -25,7 +25,7 @@
// Build variables
// these get injected during the build pipeline
#define CURRENT_VERSION "22.21.1-dev" // always append -dev to the current version for custom-builds
#define CURRENT_VERSION "22.24.0-dev" // always append -dev to the current version for custom-builds
#define LOGIN_VERSION "0.8.0"
#define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__
@@ -42,7 +42,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9233
#define CURRENT_BINARY_DATABASE_VERSION 9235
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9039
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "eqemu-server",
"version": "22.21.1",
"version": "22.24.0",
"repository": {
"type": "git",
"url": "https://github.com/EQEmu/Server.git"
@@ -112,6 +112,7 @@ if ($requested_table_to_generate ne "all") {
}
my @cereal_enabled_tables = (
"data_buckets",
"player_event_logs"
);
+1
View File
@@ -138,6 +138,7 @@ std::vector<Reload> reload_types = {
Reload{.command = "alternate_currencies", .opcode = ServerOP_ReloadAlternateCurrencies, .desc = "Alternate Currencies"},
Reload{.command = "blocked_spells", .opcode = ServerOP_ReloadBlockedSpells, .desc = "Blocked Spells"},
Reload{.command = "commands", .opcode = ServerOP_ReloadCommands, .desc = "Commands"},
Reload{.command = "data_buckets_cache", .opcode = ServerOP_ReloadDataBucketsCache, .desc = "Data Buckets Cache"},
Reload{.command = "doors", .opcode = ServerOP_ReloadDoors, .desc = "Doors"},
Reload{.command = "dztemplates", .opcode = ServerOP_ReloadDzTemplates, .desc = "Dynamic Zone Templates"},
Reload{.command = "ground_spawns", .opcode = ServerOP_ReloadGroundSpawns, .desc = "Ground Spawns"},
+7
View File
@@ -46,6 +46,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/repositories/player_event_logs_repository.h"
#include "../common/events/player_event_logs.h"
#include "../common/patches/patches.h"
#include "../zone/data_bucket.h"
extern ClientList client_list;
extern GroupLFPList LFPGroupList;
@@ -1342,6 +1343,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
case ServerOP_ReloadBlockedSpells:
case ServerOP_ReloadCommands:
case ServerOP_ReloadDoors:
case ServerOP_ReloadDataBucketsCache:
case ServerOP_ReloadGroundSpawns:
case ServerOP_ReloadLevelEXPMods:
case ServerOP_ReloadMerchants:
@@ -1468,6 +1470,11 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
DynamicZone::HandleZoneMessage(pack);
break;
}
case ServerOP_DataBucketCacheUpdate: {
zoneserver_list.SendPacket(pack);
break;
}
default: {
LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size);
DumpPacket(pack->pBuffer, pack->size);
+5
View File
@@ -6458,3 +6458,8 @@ int64 Mob::GetManaRegen() const
{
return mana_regen;
}
int64 Mob::GetEnduranceRegen() const
{
return 0; // not implemented
}
+1 -1
View File
@@ -6036,7 +6036,7 @@ float Mob::CheckHeroicBonusesDataBuckets(std::string bucket_name)
DataBucketKey k = GetScopedBucketKeys();
k.key = bucket_name;
if (IsOfClientBot()) {
bucket_value = DataBucket::CheckBucketKey(this, k);
bucket_value = DataBucket::GetData(k).value;
}
if (bucket_value.empty() || !Strings::IsNumber(bucket_value)) {
+44 -21
View File
@@ -268,26 +268,48 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
case SE_IllusionCopy:
case SE_Illusion: {
if (spell.base_value[x1] == -1) {
if (gender == 1)
gender = 0;
else if (gender == 0)
gender = 1;
SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF);
}
else if (spell.base_value[x1] == -2) // WTF IS THIS
if (gender == FEMALE) {
gender = MALE;
} else if (gender == MALE) {
gender = FEMALE;
}
SendIllusionPacket(
AppearanceStruct{
.gender_id = gender,
.race_id = GetRace(),
}
);
} else if (spell.base_value[x1] == -2) // WTF IS THIS
{
if (GetRace() == IKSAR || GetRace() == VAHSHIR || GetRace() <= GNOME) {
SendIllusionPacket(GetRace(), GetGender(), spell.limit_value[x1], spell.max_value[x1]);
SendIllusionPacket(
AppearanceStruct{
.gender_id = GetGender(),
.helmet_texture = static_cast<uint8>(spell.max_value[x1]),
.race_id = GetRace(),
.texture = static_cast<uint8>(spell.limit_value[x1]),
}
);
}
} else if (spell.max_value[x1] > 0) {
SendIllusionPacket(
AppearanceStruct{
.helmet_texture = static_cast<uint8>(spell.max_value[x1]),
.race_id = static_cast<uint16>(spell.base_value[x1]),
.texture = static_cast<uint8>(spell.limit_value[x1]),
}
);
} else {
SendIllusionPacket(
AppearanceStruct{
.helmet_texture = static_cast<uint8>(spell.max_value[x1]),
.race_id = static_cast<uint16>(spell.base_value[x1]),
.texture = static_cast<uint8>(spell.limit_value[x1]),
}
);
}
else if (spell.max_value[x1] > 0)
{
SendIllusionPacket(spell.base_value[x1], 0xFF, spell.limit_value[x1], spell.max_value[x1]);
}
else
{
SendIllusionPacket(spell.base_value[x1], 0xFF, 0xFF, 0xFF);
}
switch (spell.base_value[x1]) {
case OGRE:
SendAppearancePacket(AT_Size, 9);
@@ -427,6 +449,7 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
Bot::~Bot() {
AI_Stop();
LeaveHealRotationMemberPool();
DataBucket::DeleteCachedBuckets(DataBucketLoadType::Bot, GetBotID());
if (HasPet()) {
GetPet()->Depop();
@@ -8197,18 +8220,18 @@ bool Bot::CheckDataBucket(std::string bucket_name, const std::string& bucket_val
DataBucketKey k = GetScopedBucketKeys();
k.key = bucket_name;
auto player_value = DataBucket::CheckBucketKey(this, k);
if (player_value.empty() && GetBotOwner()) {
auto b = DataBucket::GetData(k);
if (b.value.empty() && GetBotOwner()) {
// fetch from owner
k = GetBotOwner()->GetScopedBucketKeys();
player_value = DataBucket::CheckBucketKey(GetBotOwner(), k);
if (player_value.empty()) {
b = DataBucket::GetData(k);
if (b.value.empty()) {
return false;
}
}
if (zone->CompareDataBucket(bucket_comparison, bucket_value, player_value)) {
if (zone->CompareDataBucket(bucket_comparison, bucket_value, b.value)) {
return true;
}
}
+22 -19
View File
@@ -63,6 +63,7 @@
#include "water_map.h"
#include "worldserver.h"
#include "dialogue_window.h"
#include "mob.h"
#include <fmt/format.h>
@@ -8819,26 +8820,28 @@ void helper_bot_appearance_form_final(Client *bot_owner, Bot *my_bot)
void helper_bot_appearance_form_update(Bot *my_bot)
{
if (!my_bot)
if (!my_bot) {
return;
}
my_bot->SendIllusionPacket(
my_bot->GetRace(),
my_bot->GetGender(),
0xFF, //my_bot->GetTexture(), // 0xFF - change back if issues arise
0xFF, //my_bot->GetHelmTexture(), // 0xFF - change back if issues arise
my_bot->GetHairColor(),
my_bot->GetBeardColor(),
my_bot->GetEyeColor1(),
my_bot->GetEyeColor2(),
my_bot->GetHairStyle(),
my_bot->GetLuclinFace(),
my_bot->GetBeard(),
0xFF, // aa_title (0xFF)
my_bot->GetDrakkinHeritage(),
my_bot->GetDrakkinTattoo(),
my_bot->GetDrakkinDetails(),
my_bot->GetSize()
AppearanceStruct{
.beard = my_bot->GetBeard(),
.beard_color = my_bot->GetBeardColor(),
.drakkin_details = my_bot->GetDrakkinDetails(),
.drakkin_heritage = my_bot->GetDrakkinHeritage(),
.drakkin_tattoo = my_bot->GetDrakkinTattoo(),
.eye_color_one = my_bot->GetEyeColor1(),
.eye_color_two = my_bot->GetEyeColor2(),
.face = my_bot->GetLuclinFace(),
.gender_id = my_bot->GetGender(),
.hair = my_bot->GetHairStyle(),
.hair_color = my_bot->GetHairColor(),
.helmet_texture = my_bot->GetHelmTexture(),
.race_id = my_bot->GetRace(),
.size = my_bot->GetSize(),
.texture = my_bot->GetTexture(),
}
);
}
@@ -10002,7 +10005,7 @@ void bot_command_pickpocket(Client *c, const Seperator *sep)
if (helper_command_disabled(c, RuleB(Bots, AllowPickpocketCommand), "pickpocket")) {
return;
}
if (helper_command_alias_fail(c, "bot_command_pickpocket", sep->arg[0], "pickpocket")) {
return;
}
@@ -10037,7 +10040,7 @@ void bot_command_pickpocket(Client *c, const Seperator *sep)
float mob_xy_distance = ((mob_distance.x * mob_distance.x) + (mob_distance.y * mob_distance.y));
float mob_z_distance = (mob_distance.z * mob_distance.z);
float z_offset_diff = target_mob->GetZOffset() - c->GetZOffset();
if (mob_z_distance >= (35-z_offset_diff) || mob_xy_distance > 250) {
c->Message(Chat::White, "You must be closer to an enemy to use this command");
return;
+15 -2
View File
@@ -381,6 +381,8 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
Client::~Client() {
mMovementManager->RemoveClient(this);
DataBucket::DeleteCachedBuckets(DataBucketLoadType::Client, CharacterID());
if (RuleB(Bots, Enabled)) {
Bot::ProcessBotOwnerRefDelete(this);
}
@@ -8881,7 +8883,8 @@ void Client::ShowDevToolsMenu()
menu_reload_two += Saylink::Silent("#reload commands", "Commands");
menu_reload_two += " | " + Saylink::Silent("#reload content_flags", "Content Flags");
menu_reload_three += Saylink::Silent("#reload doors", "Doors");
menu_reload_three += Saylink::Silent("#reload data_buckets_cache", "Databuckets");
menu_reload_three += " | " + Saylink::Silent("#reload doors", "Doors");
menu_reload_three += " | " + Saylink::Silent("#reload ground_spawns", "Ground Spawns");
menu_reload_four += Saylink::Silent("#reload logs", "Level Based Experience Modifiers");
@@ -10835,6 +10838,16 @@ void Client::SendReloadCommandMessages() {
).c_str()
);
auto data_buckets_link = Saylink::Silent("#reload data_buckets_cache");
Message(
Chat::White,
fmt::format(
"Usage: {} - Reloads data buckets cache globally",
data_buckets_link
).c_str()
);
auto dztemplates_link = Saylink::Silent("#reload dztemplates");
Message(Chat::White, fmt::format("Usage: {} - Reloads Dynamic Zone Templates globally", dztemplates_link).c_str());
@@ -11737,7 +11750,7 @@ void Client::ShowSpells(Client* c, ShowSpellType show_spell_type)
"{}. {} ({})",
index,
GetSpellName(spell_id),
Strings::Commify(spell_id)
spell_id
).c_str()
);
}
+1
View File
@@ -233,6 +233,7 @@ public:
~Client();
void ReconnectUCS();
void RecordStats();
void SetDisplayMobInfoWindow(bool display_mob_info_window);
bool GetDisplayMobInfoWindow() const;
+94 -3
View File
@@ -62,6 +62,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/repositories/account_repository.h"
#include "../common/events/player_event_logs.h"
#include "../common/repositories/character_stats_record_repository.h"
extern QueryServ* QServ;
extern Zone* zone;
@@ -915,6 +916,8 @@ void Client::CompleteConnect()
heroforge_wearchange_timer.Start(250);
RecordStats();
// enforce some rules..
if (!CanEnterZone()) {
LogInfo("Kicking character [{}] from zone, not allowed here (missing requirements)", GetCleanName());
@@ -5224,12 +5227,12 @@ void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app)
uint32 decay_time = t->GetDecayTime();
if (decay_time) {
auto time_string = Strings::SecondsToTime(decay_time, true);
const std::string& time_string = Strings::SecondsToTime(decay_time, true);
Message(
Chat::NPCQuestSay,
fmt::format(
"This corpse will decay in {}.",
time_string
Strings::ToLower(time_string)
).c_str()
);
@@ -15953,7 +15956,7 @@ void Client::Handle_OP_XTargetRequest(const EQApplicationPacket *app)
if (t.type == Type) {
Raid* r = GetRaid();
if (r) {
auto mm = entity_list.GetNPCByID(r->marked_npcs[t.assist_slot]);
auto mm = entity_list.GetNPCByID(r->marked_npcs[t.assist_slot].entity_id);
if (mm) {
UpdateXTargetType(t.type, mm->CastToMob(), mm->CastToMob()->GetName());
}
@@ -16392,3 +16395,91 @@ void Client::Handle_OP_RaidClearNPCMarks(const EQApplicationPacket* app)
r->RaidClearNPCMarks(this);
}
}
void Client::RecordStats()
{
auto r = CharacterStatsRecordRepository::FindOne(
database,
CharacterID()
);
r.status = Admin();
r.name = GetCleanName();
r.aa_points = GetAAPoints() + GetSpentAA();
r.level = GetLevel();
r.class_ = GetBaseClass();
r.race = GetBaseRace();
r.hp = GetMaxHP() - GetSpellBonuses().HP;
r.mana = GetMaxMana() - GetSpellBonuses().Mana;
r.endurance = GetMaxEndurance() - GetSpellBonuses().Endurance;
r.ac = GetDisplayAC() - GetSpellBonuses().AC;
r.strength = GetSTR() - GetSpellBonuses().STR;
r.stamina = GetSTA() - GetSpellBonuses().STA;
r.dexterity = GetDEX() - GetSpellBonuses().DEX;
r.agility = GetAGI() - GetSpellBonuses().AGI;
r.intelligence = GetINT() - GetSpellBonuses().INT;
r.wisdom = GetWIS() - GetSpellBonuses().WIS;
r.charisma = GetCHA() - GetSpellBonuses().CHA;
r.magic_resist = GetMR() - GetSpellBonuses().MR;
r.fire_resist = GetFR() - GetSpellBonuses().FR;
r.cold_resist = GetCR() - GetSpellBonuses().CR;
r.poison_resist = GetPR() - GetSpellBonuses().PR;
r.disease_resist = GetDR() - GetSpellBonuses().DR;
r.corruption_resist = GetCorrup() - GetSpellBonuses().Corrup;
r.heroic_strength = GetHeroicSTR() - GetSpellBonuses().HeroicSTR;
r.heroic_stamina = GetHeroicSTA() - GetSpellBonuses().HeroicSTA;
r.heroic_dexterity = GetHeroicDEX() - GetSpellBonuses().HeroicDEX;
r.heroic_agility = GetHeroicAGI() - GetSpellBonuses().HeroicAGI;
r.heroic_intelligence = GetHeroicINT() - GetSpellBonuses().HeroicINT;
r.heroic_wisdom = GetHeroicWIS() - GetSpellBonuses().HeroicWIS;
r.heroic_charisma = GetHeroicCHA() - GetSpellBonuses().HeroicCHA;
r.heroic_magic_resist = GetHeroicMR() - GetSpellBonuses().HeroicMR;
r.heroic_fire_resist = GetHeroicFR() - GetSpellBonuses().HeroicFR;
r.heroic_cold_resist = GetHeroicCR() - GetSpellBonuses().HeroicCR;
r.heroic_poison_resist = GetHeroicPR() - GetSpellBonuses().HeroicPR;
r.heroic_disease_resist = GetHeroicDR() - GetSpellBonuses().HeroicDR;
r.heroic_corruption_resist = GetHeroicCorrup() - GetSpellBonuses().HeroicCorrup;
r.haste = GetHaste();
r.accuracy = GetAccuracy() - GetSpellBonuses().Accuracy[EQ::skills::HIGHEST_SKILL + 1];
r.attack = GetTotalATK() - GetSpellBonuses().ATK;
r.avoidance = GetAvoidance() - GetSpellBonuses().AvoidMeleeChance;
r.clairvoyance = GetClair() - GetSpellBonuses().Clairvoyance;
r.combat_effects = GetCombatEffects() - GetSpellBonuses().ProcChance;
r.damage_shield_mitigation = GetDSMit() - GetSpellBonuses().DSMitigation;
r.damage_shield = GetDS() - GetSpellBonuses().DamageShield;
r.dot_shielding = GetDoTShield() - GetSpellBonuses().DoTShielding;
r.hp_regen = GetHPRegen() - GetSpellBonuses().HPRegen;
r.mana_regen = GetManaRegen() - GetSpellBonuses().ManaRegen;
r.endurance_regen = GetEnduranceRegen() - GetSpellBonuses().EnduranceRegen;
r.shielding = GetShielding() - GetSpellBonuses().MeleeMitigation;
r.spell_damage = GetSpellDmg() - GetSpellBonuses().SpellDmg;
r.spell_shielding = GetSpellShield() - GetSpellBonuses().SpellShield;
r.strikethrough = GetStrikeThrough() - GetSpellBonuses().StrikeThrough;
r.stun_resist = GetStunResist() - GetSpellBonuses().StunResist;
r.backstab = 0;
r.wind = GetWindMod();
r.brass = GetBrassMod();
r.string = GetStringMod();
r.percussion = GetPercMod();
r.singing = GetSingMod();
r.baking = GetSkill(EQ::skills::SkillType::SkillBaking);
r.alchemy = GetSkill(EQ::skills::SkillType::SkillAlchemy);
r.jewelry = GetSkill(EQ::skills::SkillType::SkillJewelryMaking);
r.tailoring = GetSkill(EQ::skills::SkillType::SkillTailoring);
r.blacksmithing = GetSkill(EQ::skills::SkillType::SkillBlacksmithing);
r.fletching = GetSkill(EQ::skills::SkillType::SkillFletching);
r.brewing = GetSkill(EQ::skills::SkillType::SkillBrewing);
r.fishing = GetSkill(EQ::skills::SkillType::SkillFishing);
r.pottery = GetSkill(EQ::skills::SkillType::SkillPottery);
r.alcohol = GetSkill(EQ::skills::SkillType::SkillAlcoholTolerance);
r.tinkering = GetSkill(EQ::skills::SkillType::SkillTinkering);
r.updated_at = std::time(nullptr);
if (r.character_id > 0) {
CharacterStatsRecordRepository::UpdateOne(database, r);
} else {
r.character_id = CharacterID();
r.created_at = std::time(nullptr);
CharacterStatsRecordRepository::InsertOne(database, r);
}
}
+5 -3
View File
@@ -741,6 +741,8 @@ void Client::OnDisconnect(bool hard_disconnect) {
parse->EventPlayer(EVENT_DISCONNECT, this, "", 0);
}
RecordStats();
Disconnect();
}
@@ -857,12 +859,12 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
DataBucketKey k = GetScopedBucketKeys();
k.key = bucket_name;
auto const& player_value = DataBucket::CheckBucketKey(this, k);
if (player_value.empty()) {
auto b = DataBucket::GetData(k);
if (b.value.empty()) {
continue;
}
if (!zone->CompareDataBucket(ml.bucket_comparison, bucket_value, player_value)) {
if (!zone->CompareDataBucket(ml.bucket_comparison, bucket_value, b.value)) {
continue;
}
}
+2 -1
View File
@@ -30,6 +30,7 @@ uint8 GetCommandStatus(std::string command_name);
void ListModifyNPCStatMap(Client *c);
std::map<std::string, std::string> GetModifyNPCStatMap();
std::string GetModifyNPCStatDescription(std::string stat);
void SendFeatureSubCommands(Client *c);
void SendNPCEditSubCommands(Client *c);
void SendRuleSubCommands(Client *c);
void SendGuildSubCommands(Client *c);
@@ -134,7 +135,7 @@ void command_petitems(Client *c, const Seperator *sep);
void command_picklock(Client *c, const Seperator *sep);
void command_profanity(Client *c, const Seperator *sep);
void command_push(Client *c, const Seperator *sep);
void command_pvp(Client *c, const Seperator *sep);;
void command_pvp(Client *c, const Seperator *sep);
void command_raidloot(Client* c, const Seperator* sep);
void command_randomfeatures(Client *c, const Seperator *sep);
void command_refreshgroup(Client *c, const Seperator *sep);
+2 -2
View File
@@ -1207,7 +1207,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a
client->Message(
Chat::Yellow,
fmt::format(
"This corpse Contains {}.",
"This corpse contains {}.",
Strings::Money(
GetPlatinum(),
GetGold(),
@@ -1217,7 +1217,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a
).c_str()
);
} else {
client->Message(Chat::Yellow, "This corpse Contains no money.");
client->Message(Chat::Yellow, "This corpse contains no money.");
}
auto outapp = new EQApplicationPacket(OP_MoneyOnCorpse, sizeof(moneyOnCorpseStruct));
+519 -55
View File
@@ -1,9 +1,14 @@
#include "data_bucket.h"
#include "entity.h"
#include "zonedb.h"
#include "mob.h"
#include "worldserver.h"
#include <ctime>
#include <cctype>
#include "../common/repositories/data_buckets_repository.h"
extern WorldServer worldserver;
std::vector<DataBucketCacheEntry> g_data_bucket_cache = {};
void DataBucket::SetData(const std::string &bucket_key, const std::string &bucket_value, std::string expires_time)
{
@@ -22,40 +27,67 @@ void DataBucket::SetData(const std::string &bucket_key, const std::string &bucke
void DataBucket::SetData(const DataBucketKey &k)
{
auto b = DataBucketsRepository::NewEntity();
auto r = GetData(k);
auto r = GetData(k, true);
// if we have an entry, use it
if (r.id > 0) {
b = r;
}
// add scoping to bucket
if (k.character_id > 0) {
b.character_id = k.character_id;
} else if (k.npc_id > 0) {
}
else if (k.npc_id > 0) {
b.npc_id = k.npc_id;
} else if (k.bot_id > 0) {
}
else if (k.bot_id > 0) {
b.bot_id = k.bot_id;
}
uint64 bucket_id = b.id;
long long expires_time_unix = 0;
const uint64 bucket_id = b.id;
int64 expires_time_unix = 0;
if (!k.expires.empty()) {
expires_time_unix = (long long) std::time(nullptr) + Strings::ToInt(k.expires);
expires_time_unix = static_cast<int64>(std::time(nullptr)) + Strings::ToInt(k.expires);
if (isalpha(k.expires[0]) || isalpha(k.expires[k.expires.length() - 1])) {
expires_time_unix = (long long) std::time(nullptr) + Strings::TimeToSeconds(k.expires);
expires_time_unix = static_cast<int64>(std::time(nullptr)) + Strings::TimeToSeconds(k.expires);
}
}
if (bucket_id > 0) {
b.expires = expires_time_unix;
b.value = k.value;
b.expires = expires_time_unix;
b.value = k.value;
if (bucket_id) {
// loop cache and update cache value and timestamp
for (auto &ce: g_data_bucket_cache) {
if (CheckBucketMatch(ce.e, k)) {
ce.e = b;
ce.updated_time = GetCurrentTimeUNIX();
ce.update_action = DataBucketCacheUpdateAction::Upsert;
SendDataBucketCacheUpdate(ce);
break;
}
}
DataBucketsRepository::UpdateOne(database, b);
}
else {
b.expires = expires_time_unix;
b.key_ = k.key;
b.value = k.value;
DataBucketsRepository::InsertOne(database, b);
b.key_ = k.key;
b = DataBucketsRepository::InsertOne(database, b);
if (!ExistsInCache(b)) {
// add data bucket and timestamp to cache
auto ce = DataBucketCacheEntry{
.e = b,
.updated_time = DataBucket::GetCurrentTimeUNIX(),
.update_action = DataBucketCacheUpdateAction::Upsert
};
g_data_bucket_cache.emplace_back(ce);
SendDataBucketCacheUpdate(ce);
DeleteFromMissesCache(b);
}
}
}
@@ -66,8 +98,39 @@ std::string DataBucket::GetData(const std::string &bucket_key)
return GetData(k).value;
}
DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k)
// GetData fetches bucket data from the database or cache if it exists
// if the bucket doesn't exist, it will be added to the cache as a miss
// if ignore_misses_cache is true, the bucket will not be added to the cache as a miss
// the only place we should be ignoring the misses cache is on the initial read during SetData
DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k, bool ignore_misses_cache)
{
LogDataBuckets(
"Getting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}]",
k.key,
k.bot_id,
k.character_id,
k.npc_id
);
for (const auto &ce: g_data_bucket_cache) {
if (CheckBucketMatch(ce.e, k)) {
if (ce.e.expires > 0 && ce.e.expires < std::time(nullptr)) {
LogDataBuckets("Attempted to read expired key [{}] removing from cache", ce.e.key_);
DeleteData(k);
return DataBucketsRepository::NewEntity();
}
// this is a bucket miss, return empty entity
// we still cache bucket misses, so we don't have to hit the database
if (ce.e.id == 0) {
return DataBucketsRepository::NewEntity();
}
LogDataBuckets("Returning key [{}] value [{}] from cache", ce.e.key_, ce.e.value);
return ce.e;
}
}
auto r = DataBucketsRepository::GetWhere(
database,
fmt::format(
@@ -78,6 +141,40 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k)
);
if (r.empty()) {
// if we're ignoring the misses cache, don't add to the cache
// the only place this is ignored is during the initial read of SetData
if (!ignore_misses_cache) {
size_t size_before = g_data_bucket_cache.size();
// cache bucket misses, so we don't have to hit the database
// when scripts try to read a bucket that doesn't exist
g_data_bucket_cache.emplace_back(
DataBucketCacheEntry{
.e = DataBucketsRepository::DataBuckets{
.id = 0,
.key_ = k.key,
.value = "",
.expires = 0,
.character_id = k.character_id,
.npc_id = k.npc_id,
.bot_id = k.bot_id
},
.updated_time = DataBucket::GetCurrentTimeUNIX()
}
);
LogDataBuckets(
"Key [{}] not found in database, adding to cache as a miss character_id [{}] npc_id [{}] bot_id [{}] cache size before [{}] after [{}]",
k.key,
k.character_id,
k.npc_id,
k.bot_id,
size_before,
g_data_bucket_cache.size()
);
}
return {};
}
@@ -87,6 +184,24 @@ DataBucketsRepository::DataBuckets DataBucket::GetData(const DataBucketKey &k)
return {};
}
bool has_cache = false;
for (auto &ce: g_data_bucket_cache) {
if (ce.e.id == r[0].id) {
has_cache = true;
break;
}
}
if (!has_cache) {
// add data bucket and timestamp to cache
g_data_bucket_cache.emplace_back(
DataBucketCacheEntry{
.e = r[0],
.updated_time = DataBucket::GetCurrentTimeUNIX()
}
);
}
return r[0];
}
@@ -107,57 +222,68 @@ std::string DataBucket::GetDataRemaining(const std::string &bucket_key)
bool DataBucket::DeleteData(const std::string &bucket_key)
{
DataBucketKey r = {};
r.key = bucket_key;
return DeleteData(r);
DataBucketKey k = {};
k.key = bucket_key;
return DeleteData(k);
}
// GetDataBuckets bulk loads all data buckets for a mob
bool DataBucket::GetDataBuckets(Mob *mob)
{
DataBucketKey k = mob->GetScopedBucketKeys();
auto l = BaseDataBucketsRepository::GetWhere(
database,
fmt::format(
"{} (`expires` > {} OR `expires` = 0)",
DataBucket::GetScopedDbFilters(k),
(long long) std::time(nullptr)
)
);
DataBucketLoadType::Type t;
const uint32 id = mob->GetMobTypeIdentifier();
if (l.empty()) {
if (!id) {
return false;
}
mob->m_data_bucket_cache.clear();
DataBucketCache d;
for (const auto &e: l) {
d.bucket_id = e.id;
d.bucket_key = e.key_;
d.bucket_value = e.value;
d.bucket_expires = e.expires;
mob->m_data_bucket_cache.emplace_back(d);
if (mob->IsBot()) {
t = DataBucketLoadType::Bot;
}
else if (mob->IsClient()) {
t = DataBucketLoadType::Client;
}
else if (mob->IsNPC()) {
t = DataBucketLoadType::NPC;
}
BulkLoadEntities(t, {id});
return true;
}
std::string DataBucket::CheckBucketKey(const Mob *mob, const DataBucketKey &k)
{
std::string bucket_value;
for (const auto &d: mob->m_data_bucket_cache) {
if (d.bucket_key == k.key) {
bucket_value = d.bucket_value;
break;
}
}
return bucket_value;
}
bool DataBucket::DeleteData(const DataBucketKey &k)
{
size_t size_before = g_data_bucket_cache.size();
// delete from cache where contents match
g_data_bucket_cache.erase(
std::remove_if(
g_data_bucket_cache.begin(),
g_data_bucket_cache.end(),
[&](DataBucketCacheEntry &ce) {
bool match = CheckBucketMatch(ce.e, k);
if (match) {
ce.update_action = DataBucketCacheUpdateAction::Delete;
SendDataBucketCacheUpdate(ce);
}
return match;
}
),
g_data_bucket_cache.end()
);
LogDataBuckets(
"Deleting bucket key [{}] bot_id [{}] character_id [{}] npc_id [{}] cache size before [{}] after [{}]",
k.key,
k.bot_id,
k.character_id,
k.npc_id,
size_before,
g_data_bucket_cache.size()
);
return DataBucketsRepository::DeleteWhere(
database,
fmt::format(
@@ -170,16 +296,32 @@ bool DataBucket::DeleteData(const DataBucketKey &k)
std::string DataBucket::GetDataExpires(const DataBucketKey &k)
{
LogDataBuckets(
"Getting bucket expiration key [{}] bot_id [{}] character_id [{}] npc_id [{}]",
k.key,
k.bot_id,
k.character_id,
k.npc_id
);
auto r = GetData(k);
if (r.id == 0) {
return {};
}
return fmt::format("{}", r.expires);
return std::to_string(r.expires);
}
std::string DataBucket::GetDataRemaining(const DataBucketKey &k)
{
LogDataBuckets(
"Getting bucket remaining key [{}] bot_id [{}] character_id [{}] npc_id [{}]",
k.key,
k.bot_id,
k.character_id,
k.npc_id
);
auto r = GetData(k);
if (r.id == 0) {
return "0";
@@ -194,12 +336,23 @@ std::string DataBucket::GetScopedDbFilters(const DataBucketKey &k)
if (k.character_id > 0) {
query.emplace_back(fmt::format("character_id = {}", k.character_id));
}
else if (k.npc_id > 0) {
else {
query.emplace_back("character_id = 0");
}
if (k.npc_id > 0) {
query.emplace_back(fmt::format("npc_id = {}", k.npc_id));
}
else if (k.bot_id > 0) {
else {
query.emplace_back("npc_id = 0");
}
if (k.bot_id > 0) {
query.emplace_back(fmt::format("bot_id = {}", k.bot_id));
}
else {
query.emplace_back("bot_id = 0");
}
return fmt::format(
"{} {}",
@@ -207,3 +360,314 @@ std::string DataBucket::GetScopedDbFilters(const DataBucketKey &k)
!query.empty() ? "AND" : ""
);
}
bool DataBucket::CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe, const DataBucketKey &k)
{
return (
dbe.key_ == k.key &&
dbe.bot_id == k.bot_id &&
dbe.character_id == k.character_id &&
dbe.npc_id == k.npc_id
);
}
void DataBucket::BulkLoadEntities(DataBucketLoadType::Type t, std::vector<uint32> ids)
{
if (ids.empty()) {
return;
}
if (ids.size() == 1) {
bool has_cache = false;
for (const auto &ce: g_data_bucket_cache) {
if (t == DataBucketLoadType::Bot) {
has_cache = ce.e.bot_id == ids[0];
}
else if (t == DataBucketLoadType::Client) {
has_cache = ce.e.character_id == ids[0];
}
else if (t == DataBucketLoadType::NPC) {
has_cache = ce.e.npc_id == ids[0];
}
}
if (has_cache) {
LogDataBucketsDetail("LoadType [{}] ID [{}] has cache", DataBucketLoadType::Name[t], ids[0]);
return;
}
}
std::string column;
switch (t) {
case DataBucketLoadType::Bot:
column = "bot_id";
break;
case DataBucketLoadType::Client:
column = "character_id";
break;
case DataBucketLoadType::NPC:
column = "npc_id";
break;
default:
LogError("Incorrect LoadType [{}]", t);
break;
}
const auto &l = DataBucketsRepository::GetWhere(
database,
fmt::format(
"{} IN ({}) AND (`expires` > {} OR `expires` = 0)",
column,
Strings::Join(ids, ", "),
(long long) std::time(nullptr)
)
);
if (l.empty()) {
return;
}
size_t size_before = g_data_bucket_cache.size();
LogDataBucketsDetail("cache size before [{}] l size [{}]", g_data_bucket_cache.size(), l.size());
uint32 added_count = 0;
for (const auto &e: l) {
if (!ExistsInCache(e)) {
added_count++;
}
}
g_data_bucket_cache.reserve(g_data_bucket_cache.size() + added_count);
for (const auto &e: l) {
if (!ExistsInCache(e)) {
LogDataBucketsDetail("bucket id [{}] bucket key [{}] bucket value [{}]", e.id, e.key_, e.value);
g_data_bucket_cache.emplace_back(
DataBucketCacheEntry{
.e = e,
.updated_time = GetCurrentTimeUNIX()
}
);
}
}
LogDataBucketsDetail("cache size after [{}]", g_data_bucket_cache.size());
LogDataBuckets(
"Bulk Loaded ids [{}] column [{}] new cache size is [{}]",
ids.size(),
column,
g_data_bucket_cache.size()
);
}
void DataBucket::DeleteCachedBuckets(DataBucketLoadType::Type t, uint32 id)
{
size_t size_before = g_data_bucket_cache.size();
g_data_bucket_cache.erase(
std::remove_if(
g_data_bucket_cache.begin(),
g_data_bucket_cache.end(),
[&](DataBucketCacheEntry &ce) {
return (
(t == DataBucketLoadType::Bot && ce.e.bot_id == id) ||
(t == DataBucketLoadType::Client && ce.e.character_id == id) ||
(t == DataBucketLoadType::NPC && ce.e.npc_id == id)
);
}
),
g_data_bucket_cache.end()
);
LogDataBuckets(
"LoadType [{}] id [{}] cache size before [{}] after [{}]",
DataBucketLoadType::Name[t],
id,
size_before,
g_data_bucket_cache.size()
);
}
int64_t DataBucket::GetCurrentTimeUNIX()
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now().time_since_epoch()
).count();
}
bool DataBucket::ExistsInCache(const DataBucketsRepository::DataBuckets &e)
{
for (const auto &ce: g_data_bucket_cache) {
if (ce.e.id == e.id) {
return true;
}
}
return false;
}
bool DataBucket::SendDataBucketCacheUpdate(const DataBucketCacheEntry &e)
{
if (!e.e.id) {
return false;
}
EQ::Net::DynamicPacket p;
p.PutSerialize(0, e);
auto pack_size = sizeof(ServerDataBucketCacheUpdate_Struct) + p.Length();
auto pack = new ServerPacket(ServerOP_DataBucketCacheUpdate, static_cast<uint32_t>(pack_size));
auto buf = reinterpret_cast<ServerDataBucketCacheUpdate_Struct *>(pack->pBuffer);
buf->cereal_size = static_cast<uint32_t>(p.Length());
memcpy(buf->cereal_data, p.Data(), p.Length());
worldserver.SendPacket(pack);
return true;
}
void DataBucket::HandleWorldMessage(ServerPacket *p)
{
DataBucketCacheEntry n;
auto s = (ServerDataBucketCacheUpdate_Struct *) p->pBuffer;
EQ::Util::MemoryStreamReader ss(s->cereal_data, s->cereal_size);
cereal::BinaryInputArchive archive(ss);
archive(n);
LogDataBucketsDetail(
"Received cache packet for id [{}] key [{}] value [{}] action [{}]",
n.e.id,
n.e.key_,
n.e.value,
n.update_action
);
// delete
if (n.update_action == DataBucketCacheUpdateAction::Delete) {
DeleteFromMissesCache(n.e);
g_data_bucket_cache.erase(
std::remove_if(
g_data_bucket_cache.begin(),
g_data_bucket_cache.end(),
[&](DataBucketCacheEntry &ce) {
bool match = n.e.id > 0 && ce.e.id == n.e.id;
if (match) {
LogDataBuckets(
"[delete] cache key [{}] id [{}] cache_size before [{}] after [{}]",
ce.e.key_,
ce.e.id,
g_data_bucket_cache.size(),
g_data_bucket_cache.size() - 1
);
}
return match;
}
),
g_data_bucket_cache.end()
);
return;
}
// update
bool has_key = false;
for (auto &ce: g_data_bucket_cache) {
// update cache
if (ce.e.id == n.e.id) {
// reject old updates
int64 time_delta = ce.updated_time - n.updated_time;
if (ce.updated_time >= n.updated_time) {
LogDataBuckets(
"Attempted to update older cache key [{}] rejecting old time [{}] new time [{}] delta [{}] cache_size [{}]",
ce.e.key_,
ce.updated_time,
n.updated_time,
time_delta,
g_data_bucket_cache.size()
);
return;
}
DeleteFromMissesCache(n.e);
LogDataBuckets(
"[update] cache id [{}] key [{}] value [{}] old time [{}] new time [{}] delta [{}] cache_size [{}]",
ce.e.id,
ce.e.key_,
n.e.value,
ce.updated_time,
n.updated_time,
time_delta,
g_data_bucket_cache.size()
);
ce.e = n.e;
ce.updated_time = n.updated_time;
has_key = true;
break;
}
}
// create
if (!has_key) {
DeleteFromMissesCache(n.e);
size_t size_before = g_data_bucket_cache.size();
g_data_bucket_cache.emplace_back(
DataBucketCacheEntry{
.e = n.e,
.updated_time = GetCurrentTimeUNIX()
}
);
LogDataBuckets(
"[create] Adding new cache id [{}] key [{}] value [{}] cache size before [{}] after [{}]",
n.e.id,
n.e.key_,
n.e.value,
size_before,
g_data_bucket_cache.size()
);
}
}
void DataBucket::DeleteFromMissesCache(DataBucketsRepository::DataBuckets e)
{
// delete from cache where there might have been a written bucket miss to the cache
// this is to prevent the cache from growing too large
size_t size_before = g_data_bucket_cache.size();
g_data_bucket_cache.erase(
std::remove_if(
g_data_bucket_cache.begin(),
g_data_bucket_cache.end(),
[&](DataBucketCacheEntry &ce) {
return ce.e.id == 0 && ce.e.key_ == e.key_ &&
ce.e.character_id == e.character_id &&
ce.e.npc_id == e.npc_id &&
ce.e.bot_id == e.bot_id;
}
),
g_data_bucket_cache.end()
);
LogDataBucketsDetail(
"Deleted bucket misses from cache where key [{}] size before [{}] after [{}]",
e.key_,
size_before,
g_data_bucket_cache.size()
);
}
void DataBucket::ClearCache()
{
g_data_bucket_cache.clear();
LogInfo("Cleared data buckets cache");
}
+63 -13
View File
@@ -9,7 +9,29 @@
#include "../common/types.h"
#include "../common/repositories/data_buckets_repository.h"
#include "mob.h"
#include "../common/json/json_archive_single_line.h"
#include "../common/servertalk.h"
enum DataBucketCacheUpdateAction : uint8 {
Upsert,
Delete
};
struct DataBucketCacheEntry {
DataBucketsRepository::DataBuckets e;
int64_t updated_time{};
DataBucketCacheUpdateAction update_action{};
template<class Archive>
void serialize(Archive &ar)
{
ar(
CEREAL_NVP(e),
CEREAL_NVP(updated_time),
CEREAL_NVP(update_action)
);
}
};
struct DataBucketKey {
std::string key;
@@ -20,25 +42,53 @@ struct DataBucketKey {
int64_t bot_id;
};
namespace DataBucketLoadType {
enum Type : uint8 {
Bot,
Client,
NPC,
MaxType
};
static const std::string Name[Type::MaxType] = {
"Bot",
"Client",
"NPC",
};
}
class DataBucket {
public:
// non-scoped bucket methods (for global buckets)
static void SetData(const std::string& bucket_key, const std::string& bucket_value, std::string expires_time = "");
static bool DeleteData(const std::string& bucket_key);
static std::string GetData(const std::string& bucket_key);
static std::string GetDataExpires(const std::string& bucket_key);
static std::string GetDataRemaining(const std::string& bucket_key);
static void SetData(const std::string &bucket_key, const std::string &bucket_value, std::string expires_time = "");
static bool DeleteData(const std::string &bucket_key);
static std::string GetData(const std::string &bucket_key);
static std::string GetDataExpires(const std::string &bucket_key);
static std::string GetDataRemaining(const std::string &bucket_key);
static bool GetDataBuckets(Mob* mob);
static bool GetDataBuckets(Mob *mob);
static int64_t GetCurrentTimeUNIX();
// scoped bucket methods
static void SetData(const DataBucketKey& k);
static bool DeleteData(const DataBucketKey& k);
static DataBucketsRepository::DataBuckets GetData(const DataBucketKey& k);
static std::string GetDataExpires(const DataBucketKey& k);
static std::string GetDataRemaining(const DataBucketKey& k);
static std::string CheckBucketKey(const Mob* mob, const DataBucketKey& k);
static std::string GetScopedDbFilters(const DataBucketKey& k);
static void SetData(const DataBucketKey &k);
static bool DeleteData(const DataBucketKey &k);
static DataBucketsRepository::DataBuckets GetData(const DataBucketKey &k, bool ignore_misses_cache = false);
static std::string GetDataExpires(const DataBucketKey &k);
static std::string GetDataRemaining(const DataBucketKey &k);
static std::string GetScopedDbFilters(const DataBucketKey &k);
// bucket repository versus key matching
static bool CheckBucketMatch(const DataBucketsRepository::DataBuckets &dbe, const DataBucketKey &k);
static bool ExistsInCache(const DataBucketsRepository::DataBuckets &e);
static void BulkLoadEntities(DataBucketLoadType::Type t, std::vector<uint32> ids);
static void DeleteCachedBuckets(DataBucketLoadType::Type t, uint32 id);
static bool SendDataBucketCacheUpdate(const DataBucketCacheEntry &e);
static void HandleWorldMessage(ServerPacket *p);
static void DeleteFromMissesCache(DataBucketsRepository::DataBuckets e);
static void ClearCache();
};
#endif //EQEMU_DATABUCKET_H
+26
View File
@@ -83,6 +83,8 @@ Doors::Doors(const DoorsRepository::Doors &door) :
m_close_timer.Disable();
m_disable_timer = (door.disable_timer == 1 ? true : false);
m_is_blacklisted_to_open = GetIsDoorBlacklisted();
}
Doors::Doors(const char *model, const glm::vec4 &position, uint8 open_type, uint16 size) :
@@ -901,3 +903,27 @@ bool Doors::IsDestinationZoneSame() const
{
return m_same_destination_zone;
}
// IsDoorBlacklisted has a static list of doors that are blacklisted
// from being opened by NPCs. This is used to prevent NPCs from opening
// doors that are not meant to be opened by NPCs.
bool Doors::GetIsDoorBlacklisted()
{
std::vector<std::string> blacklist = {
"TOGGLE",
"PNDRESSER101",
};
for (auto& name : blacklist) {
std::string door_name = GetDoorName();
if (name == door_name) {
return true;
}
}
return false;
}
bool Doors::IsDoorBlacklisted() {
return m_is_blacklisted_to_open;
}
+4
View File
@@ -69,7 +69,10 @@ public:
bool HasDestinationZone() const;
bool IsDestinationZoneSame() const;
bool IsDoorBlacklisted();
private:
bool GetIsDoorBlacklisted();
bool m_has_destination_zone = false;
bool m_same_destination_zone = false;
@@ -99,5 +102,6 @@ private:
uint8 m_is_ldon_door;
int m_dz_switch_id = 0;
uint32 m_client_version_mask;
bool m_is_blacklisted_to_open = false; // is door blacklisted to open by npcs
};
#endif
+9 -9
View File
@@ -1047,42 +1047,42 @@ void Perl__processmobswhilezoneempty(bool on)
quest_manager.processmobswhilezoneempty(on);
}
void Perl__npcrace(int race_id)
void Perl__npcrace(uint16 race_id)
{
quest_manager.npcrace(race_id);
}
void Perl__npcgender(int gender_id)
void Perl__npcgender(uint8 gender_id)
{
quest_manager.npcgender(gender_id);
}
void Perl__npcsize(int size)
void Perl__npcsize(float size)
{
quest_manager.npcsize(size);
}
void Perl__npctexture(int texture_id)
void Perl__npctexture(uint8 texture_id)
{
quest_manager.npctexture(texture_id);
}
void Perl__playerrace(int race_id)
void Perl__playerrace(uint16 race_id)
{
quest_manager.playerrace(race_id);
}
void Perl__playergender(int gender_id)
void Perl__playergender(uint8 gender_id)
{
quest_manager.playergender(gender_id);
}
void Perl__playersize(int newsize)
void Perl__playersize(float size)
{
quest_manager.playersize(newsize);
quest_manager.playersize(size);
}
void Perl__playertexture(int texture_id)
void Perl__playertexture(uint8 texture_id)
{
quest_manager.playertexture(texture_id);
}
+2 -2
View File
@@ -674,6 +674,8 @@ void EntityList::AddNPC(NPC *npc, bool send_spawn_packet, bool dont_queue)
npc_list.emplace(std::pair<uint16, NPC *>(npc->GetID(), npc));
mob_list.emplace(std::pair<uint16, Mob *>(npc->GetID(), npc));
entity_list.ScanCloseMobs(npc->close_mobs, npc, true);
if (parse->HasQuestSub(npc->GetNPCTypeID(), EVENT_SPAWN)) {
parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0);
}
@@ -713,8 +715,6 @@ void EntityList::AddNPC(NPC *npc, bool send_spawn_packet, bool dont_queue)
npc->SendPositionToClients();
entity_list.ScanCloseMobs(npc->close_mobs, npc, true);
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_SPAWN_ZONE)) {
npc->DispatchZoneControllerEvent(EVENT_SPAWN_ZONE, npc, "", 0, nullptr);
}
+4 -4
View File
@@ -39,9 +39,9 @@ void GlobalLootManager::ShowZoneGlobalLoot(Client *c) const
global_loot_table += DialogueWindow::TableRow(
fmt::format(
"{}{}{}",
DialogueWindow::TableCell(Strings::Commify(e.GetID())),
DialogueWindow::TableCell(std::to_string(e.GetID())),
DialogueWindow::TableCell(e.GetDescription()),
DialogueWindow::TableCell(Strings::Commify(e.GetLootTableID()))
DialogueWindow::TableCell(std::to_string(e.GetLootTableID()))
)
);
}
@@ -76,9 +76,9 @@ void GlobalLootManager::ShowNPCGlobalLoot(Client *c, NPC *t) const
global_loot_table += DialogueWindow::TableRow(
fmt::format(
"{}{}{}",
DialogueWindow::TableCell(Strings::Commify(e.GetID())),
DialogueWindow::TableCell(std::to_string(e.GetID())),
DialogueWindow::TableCell(e.GetDescription()),
DialogueWindow::TableCell(Strings::Commify(e.GetLootTableID()))
DialogueWindow::TableCell(std::to_string(e.GetLootTableID()))
)
);
}
+18 -17
View File
@@ -67,23 +67,24 @@ void command_appearanceeffects(Client *c, const Seperator *sep)
);
} else if (is_remove) {
t->SendIllusionPacket(
t->GetRace(),
t->GetGender(),
t->GetTexture(),
t->GetHelmTexture(),
t->GetHairColor(),
t->GetBeardColor(),
t->GetEyeColor1(),
t->GetEyeColor2(),
t->GetHairStyle(),
t->GetLuclinFace(),
t->GetBeard(),
0xFF,
t->GetDrakkinHeritage(),
t->GetDrakkinTattoo(),
t->GetDrakkinDetails(),
t->GetSize(),
false
AppearanceStruct{
.beard = t->GetBeard(),
.beard_color = t->GetBeardColor(),
.drakkin_details = t->GetDrakkinDetails(),
.drakkin_heritage = t->GetDrakkinHeritage(),
.drakkin_tattoo = t->GetDrakkinTattoo(),
.eye_color_one = t->GetEyeColor1(),
.eye_color_two = t->GetEyeColor2(),
.face = t->GetLuclinFace(),
.gender_id = t->GetGender(),
.hair = t->GetHairStyle(),
.hair_color = t->GetHairColor(),
.helmet_texture = t->GetHelmTexture(),
.race_id = t->GetRace(),
.send_effects = false,
.size = t->GetSize(),
.texture = t->GetTexture(),
}
);
t->ClearAppearenceEffects();
c->Message(
+7 -7
View File
@@ -41,7 +41,7 @@ void command_bugs(Client *c, const Seperator *sep)
auto bug_id = Strings::ToUnsignedInt(sep->arg[2]);
auto r = BugReportsRepository::FindOne(content_db, bug_id);
auto r = BugReportsRepository::FindOne(database, bug_id);
if (!r.id) {
c->Message(
Chat::White,
@@ -55,7 +55,7 @@ void command_bugs(Client *c, const Seperator *sep)
r.bug_status = 1;
if (!BugReportsRepository::UpdateOne(content_db, r)) {
if (!BugReportsRepository::UpdateOne(database, r)) {
c->Message(
Chat::White,
fmt::format(
@@ -81,7 +81,7 @@ void command_bugs(Client *c, const Seperator *sep)
}
auto bug_id = Strings::ToUnsignedInt(sep->arg[2]);
auto deleted_count = BugReportsRepository::DeleteOne(content_db, bug_id);
auto deleted_count = BugReportsRepository::DeleteOne(database, bug_id);
if (!deleted_count) {
c->Message(
Chat::White,
@@ -112,7 +112,7 @@ void command_bugs(Client *c, const Seperator *sep)
auto bug_id = Strings::ToUnsignedInt(sep->arg[2]);
auto bug_review = sep->argplus[3];
auto r = BugReportsRepository::FindOne(content_db, bug_id);
auto r = BugReportsRepository::FindOne(database, bug_id);
if (!r.id) {
c->Message(
Chat::White,
@@ -128,7 +128,7 @@ void command_bugs(Client *c, const Seperator *sep)
r.last_reviewer = c->GetCleanName();
r.reviewer_notes = bug_review;
if (!BugReportsRepository::UpdateOne(content_db, r)) {
if (!BugReportsRepository::UpdateOne(database, r)) {
c->Message(
Chat::White,
fmt::format(
@@ -154,7 +154,7 @@ void command_bugs(Client *c, const Seperator *sep)
auto search_criteria = sep->argplus[2];
auto l = BugReportsRepository::GetWhere(
content_db,
database,
fmt::format(
"bug_status = 0 AND (character_name LIKE '%%{}%%' OR bug_report LIKE '%%{}%%')",
Strings::Escape(search_criteria),
@@ -205,7 +205,7 @@ void command_bugs(Client *c, const Seperator *sep)
auto bug_id = Strings::ToUnsignedInt(sep->arg[2]);
auto r = BugReportsRepository::FindOne(content_db, bug_id);
auto r = BugReportsRepository::FindOne(database, bug_id);
if (!r.id) {
c->Message(
Chat::White,
+1 -1
View File
@@ -47,7 +47,7 @@ void DoorManipulation::CommandHandler(Client *c, const Seperator *sep)
const std::string set_size_action = "set_size";
// we're passing a move action here
if (!arg3.empty() && Strings::IsNumber(arg3)) {
if (!arg3.empty() && (Strings::IsFloat(arg3) || Strings::IsNumber(arg3))) {
float x_move = 0.0f;
float y_move = 0.0f;
float z_move = 0.0f;
+116 -126
View File
@@ -2,55 +2,35 @@
void command_feature(Client *c, const Seperator *sep)
{
// nested command aliasing
std::string command = sep->arg[0] ? sep->arg[0] : "";
bool is_size_alias = sep->arg[0] && Strings::Contains(command, "#size");
bool is_nested_alias = (is_size_alias);
const bool is_size_alias = sep->arg[0] && Strings::Contains(command, "#size");
const bool is_nested_alias = is_size_alias;
int arguments = sep->argnum;
const auto arguments = sep->argnum;
if ((arguments < 2 || !sep->IsNumber(2)) && !is_nested_alias) {
auto feature_save_link = Saylink::Silent("#npcedit featuresave");
c->Message(Chat::White, "Usage: #feature beard [Beard] - Change your or your target's Beard");
c->Message(Chat::White, "Usage: #feature beardcolor [Beard Color] - Change your or your target's Beard Color");
c->Message(Chat::White, "Usage: #feature details [Details] - Change your or your target's Drakkin Details");
c->Message(Chat::White, "Usage: #feature eyes [Eye Color] - Change your or your target's Eyes");
c->Message(Chat::White, "Usage: #feature face [Face] - Change your or your target's Face");
c->Message(Chat::White, "Usage: #feature gender [Gender] - Change your or your target's Gender");
c->Message(Chat::White, "Usage: #feature hair [Hair] - Change your or your target's Hair");
c->Message(Chat::White, "Usage: #feature haircolor [Hair Color] - Change your or your target's Hair Color");
c->Message(Chat::White, "Usage: #feature helm [Helmet Texture] - Change your or your target's Helmet Texture");
c->Message(Chat::White, "Usage: #feature heritage [Heritage] - Change your or your target's Drakkin Heritage");
c->Message(Chat::White, "Usage: #feature race [Race ID] - Change your or your target's Race");
c->Message(Chat::White, "Usage: #feature size [Size] - Change your or your target's Size (Valid values are 0 to 255, decimal increments are allowed.)");
c->Message(Chat::White, "Usage: #feature tattoo [Tattoo] - Change your or your target's Drakkin Tattoos");
c->Message(Chat::White, "Usage: #feature texture [Texture] - Change your or your target's Texture");
c->Message(
Chat::White,
fmt::format(
"Note: All features are temporary. If your target is an NPC, you can save these features to the database using {}.",
feature_save_link
).c_str()
);
SendFeatureSubCommands(c);
return;
}
Mob* target = c->GetTarget() ? c->GetTarget() : c;
Mob* t = c;
if (c->GetTarget()) {
t = c->GetTarget();
}
bool is_beard = !strcasecmp(sep->arg[1], "beard");
bool is_beard_color = !strcasecmp(sep->arg[1], "beardcolor");
bool is_details = !strcasecmp(sep->arg[1], "details");
bool is_eyes = !strcasecmp(sep->arg[1], "eyes");
bool is_face = !strcasecmp(sep->arg[1], "face");
bool is_gender = !strcasecmp(sep->arg[1], "gender");
bool is_hair = !strcasecmp(sep->arg[1], "hair");
bool is_hair_color = !strcasecmp(sep->arg[1], "haircolor");
bool is_helm = !strcasecmp(sep->arg[1], "helm");
bool is_heritage = !strcasecmp(sep->arg[1], "heritage");
bool is_race = !strcasecmp(sep->arg[1], "race");
bool is_size = !strcasecmp(sep->arg[1], "size") || is_size_alias;
bool is_tattoo = !strcasecmp(sep->arg[1], "tattoo");
bool is_texture = !strcasecmp(sep->arg[1], "texture");
const bool is_beard = !strcasecmp(sep->arg[1], "beard");
const bool is_beard_color = !strcasecmp(sep->arg[1], "beardcolor");
const bool is_details = !strcasecmp(sep->arg[1], "details");
const bool is_eyes = !strcasecmp(sep->arg[1], "eyes");
const bool is_face = !strcasecmp(sep->arg[1], "face");
const bool is_gender = !strcasecmp(sep->arg[1], "gender");
const bool is_hair = !strcasecmp(sep->arg[1], "hair");
const bool is_hair_color = !strcasecmp(sep->arg[1], "haircolor");
const bool is_helm = !strcasecmp(sep->arg[1], "helm");
const bool is_heritage = !strcasecmp(sep->arg[1], "heritage");
const bool is_race = !strcasecmp(sep->arg[1], "race");
const bool is_size = !strcasecmp(sep->arg[1], "size") || is_size_alias;
const bool is_tattoo = !strcasecmp(sep->arg[1], "tattoo");
const bool is_texture = !strcasecmp(sep->arg[1], "texture");
if (
!is_beard &&
@@ -68,103 +48,82 @@ void command_feature(Client *c, const Seperator *sep)
!is_tattoo &&
!is_texture
) {
auto feature_save_link = Saylink::Silent("#npcedit featuresave");
c->Message(Chat::White, "Usage: #feature beard [Beard] - Change your or your target's Beard");
c->Message(Chat::White, "Usage: #feature beardcolor [Beard Color] - Change your or your target's Beard Color");
c->Message(Chat::White, "Usage: #feature details [Details] - Change your or your target's Drakkin Details");
c->Message(Chat::White, "Usage: #feature eyes [Eye Color] - Change your or your target's Eyes");
c->Message(Chat::White, "Usage: #feature face [Face] - Change your or your target's Face");
c->Message(Chat::White, "Usage: #feature gender [Gender] - Change your or your target's Gender");
c->Message(Chat::White, "Usage: #feature hair [Hair] - Change your or your target's Hair");
c->Message(Chat::White, "Usage: #feature haircolor [Hair Color] - Change your or your target's Hair Color");
c->Message(Chat::White, "Usage: #feature helm [Helmet Texture] - Change your or your target's Helmet Texture");
c->Message(Chat::White, "Usage: #feature heritage [Heritage] - Change your or your target's Drakkin Heritage");
c->Message(Chat::White, "Usage: #feature race [Race ID] - Change your or your target's Race");
c->Message(Chat::White, "Usage: #feature size [Size] - Change your or your target's Size (Valid values are 0 to 255, decimal increments are allowed.)");
c->Message(Chat::White, "Usage: #feature tattoo [Tattoo] - Change your or your target's Drakkin Tattoos");
c->Message(Chat::White, "Usage: #feature texture [Texture] - Change your or your target's Texture");
c->Message(
Chat::White,
fmt::format(
"Note: All features are temporary. If your target is an NPC, you can save these features to the database using {}.",
feature_save_link
).c_str()
);
SendFeatureSubCommands(c);
return;
}
FaceChange_Struct face{};
face.haircolor = target->GetHairColor();
face.beardcolor = target->GetBeardColor();
face.eyecolor1 = target->GetEyeColor1();
face.eyecolor2 = target->GetEyeColor2();
face.hairstyle = target->GetHairStyle();
face.face = target->GetLuclinFace();
face.beard = target->GetBeard();
face.drakkin_heritage = target->GetDrakkinHeritage();
face.drakkin_tattoo = target->GetDrakkinTattoo();
face.drakkin_details = target->GetDrakkinDetails();
FaceChange_Struct f{
.haircolor = t->GetHairColor(),
.beardcolor = t->GetBeardColor(),
.eyecolor1 = t->GetEyeColor1(),
.eyecolor2 = t->GetEyeColor2(),
.hairstyle = t->GetHairStyle(),
.beard = t->GetBeard(),
.face = t->GetLuclinFace(),
.drakkin_heritage = t->GetDrakkinHeritage(),
.drakkin_tattoo = t->GetDrakkinTattoo(),
.drakkin_details = t->GetDrakkinDetails(),
};
auto gender = target->GetGender();
auto helm_texture = target->GetHelmTexture();
auto race = target->GetModel();
auto size = target->GetSize();
auto texture = target->GetTexture();
uint8 gender = t->GetGender();
uint8 helm_texture = t->GetHelmTexture();
uint16 race = t->GetModel();
float size = t->GetSize();
uint8 texture = t->GetTexture();
std::string feature_changed;
float value_changed = 0.0f;
if (is_beard) {
face.beard = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
f.beard = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Beard";
value_changed = face.beard;
value_changed = f.beard;
} else if (is_beard_color) {
face.beardcolor = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
f.beardcolor = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Beard Color";
value_changed = face.beardcolor;
value_changed = f.beardcolor;
} else if (is_details) {
if (target->GetRace() != DRAKKIN) {
if (t->GetRace() != DRAKKIN) {
c->Message(Chat::White, "You must target a Drakkin to use this command.");
return;
}
face.drakkin_details = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
f.drakkin_details = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Drakkin Details";
value_changed = static_cast<float>(face.drakkin_details);
value_changed = static_cast<float>(f.drakkin_details);
} else if (is_eyes) {
face.eyecolor1 = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
f.eyecolor1 = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Eyes";
value_changed = face.eyecolor1; // eyecolor2 isn't used
value_changed = f.eyecolor1; // eyecolor2 isn't used
} else if (is_face) {
face.face = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
f.face = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Face";
value_changed = face.face;
value_changed = f.face;
} else if (is_gender) {
gender = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Gender";
value_changed = gender;
} else if (is_hair) {
face.hairstyle = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
f.hairstyle = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Hair";
value_changed = face.hairstyle;
value_changed = f.hairstyle;
} else if (is_hair_color) {
face.haircolor = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
f.haircolor = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Hair Color";
value_changed = face.haircolor;
value_changed = f.haircolor;
} else if (is_helm) {
helm_texture = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Helmet Texture";
value_changed = helm_texture;
} else if (is_heritage) {
if (target->GetRace() != DRAKKIN) {
if (t->GetRace() != DRAKKIN) {
c->Message(Chat::White, "You must target a Drakkin to use this command.");
return;
}
face.drakkin_heritage = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
f.drakkin_heritage = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Drakkin Heritage";
value_changed = static_cast<float>(face.drakkin_heritage);
value_changed = static_cast<float>(f.drakkin_heritage);
} else if (is_race) {
race = static_cast<uint16>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Race";
@@ -189,14 +148,14 @@ void command_feature(Client *c, const Seperator *sep)
feature_changed = "Size";
value_changed = size;
} else if (is_tattoo) {
if (target->GetRace() != DRAKKIN) {
if (t->GetRace() != DRAKKIN) {
c->Message(Chat::White, "You must target a Drakkin to use this command.");
return;
}
face.drakkin_tattoo = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
f.drakkin_tattoo = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Drakkin Tattoos";
value_changed = static_cast<float>(face.drakkin_tattoo);
value_changed = static_cast<float>(f.drakkin_tattoo);
} else if (is_texture) {
texture = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
feature_changed = "Texture";
@@ -206,29 +165,34 @@ void command_feature(Client *c, const Seperator *sep)
// For now face number is not set through SetFace. This is because the
// client may not update face features after being set to an invalid face
// until a specific valid face number is re-sent (needs more research)
if (!is_gender && !is_helm && !is_race && !is_size && !is_texture && !is_face)
{
target->SetFaceAppearance(face);
}
else
{
target->SendIllusionPacket(
race,
gender,
texture,
helm_texture,
face.haircolor,
face.beardcolor,
target->GetEyeColor1(),
target->GetEyeColor2(),
face.hairstyle,
face.face,
face.beard,
0xFF,
face.drakkin_heritage,
face.drakkin_tattoo,
face.drakkin_details,
size
if (
!is_face &&
!is_gender &&
!is_helm &&
!is_race &&
!is_size &&
!is_texture
) {
t->SetFaceAppearance(f);
} else {
t->SendIllusionPacket(
AppearanceStruct{
.beard = f.beard,
.beard_color = f.beardcolor,
.drakkin_details = f.drakkin_details,
.drakkin_heritage = f.drakkin_heritage,
.drakkin_tattoo = f.drakkin_tattoo,
.eye_color_one = t->GetEyeColor1(),
.eye_color_two = t->GetEyeColor2(),
.face = f.face,
.gender_id = gender,
.hair = f.hairstyle,
.hair_color = f.haircolor,
.helmet_texture = helm_texture,
.race_id = race,
.size = size,
.texture = texture,
}
);
}
@@ -237,7 +201,7 @@ void command_feature(Client *c, const Seperator *sep)
fmt::format(
"{} set for {} to {}.",
feature_changed,
c->GetTargetDescription(target),
c->GetTargetDescription(t),
(
is_size ?
fmt::format(
@@ -253,3 +217,29 @@ void command_feature(Client *c, const Seperator *sep)
);
}
void SendFeatureSubCommands(Client *c)
{
const std::string& feature_save_link = Saylink::Silent("#npcedit featuresave");
c->Message(Chat::White, "Usage: #feature beard [Beard] - Change your or your target's Beard");
c->Message(Chat::White, "Usage: #feature beardcolor [Beard Color] - Change your or your target's Beard Color");
c->Message(Chat::White, "Usage: #feature details [Details] - Change your or your target's Drakkin Details");
c->Message(Chat::White, "Usage: #feature eyes [Eye Color] - Change your or your target's Eyes");
c->Message(Chat::White, "Usage: #feature face [Face] - Change your or your target's Face");
c->Message(Chat::White, "Usage: #feature gender [Gender] - Change your or your target's Gender");
c->Message(Chat::White, "Usage: #feature hair [Hair] - Change your or your target's Hair");
c->Message(Chat::White, "Usage: #feature haircolor [Hair Color] - Change your or your target's Hair Color");
c->Message(Chat::White, "Usage: #feature helm [Helmet Texture] - Change your or your target's Helmet Texture");
c->Message(Chat::White, "Usage: #feature heritage [Heritage] - Change your or your target's Drakkin Heritage");
c->Message(Chat::White, "Usage: #feature race [Race ID] - Change your or your target's Race");
c->Message(Chat::White, "Usage: #feature size [Size] - Change your or your target's Size (Valid values are 0 to 255, decimal increments are allowed.)");
c->Message(Chat::White, "Usage: #feature tattoo [Tattoo] - Change your or your target's Drakkin Tattoos");
c->Message(Chat::White, "Usage: #feature texture [Texture] - Change your or your target's Texture");
c->Message(
Chat::White,
fmt::format(
"Note: All features are temporary. If your target is an NPC, you can save these features to the database using {}.",
feature_save_link
).c_str()
);
}
+3 -3
View File
@@ -10,7 +10,7 @@ void FindAA(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"AA {} | {}",
Strings::Commify(aa_id),
aa_id,
aa_name
).c_str()
);
@@ -22,7 +22,7 @@ void FindAA(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"AA ID {} was not found.",
Strings::Commify(aa_id)
aa_id
).c_str()
);
@@ -51,7 +51,7 @@ void FindAA(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"AA {} | {}",
Strings::Commify(a.first),
a.first,
aa_name
).c_str()
);
+2 -2
View File
@@ -12,7 +12,7 @@ void FindCharacter(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Character ID {} does not exist or is invalid.",
Strings::Commify(character_id)
character_id
).c_str()
);
@@ -23,7 +23,7 @@ void FindCharacter(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Character ID {} | {}",
Strings::Commify(character_id),
character_id,
e.name
).c_str()
);
+2 -2
View File
@@ -13,7 +13,7 @@ void FindCurrency(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"There is no currency with an item ID of {}.",
Strings::Commify(item_id)
item_id
).c_str()
);
@@ -26,7 +26,7 @@ void FindCurrency(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Item ID {} does not exist.",
Strings::Commify(item_id)
item_id
).c_str()
);
+3 -3
View File
@@ -10,7 +10,7 @@ void FindFaction(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Faction {} | {}",
Strings::Commify(faction_id),
faction_id,
faction_name
).c_str()
);
@@ -22,7 +22,7 @@ void FindFaction(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Faction ID {} was not found.",
Strings::Commify(faction_id)
faction_id
).c_str()
);
@@ -48,7 +48,7 @@ void FindFaction(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Faction {} | {}",
Strings::Commify(faction_id),
faction_id,
faction_name
).c_str()
);
+1 -1
View File
@@ -104,7 +104,7 @@ void FindItem(Client *c, const Seperator *sep)
"{} | {} ({})",
summon_links,
database.CreateItemLink(e),
Strings::Commify(item->ID)
item->ID
).c_str()
);
+1 -1
View File
@@ -47,7 +47,7 @@ void FindNPCType(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"NPC {} | {}{}",
Strings::Commify(row[0]),
row[0],
row[1],
(
can_spawn_npcs ?
+2 -2
View File
@@ -19,7 +19,7 @@ void FindRecipe(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Recipe ID {} could not be found.",
Strings::Commify(recipe_id)
recipe_id
).c_str()
);
return;
@@ -29,7 +29,7 @@ void FindRecipe(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Recipe {} | {}{}",
Strings::Commify(recipe_id),
recipe_id,
l[0].name,
(
can_view_recipes ?
+3 -3
View File
@@ -16,7 +16,7 @@ void FindSpell(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Spell ID {} was not found.",
Strings::Commify(spell_id)
spell_id
).c_str()
);
@@ -27,7 +27,7 @@ void FindSpell(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Spell {} | {}",
Strings::Commify(spell_id),
spell_id,
spells[spell_id].name
).c_str()
);
@@ -53,7 +53,7 @@ void FindSpell(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Spell {} | {}{}",
Strings::Commify(spell_id),
spell_id,
spell_name,
(
can_cast_spells ?
+3 -3
View File
@@ -18,7 +18,7 @@ void FindTask(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Task ID {} was not found.",
Strings::Commify(task_id)
task_id
).c_str()
);
@@ -29,7 +29,7 @@ void FindTask(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Task {} | {}",
Strings::Commify(task_id),
task_id,
task_name
).c_str()
);
@@ -52,7 +52,7 @@ void FindTask(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Task {} | {}{}",
Strings::Commify(t.first),
t.first,
task_name,
(
can_assign_tasks ?
+20 -7
View File
@@ -60,11 +60,11 @@ void command_fixmob(Client *c, const Seperator *sep)
ChangeSetting = Race;
}
else if (strcasecmp(command, "gender") == 0) {
if (Gender == 0 && codeMove == 'p') {
Gender = 2;
if (Gender == MALE && codeMove == 'p') {
Gender = NEUTER;
}
else if (Gender >= 2 && codeMove != 'p') {
Gender = 0;
else if (Gender >= NEUTER && codeMove != 'p') {
Gender = MALE;
}
else {
Gender += Adjustment;
@@ -238,9 +238,22 @@ void command_fixmob(Client *c, const Seperator *sep)
}
else {
target->SendIllusionPacket(
Race, Gender, Texture, HelmTexture, HairColor, BeardColor,
EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF,
DrakkinHeritage, DrakkinTattoo, DrakkinDetails
AppearanceStruct{
.beard = Beard,
.beard_color = BeardColor,
.drakkin_details = DrakkinDetails,
.drakkin_heritage = DrakkinHeritage,
.drakkin_tattoo = DrakkinTattoo,
.eye_color_one = EyeColor1,
.eye_color_two = EyeColor2,
.face = LuclinFace,
.gender_id = Gender,
.hair = HairStyle,
.hair_color = HairColor,
.helmet_texture = HelmTexture,
.race_id = Race,
.texture = Texture,
}
);
c->Message(Chat::White, "%s=%i", ChangeType, ChangeSetting);
+1 -1
View File
@@ -25,7 +25,7 @@ void command_npcedit(Client *c, const Seperator *sep)
auto npc_id = t->GetNPCTypeID();
auto npc_id_string = fmt::format(
"NPC ID {}",
Strings::Commify(std::to_string(npc_id))
npc_id
);
auto n = NpcTypesRepository::FindOne(content_db, npc_id);
+5
View File
@@ -18,6 +18,7 @@ void command_reload(Client *c, const Seperator *sep)
bool is_blocked_spells = !strcasecmp(sep->arg[1], "blocked_spells");
bool is_commands = !strcasecmp(sep->arg[1], "commands");
bool is_content_flags = !strcasecmp(sep->arg[1], "content_flags");
bool is_data_buckets = !strcasecmp(sep->arg[1], "data_buckets_cache");
bool is_doors = !strcasecmp(sep->arg[1], "doors");
bool is_dztemplates = !strcasecmp(sep->arg[1], "dztemplates");
bool is_ground_spawns = !strcasecmp(sep->arg[1], "ground_spawns");
@@ -46,6 +47,7 @@ void command_reload(Client *c, const Seperator *sep)
!is_blocked_spells &&
!is_commands &&
!is_content_flags &&
!is_data_buckets &&
!is_doors &&
!is_dztemplates &&
!is_ground_spawns &&
@@ -92,6 +94,9 @@ void command_reload(Client *c, const Seperator *sep)
} else if (is_doors) {
c->Message(Chat::White, "Attempting to reload Doors globally.");
pack = new ServerPacket(ServerOP_ReloadDoors, 0);
} else if (is_data_buckets) {
c->Message(Chat::White, "Attempting to flush data buckets cache globally.");
pack = new ServerPacket(ServerOP_ReloadDataBucketsCache, 0);
} else if (is_dztemplates) {
c->Message(Chat::White, "Attempting to reload Dynamic Zone Templates globally.");
pack = new ServerPacket(ServerOP_ReloadDzTemplates, 0);
+1 -1
View File
@@ -82,7 +82,7 @@ void command_set(Client *c, const Seperator *sep)
Cmd{.cmd = "frozen", .u = "frozen [on|off]", .fn = SetFrozen, .a = {"#freeze", "#unfreeze"}},
Cmd{.cmd = "gender", .u = "gender [Gender ID]", .fn = SetGender, .a = {"#gender"}},
Cmd{.cmd = "gender_permanent", .u = "gender_permanent [Gender ID]", .fn = SetGenderPermanent, .a = {"#permagender"}},
Cmd{.cmd = "gm", .u = "gm [on|off]", .fn = SetGM, .a = {"#flymode"}},
Cmd{.cmd = "gm", .u = "gm [on|off]", .fn = SetGM, .a = {"#gm"}},
Cmd{.cmd = "gm_speed", .u = "gm_speed [on|off]", .fn = SetGMSpeed, .a = {"#gmspeed"}},
Cmd{.cmd = "gm_status", .u = "gm_status [GM Status] [Account]", .fn = SetGMStatus, .a = {"#flag"}},
Cmd{.cmd = "god_mode", .u = "god_mode [on|off]", .fn = SetGodMode, .a = {"#godmode"}},
+5 -2
View File
@@ -22,8 +22,11 @@ void SetGender(Client *c, const Seperator *sep)
}
t->SendIllusionPacket(
t->GetRace(),
gender_id
AppearanceStruct{
.gender_id = gender_id,
.race_id = t->GetRace(),
.size = t->GetSize(),
}
);
c->Message(
+7 -1
View File
@@ -30,7 +30,13 @@ void SetGenderPermanent(Client *c, const Seperator *sep)
t->SetBaseGender(gender_id);
t->Save();
t->SendIllusionPacket(t->GetRace(), gender_id);
t->SendIllusionPacket(
AppearanceStruct{
.gender_id = gender_id,
.race_id = t->GetRace(),
.size = t->GetSize(),
}
);
c->Message(
Chat::White,
+6 -1
View File
@@ -37,7 +37,12 @@ void SetRace(Client *c, const Seperator *sep)
return;
}
t->SendIllusionPacket(race_id);
t->SendIllusionPacket(
AppearanceStruct{
.race_id = race_id,
.size = t->GetSize(),
}
);
c->Message(
Chat::White,
+7 -1
View File
@@ -31,7 +31,13 @@ void SetRacePermanent(Client *c, const Seperator *sep)
t->SetBaseRace(race_id);
t->SetBaseGender(gender_id);
t->Save();
t->SendIllusionPacket(race_id, gender_id);
t->SendIllusionPacket(
AppearanceStruct{
.gender_id = gender_id,
.race_id = race_id,
.size = t->GetSize()
}
);
c->Message(
Chat::White,
+8 -6
View File
@@ -8,8 +8,8 @@ void SetTexture(Client *c, const Seperator *sep)
return;
}
const uint16 texture = Strings::ToUnsignedInt(sep->arg[2]);
const uint8 helmet_texture = (
const uint8 texture = Strings::ToUnsignedInt(sep->arg[2]);
const uint8 helmet_texture = (
sep->IsNumber(3) ?
Strings::ToUnsignedInt(sep->arg[3]) :
0
@@ -30,10 +30,12 @@ void SetTexture(Client *c, const Seperator *sep)
}
} else { // Non-Player Races only need Illusion Packets to be sent for texture
t->SendIllusionPacket(
t->GetModel(),
t->GetGender(),
texture,
helmet_texture
AppearanceStruct{
.gender_id = t->GetGender(),
.helmet_texture = helmet_texture,
.race_id = t->GetModel(),
.texture = texture,
}
);
}
+1 -1
View File
@@ -26,7 +26,7 @@ void ShowGroupInfo(Client *c, const Seperator *sep)
popup_table += DialogueWindow::TableRow(
DialogueWindow::TableCell("Group ID") +
DialogueWindow::TableCell(Strings::Commify(g->GetID()))
DialogueWindow::TableCell(std::to_string(g->GetID()))
);
popup_table += DialogueWindow::TableRow(
+2 -2
View File
@@ -27,7 +27,7 @@ void ShowRecipe(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Recipe ID {} has no entries or could not be found.",
Strings::Commify(recipe_id)
recipe_id
).c_str()
);
return;
@@ -37,7 +37,7 @@ void ShowRecipe(Client *c, const Seperator *sep)
Chat::White,
fmt::format(
"Recipe {} | {}",
Strings::Commify(recipe_id),
recipe_id,
r[0].name
).c_str()
);
+1 -1
View File
@@ -34,7 +34,7 @@ void ShowTimers(Client *c, const Seperator *sep)
const uint32 remaining_time = e.second->GetRemainingTime();
if (remaining_time) {
popup_table += DialogueWindow::TableRow(
DialogueWindow::TableCell(Strings::Commify(e.first)) +
DialogueWindow::TableCell(std::to_string(e.first)) +
DialogueWindow::TableCell(Strings::SecondsToTime(remaining_time))
);
}
+29 -16
View File
@@ -2,15 +2,7 @@
void ShowZoneLoot(Client *c, const Seperator *sep)
{
if (!sep->IsNumber(2)) {
c->Message(
Chat::White,
"Usage: #show zone_loot [Item ID]"
);
return;
}
const uint32 search_item_id = Strings::ToUnsignedInt(sep->arg[2]);
const uint32 search_item_id = sep->IsNumber(2) ? Strings::ToUnsignedInt(sep->arg[2]) : 0;
std::vector<std::pair<NPC *, ItemList>> v;
@@ -44,7 +36,7 @@ void ShowZoneLoot(Client *c, const Seperator *sep)
);
npc_link = fmt::format(
"NPC: {} (ID {}) [{}]",
"{} (ID {}) | {}",
n->GetCleanName(),
n->GetID(),
command_link
@@ -60,11 +52,32 @@ void ShowZoneLoot(Client *c, const Seperator *sep)
c->Message(
Chat::White,
fmt::format(
"{}. {} ({}) {}",
"Item {} | {}{}{}",
loot_number,
linker.GenerateLink(),
Strings::Commify(i->item_id),
npc_link
(
!search_item_id ?
fmt::format(
"{} ({}) | ",
linker.GenerateLink(),
i->item_id
) :
""
),
npc_link,
(
!search_item_id ?
fmt::format(
" | {}",
Saylink::Silent(
fmt::format(
"#show zone_loot {}",
i->item_id
),
"Show"
)
) :
""
)
).c_str()
);
@@ -81,7 +94,7 @@ void ShowZoneLoot(Client *c, const Seperator *sep)
fmt::format(
"{} ({}) is dropping in {} place{}.",
database.CreateItemLink(search_item_id),
Strings::Commify(search_item_id),
search_item_id,
loot_count,
loot_count != 1 ? "s" : ""
).c_str()
@@ -93,7 +106,7 @@ void ShowZoneLoot(Client *c, const Seperator *sep)
c->Message(
Chat::White,
fmt::format(
"{} Item {} {} dropping.",
"{} Item{} {} dropping.",
loot_count,
loot_count != 1 ? "s" : "",
loot_count != 1 ? "are" : "is"
+2 -2
View File
@@ -63,8 +63,8 @@ void command_spawneditmass(Client *c, const Seperator *sep)
Chat::Yellow,
fmt::format(
"Spawn2 ID: {} NPC ID: {} Name: {} Respawn Time: {} ({})",
Strings::Commify(spawn2_id),
Strings::Commify(npc_id),
spawn2_id,
npc_id,
npc_name,
Strings::SecondsToTime(Strings::ToInt(respawn_time)),
Strings::Commify(respawn_time)
+42
View File
@@ -504,6 +504,42 @@ std::string Lua_Bot::GetRaceAbbreviation() {
return GetPlayerRaceAbbreviation(self->GetBaseRace());
}
void Lua_Bot::DeleteBucket(std::string bucket_name)
{
Lua_Safe_Call_Void();
self->DeleteBucket(bucket_name);
}
std::string Lua_Bot::GetBucket(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetBucket(bucket_name);
}
std::string Lua_Bot::GetBucketExpires(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetBucketExpires(bucket_name);
}
std::string Lua_Bot::GetBucketRemaining(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetBucketRemaining(bucket_name);
}
void Lua_Bot::SetBucket(std::string bucket_name, std::string bucket_value)
{
Lua_Safe_Call_Void();
self->SetBucket(bucket_name, bucket_value);
}
void Lua_Bot::SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration)
{
Lua_Safe_Call_Void();
self->SetBucket(bucket_name, bucket_value, expiration);
}
luabind::scope lua_register_bot() {
return luabind::class_<Lua_Bot, Lua_Mob>("Bot")
.def(luabind::constructor<>())
@@ -531,6 +567,7 @@ luabind::scope lua_register_bot() {
.def("Camp", (void(Lua_Bot::*)(bool))&Lua_Bot::Camp)
.def("CountBotItem", (uint32(Lua_Bot::*)(uint32))&Lua_Bot::CountBotItem)
.def("CountItemEquippedByID", (int(Lua_Bot::*)(uint32))&Lua_Bot::CountItemEquippedByID)
.def("DeleteBucket", (void(Lua_Bot::*)(std::string))&Lua_Bot::DeleteBucket)
.def("Escape", (void(Lua_Bot::*)(void))&Lua_Bot::Escape)
.def("Fling", (void(Lua_Bot::*)(float,float,float))&Lua_Bot::Fling)
.def("Fling", (void(Lua_Bot::*)(float,float,float,bool))&Lua_Bot::Fling)
@@ -551,6 +588,9 @@ luabind::scope lua_register_bot() {
.def("GetBotID", (uint32(Lua_Bot::*)(void))&Lua_Bot::GetBotID)
.def("GetBotItem", (Lua_ItemInst(Lua_Bot::*)(uint16))&Lua_Bot::GetBotItem)
.def("GetBotItemIDBySlot", (uint32(Lua_Bot::*)(uint16))&Lua_Bot::GetBotItemIDBySlot)
.def("GetBucket", (std::string(Lua_Bot::*)(std::string))&Lua_Bot::GetBucket)
.def("GetBucketExpires", (std::string(Lua_Bot::*)(std::string))&Lua_Bot::GetBucketExpires)
.def("GetBucketRemaining", (std::string(Lua_Bot::*)(std::string))&Lua_Bot::GetBucketRemaining)
.def("GetClassAbbreviation", (std::string(Lua_Bot::*)(void))&Lua_Bot::GetClassAbbreviation)
.def("GetExpansionBitmask", (int(Lua_Bot::*)(void))&Lua_Bot::GetExpansionBitmask)
.def("GetGroup", (Lua_Group(Lua_Bot::*)(void))&Lua_Bot::GetGroup)
@@ -576,6 +616,8 @@ luabind::scope lua_register_bot() {
.def("ReloadBotSpellSettings", (void(Lua_Bot::*)(void))&Lua_Bot::ReloadBotSpellSettings)
.def("RemoveBotItem", (void(Lua_Bot::*)(uint32))&Lua_Bot::RemoveBotItem)
.def("SendSpellAnim", (void(Lua_Bot::*)(uint16,uint16))&Lua_Bot::SendSpellAnim)
.def("SetBucket", (void(Lua_Bot::*)(std::string,std::string))&Lua_Bot::SetBucket)
.def("SetBucket", (void(Lua_Bot::*)(std::string,std::string,std::string))&Lua_Bot::SetBucket)
.def("SetExpansionBitmask", (void(Lua_Bot::*)(int))&Lua_Bot::SetExpansionBitmask)
.def("SetExpansionBitmask", (void(Lua_Bot::*)(int,bool))&Lua_Bot::SetExpansionBitmask)
.def("SetSpellDuration", (void(Lua_Bot::*)(int))&Lua_Bot::SetSpellDuration)
+6
View File
@@ -68,6 +68,12 @@ public:
void SendSpellAnim(uint16 target_id, uint16 spell_id);
std::string GetClassAbbreviation();
std::string GetRaceAbbreviation();
void DeleteBucket(std::string bucket_name);
std::string GetBucket(std::string bucket_name);
std::string GetBucketExpires(std::string bucket_name);
std::string GetBucketRemaining(std::string bucket_name);
void SetBucket(std::string bucket_name, std::string bucket_value);
void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration);
void ApplySpell(int spell_id);
void ApplySpell(int spell_id, int duration);
+42
View File
@@ -3098,6 +3098,42 @@ void Lua_Client::SetLDoNPoints(uint32 theme_id, uint32 points)
self->SetLDoNPoints(theme_id, points);
}
void Lua_Client::DeleteBucket(std::string bucket_name)
{
Lua_Safe_Call_Void();
self->DeleteBucket(bucket_name);
}
std::string Lua_Client::GetBucket(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetBucket(bucket_name);
}
std::string Lua_Client::GetBucketExpires(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetBucketExpires(bucket_name);
}
std::string Lua_Client::GetBucketRemaining(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetBucketRemaining(bucket_name);
}
void Lua_Client::SetBucket(std::string bucket_name, std::string bucket_value)
{
Lua_Safe_Call_Void();
self->SetBucket(bucket_name, bucket_value);
}
void Lua_Client::SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration)
{
Lua_Safe_Call_Void();
self->SetBucket(bucket_name, bucket_value, expiration);
}
luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client")
.def(luabind::constructor<>())
@@ -3175,6 +3211,7 @@ luabind::scope lua_register_client() {
.def("CreateExpeditionFromTemplate", &Lua_Client::CreateExpeditionFromTemplate)
.def("CreateTaskDynamicZone", &Lua_Client::CreateTaskDynamicZone)
.def("DecreaseByID", (bool(Lua_Client::*)(uint32,int))&Lua_Client::DecreaseByID)
.def("DeleteBucket", (void(Lua_Client::*)(std::string))&Lua_Client::DeleteBucket)
.def("DeleteItemInInventory", (void(Lua_Client::*)(int,int))&Lua_Client::DeleteItemInInventory)
.def("DeleteItemInInventory", (void(Lua_Client::*)(int,int,bool))&Lua_Client::DeleteItemInInventory)
.def("DiaWind", (void(Lua_Client::*)(std::string))&Lua_Client::DialogueWindow)
@@ -3249,6 +3286,9 @@ luabind::scope lua_register_client() {
.def("GetBotRequiredLevel", (int(Lua_Client::*)(uint8))&Lua_Client::GetBotRequiredLevel)
.def("GetBotSpawnLimit", (int(Lua_Client::*)(void))&Lua_Client::GetBotSpawnLimit)
.def("GetBotSpawnLimit", (int(Lua_Client::*)(uint8))&Lua_Client::GetBotSpawnLimit)
.def("GetBucket", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucket)
.def("GetBucketExpires", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucketExpires)
.def("GetBucketRemaining", (std::string(Lua_Client::*)(std::string))&Lua_Client::GetBucketRemaining)
.def("GetCarriedMoney", (uint64(Lua_Client::*)(void))&Lua_Client::GetCarriedMoney)
.def("GetCarriedPlatinum", (uint32(Lua_Client::*)(void))&Lua_Client::GetCarriedPlatinum)
.def("GetCharacterFactionLevel", (int(Lua_Client::*)(int))&Lua_Client::GetCharacterFactionLevel)
@@ -3517,6 +3557,8 @@ luabind::scope lua_register_client() {
.def("SetBotRequiredLevel", (void(Lua_Client::*)(int,uint8))&Lua_Client::SetBotRequiredLevel)
.def("SetBotSpawnLimit", (void(Lua_Client::*)(int))&Lua_Client::SetBotSpawnLimit)
.def("SetBotSpawnLimit", (void(Lua_Client::*)(int,uint8))&Lua_Client::SetBotSpawnLimit)
.def("SetBucket", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetBucket)
.def("SetBucketExpires", (void(Lua_Client::*)(std::string,std::string,std::string))&Lua_Client::SetBucket)
.def("SetClientMaxLevel", (void(Lua_Client::*)(int))&Lua_Client::SetClientMaxLevel)
.def("SetConsumption", (void(Lua_Client::*)(int, int))&Lua_Client::SetConsumption)
.def("SetDeity", (void(Lua_Client::*)(int))&Lua_Client::SetDeity)
+6
View File
@@ -474,6 +474,12 @@ public:
std::string GetClassAbbreviation();
std::string GetRaceAbbreviation();
void SetLDoNPoints(uint32 theme_id, uint32 points);
void DeleteBucket(std::string bucket_name);
std::string GetBucket(std::string bucket_name);
std::string GetBucketExpires(std::string bucket_name);
std::string GetBucketRemaining(std::string bucket_name);
void SetBucket(std::string bucket_name, std::string bucket_value);
void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration);
void ApplySpell(int spell_id);
void ApplySpell(int spell_id, int duration);
+2 -13
View File
@@ -641,9 +641,7 @@ Lua_Mob_List Lua_EntityList::GetCloseMobList(Lua_Mob mob) {
Lua_Mob_List ret;
const auto& l = self->GetCloseMobList(mob);
ret.entries.reserve(l.size());
for (const auto& e : l) {
ret.entries.emplace_back(Lua_Mob(e.second));
}
@@ -653,14 +651,9 @@ Lua_Mob_List Lua_EntityList::GetCloseMobList(Lua_Mob mob) {
Lua_Mob_List Lua_EntityList::GetCloseMobList(Lua_Mob mob, float distance) {
Lua_Safe_Call_Class(Lua_Mob_List);
Lua_Mob_List ret;
const auto& l = self->GetCloseMobList(mob);
ret.entries.reserve(l.size());
for (const auto& e : l) {
for (const auto& e : self->GetCloseMobList(mob)) {
if (mob.CalculateDistance(e.second) <= distance) {
ret.entries.emplace_back(Lua_Mob(e.second));
}
@@ -674,11 +667,7 @@ Lua_Mob_List Lua_EntityList::GetCloseMobList(Lua_Mob mob, float distance, bool i
Lua_Mob_List ret;
const auto& l = self->GetCloseMobList(mob);
ret.entries.reserve(l.size());
for (const auto& e : l) {
for (const auto& e : self->GetCloseMobList(mob)) {
if (ignore_self && e.second == mob) {
continue;
}
+50 -27
View File
@@ -1554,19 +1554,33 @@ void Lua_Mob::SetFlyMode(int in) {
self->SetFlyMode(static_cast<GravityBehavior>(in));
}
void Lua_Mob::SetTexture(int in) {
void Lua_Mob::SetTexture(uint8 texture) {
Lua_Safe_Call_Void();
self->SendIllusionPacket(self->GetRace(), 0xFF, in);
self->SendIllusionPacket(
AppearanceStruct{
.race_id = self->GetRace(),
.texture = texture
}
);
}
void Lua_Mob::SetRace(int in) {
void Lua_Mob::SetRace(uint16 race_id) {
Lua_Safe_Call_Void();
self->SendIllusionPacket(in);
self->SendIllusionPacket(
AppearanceStruct{
.race_id = race_id
}
);
}
void Lua_Mob::SetGender(int in) {
void Lua_Mob::SetGender(uint8 gender_id) {
Lua_Safe_Call_Void();
self->SendIllusionPacket(self->GetRace(), in);
self->SendIllusionPacket(
AppearanceStruct{
.gender_id = gender_id,
.race_id = self->GetRace(),
}
);
}
void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
@@ -1740,24 +1754,26 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
}
self->SendIllusionPacket(
race,
gender,
texture,
helmtexture,
haircolor,
beardcolor,
eyecolor1,
eyecolor2,
hairstyle,
luclinface,
beard,
aa_title,
drakkin_heritage,
drakkin_tattoo,
drakkin_details,
size,
send_appearance_effects,
target
AppearanceStruct{
.aa_title = aa_title,
.beard = beard,
.beard_color = beardcolor,
.drakkin_details = drakkin_details,
.drakkin_heritage = drakkin_heritage,
.drakkin_tattoo = drakkin_tattoo,
.eye_color_one = eyecolor1,
.eye_color_two = eyecolor2,
.face = luclinface,
.gender_id = gender,
.hair = hairstyle,
.hair_color = haircolor,
.helmet_texture = helmtexture,
.race_id = race,
.send_effects = send_appearance_effects,
.size = size,
.target = target,
.texture = texture,
}
);
}
@@ -3133,6 +3149,12 @@ bool Lua_Mob::IsTemporaryPet()
return self->IsTempPet();
}
uint32 Lua_Mob::GetMobTypeIdentifier()
{
Lua_Safe_Call_Int();
return self->GetMobTypeIdentifier();
}
luabind::scope lua_register_mob() {
return luabind::class_<Lua_Mob, Lua_Entity>("Mob")
.def(luabind::constructor<>())
@@ -3429,6 +3451,7 @@ luabind::scope lua_register_mob() {
.def("GetMeleeDamageMod_SE", &Lua_Mob::GetMeleeDamageMod_SE)
.def("GetMeleeMinDamageMod_SE", &Lua_Mob::GetMeleeMinDamageMod_SE)
.def("GetMeleeMitigation", (int32(Lua_Mob::*)(void))&Lua_Mob::GetMeleeMitigation)
.def("GetMobTypeIdentifier", (uint32(Lua_Mob::*)(void))&Lua_Mob::GetMobTypeIdentifier)
.def("GetModSkillDmgTaken", (int(Lua_Mob::*)(int))&Lua_Mob::GetModSkillDmgTaken)
.def("GetModVulnerability", (int(Lua_Mob::*)(int))&Lua_Mob::GetModVulnerability)
.def("GetNPCTypeID", &Lua_Mob::GetNPCTypeID)
@@ -3598,7 +3621,7 @@ luabind::scope lua_register_mob() {
.def("SetExtraHaste", (void(Lua_Mob::*)(int))&Lua_Mob::SetExtraHaste)
.def("SetFlurryChance", (void(Lua_Mob::*)(int))&Lua_Mob::SetFlurryChance)
.def("SetFlyMode", (void(Lua_Mob::*)(int))&Lua_Mob::SetFlyMode)
.def("SetGender", (void(Lua_Mob::*)(int))&Lua_Mob::SetGender)
.def("SetGender", (void(Lua_Mob::*)(uint8))&Lua_Mob::SetGender)
.def("SetGlobal", (void(Lua_Mob::*)(const char*,const char*,int,const char*))&Lua_Mob::SetGlobal)
.def("SetGlobal", (void(Lua_Mob::*)(const char*,const char*,int,const char*,Lua_Mob))&Lua_Mob::SetGlobal)
.def("SetHP", &Lua_Mob::SetHP)
@@ -3615,14 +3638,14 @@ luabind::scope lua_register_mob() {
.def("SetPet", &Lua_Mob::SetPet)
.def("SetPetOrder", (void(Lua_Mob::*)(int))&Lua_Mob::SetPetOrder)
.def("SetPseudoRoot", (void(Lua_Mob::*)(bool))&Lua_Mob::SetPseudoRoot)
.def("SetRace", (void(Lua_Mob::*)(int))&Lua_Mob::SetRace)
.def("SetRace", (void(Lua_Mob::*)(uint16))&Lua_Mob::SetRace)
.def("SetRunning", (void(Lua_Mob::*)(bool))&Lua_Mob::SetRunning)
.def("SetSlotTint", (void(Lua_Mob::*)(int,int,int,int))&Lua_Mob::SetSlotTint)
.def("SetSpecialAbility", (void(Lua_Mob::*)(int,int))&Lua_Mob::SetSpecialAbility)
.def("SetSpecialAbilityParam", (void(Lua_Mob::*)(int,int,int))&Lua_Mob::SetSpecialAbilityParam)
.def("SetTarget", &Lua_Mob::SetTarget)
.def("SetTargetable", (void(Lua_Mob::*)(bool))&Lua_Mob::SetTargetable)
.def("SetTexture", (void(Lua_Mob::*)(int))&Lua_Mob::SetTexture)
.def("SetTexture", (void(Lua_Mob::*)(uint8))&Lua_Mob::SetTexture)
.def("SetTimer", &Lua_Mob::SetTimer)
.def("SetTimerMS", &Lua_Mob::SetTimerMS)
.def("StopAllTimers", &Lua_Mob::StopAllTimers)
+4 -3
View File
@@ -351,9 +351,9 @@ public:
void SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5);
void SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5, Lua_Client specific_target);
void SetFlyMode(int in);
void SetTexture(int in);
void SetRace(int in);
void SetGender(int in);
void SetTexture(uint8 texture);
void SetRace(uint16 race_id);
void SetGender(uint8 gender_id);
void SendIllusionPacket(luabind::adl::object illusion);
void ChangeRace(int in);
void ChangeGender(int in);
@@ -557,6 +557,7 @@ public:
std::string GetClassPlural();
std::string GetRacePlural();
bool IsTemporaryPet();
uint32 GetMobTypeIdentifier();
};
#endif
+42
View File
@@ -777,6 +777,42 @@ bool Lua_NPC::HasSpecialAbilities() {
return self->HasSpecialAbilities();
}
void Lua_NPC::DeleteBucket(std::string bucket_name)
{
Lua_Safe_Call_Void();
self->DeleteBucket(bucket_name);
}
std::string Lua_NPC::GetBucket(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetBucket(bucket_name);
}
std::string Lua_NPC::GetBucketExpires(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetBucketExpires(bucket_name);
}
std::string Lua_NPC::GetBucketRemaining(std::string bucket_name)
{
Lua_Safe_Call_String();
return self->GetBucketRemaining(bucket_name);
}
void Lua_NPC::SetBucket(std::string bucket_name, std::string bucket_value)
{
Lua_Safe_Call_Void();
self->SetBucket(bucket_name, bucket_value);
}
void Lua_NPC::SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration)
{
Lua_Safe_Call_Void();
self->SetBucket(bucket_name, bucket_value, expiration);
}
luabind::scope lua_register_npc() {
return luabind::class_<Lua_NPC, Lua_Mob>("NPC")
.def(luabind::constructor<>())
@@ -804,12 +840,16 @@ luabind::scope lua_register_npc() {
.def("ClearLastName", (void(Lua_NPC::*)(void))&Lua_NPC::ClearLastName)
.def("CountItem", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::CountItem)
.def("CountLoot", (int(Lua_NPC::*)(void))&Lua_NPC::CountLoot)
.def("DeleteBucket", (void(Lua_NPC::*)(std::string))&Lua_NPC::DeleteBucket)
.def("DisplayWaypointInfo", (void(Lua_NPC::*)(Lua_Client))&Lua_NPC::DisplayWaypointInfo)
.def("DoClassAttacks", (void(Lua_NPC::*)(Lua_Mob))&Lua_NPC::DoClassAttacks)
.def("GetAccuracyRating", (int(Lua_NPC::*)(void))&Lua_NPC::GetAccuracyRating)
.def("GetAttackDelay", (int(Lua_NPC::*)(void))&Lua_NPC::GetAttackDelay)
.def("GetAttackSpeed", (float(Lua_NPC::*)(void))&Lua_NPC::GetAttackSpeed)
.def("GetAvoidanceRating", &Lua_NPC::GetAvoidanceRating)
.def("GetBucket", (std::string(Lua_NPC::*)(std::string))&Lua_NPC::GetBucket)
.def("GetBucketExpires", (std::string(Lua_NPC::*)(std::string))&Lua_NPC::GetBucketExpires)
.def("GetBucketRemaining", (std::string(Lua_NPC::*)(std::string))&Lua_NPC::GetBucketRemaining)
.def("GetCopper", (uint32(Lua_NPC::*)(void))&Lua_NPC::GetCopper)
.def("GetFirstSlotByItemID", (uint16(Lua_NPC::*)(uint32))&Lua_NPC::GetFirstSlotByItemID)
.def("GetFollowCanRun", (bool(Lua_NPC::*)(void))&Lua_NPC::GetFollowCanRun)
@@ -894,6 +934,8 @@ luabind::scope lua_register_npc() {
.def("ScaleNPC", (void(Lua_NPC::*)(uint8,bool))&Lua_NPC::ScaleNPC)
.def("SendPayload", (void(Lua_NPC::*)(int))&Lua_NPC::SendPayload)
.def("SendPayload", (void(Lua_NPC::*)(int,std::string))&Lua_NPC::SendPayload)
.def("SetBucket", (void(Lua_NPC::*)(std::string,std::string))&Lua_NPC::SetBucket)
.def("SetBucket", (void(Lua_NPC::*)(std::string,std::string,std::string))&Lua_NPC::SetBucket)
.def("SetCopper", (void(Lua_NPC::*)(uint32))&Lua_NPC::SetCopper)
.def("SetFollowCanRun", (void(Lua_NPC::*)(bool))&Lua_NPC::SetFollowCanRun)
.def("SetFollowDistance", (void(Lua_NPC::*)(int))&Lua_NPC::SetFollowDistance)
+6
View File
@@ -176,6 +176,12 @@ public:
void ScaleNPC(uint8 npc_level, bool override_special_abilities);
bool IsUnderwaterOnly();
bool HasSpecialAbilities();
void DeleteBucket(std::string bucket_name);
std::string GetBucket(std::string bucket_name);
std::string GetBucketExpires(std::string bucket_name);
std::string GetBucketRemaining(std::string bucket_name);
void SetBucket(std::string bucket_name, std::string bucket_value);
void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration);
};
#endif
+43 -51
View File
@@ -231,7 +231,7 @@ void handle_npc_hate(
l_mob_o.push(L);
lua_setfield(L, -2, "other");
lua_pushboolean(L, Strings::ToInt(data) == 0 ? false : true);
lua_pushboolean(L, Strings::ToBool(data));
lua_setfield(L, -2, "joined");
}
@@ -301,8 +301,8 @@ void handle_npc_death(
lua_pushinteger(L, Strings::ToInt(sep.arg[1]));
lua_setfield(L, -2, "damage");
int spell_id = Strings::ToInt(sep.arg[2]);
if(IsValidSpell(spell_id)) {
const uint32 spell_id = Strings::ToUnsignedInt(sep.arg[2]);
if (IsValidSpell(spell_id)) {
Lua_Spell l_spell(&spells[spell_id]);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
@@ -317,16 +317,14 @@ void handle_npc_death(
lua_pushinteger(L, Strings::ToInt(sep.arg[3]));
lua_setfield(L, -2, "skill_id");
if (extra_pointers && extra_pointers->size() >= 1)
{
if (extra_pointers && extra_pointers->size() >= 1) {
Lua_Corpse l_corpse(std::any_cast<Corpse*>(extra_pointers->at(0)));
luabind::adl::object l_corpse_o = luabind::adl::object(L, l_corpse);
l_corpse_o.push(L);
lua_setfield(L, -2, "corpse");
}
if (extra_pointers && extra_pointers->size() >= 2)
{
if (extra_pointers && extra_pointers->size() >= 2) {
Lua_NPC l_npc(std::any_cast<NPC*>(extra_pointers->at(1)));
luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc);
l_npc_o.push(L);
@@ -343,18 +341,19 @@ void handle_npc_cast(
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
int spell_id = Strings::ToInt(data);
if(IsValidSpell(spell_id)) {
Lua_Spell l_spell(&spells[spell_id]);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
lua_setfield(L, -2, "spell");
} else {
Lua_Spell l_spell(nullptr);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
lua_setfield(L, -2, "spell");
}
Seperator sep(data.c_str());
const uint32 spell_id = Strings::ToUnsignedInt(sep.arg[0]);
Lua_Spell l_spell(IsValidSpell(spell_id) ? &spells[spell_id] : nullptr);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
lua_setfield(L, -2, "spell");
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[1]));
lua_setfield(L, -2, "caster_id");
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[2]));
lua_setfield(L, -2, "caster_level");
}
void handle_npc_area(
@@ -462,16 +461,16 @@ void handle_npc_damage(
lua_pushnumber(L, Strings::ToInt(sep.arg[3]));
lua_setfield(L, -2, "skill_id");
lua_pushboolean(L, Strings::ToInt(sep.arg[4]) == 0 ? false : true);
lua_pushboolean(L, Strings::ToBool(sep.arg[4]));
lua_setfield(L, -2, "is_damage_shield");
lua_pushboolean(L, Strings::ToInt(sep.arg[5]) == 0 ? false : true);
lua_pushboolean(L, Strings::ToBool(sep.arg[5]));
lua_setfield(L, -2, "is_avoidable");
lua_pushnumber(L, Strings::ToInt(sep.arg[6]));
lua_setfield(L, -2, "buff_slot");
lua_pushboolean(L, Strings::ToInt(sep.arg[7]) == 0 ? false : true);
lua_pushboolean(L, Strings::ToBool(sep.arg[7]));
lua_setfield(L, -2, "is_buff_tic");
lua_pushnumber(L, Strings::ToInt(sep.arg[8]));
@@ -711,10 +710,10 @@ void handle_player_cast(
lua_setfield(L, -2, "spell");
lua_pushinteger(L, Strings::ToInt(sep.arg[1]));
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[1]));
lua_setfield(L, -2, "caster_id");
lua_pushinteger(L, Strings::ToInt(sep.arg[2]));
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[2]));
lua_setfield(L, -2, "caster_level");
}
@@ -933,7 +932,7 @@ void handle_player_respawn(
lua_pushinteger(L, Strings::ToInt(data));
lua_setfield(L, -2, "option");
lua_pushboolean(L, extra_data == 1 ? true : false);
lua_pushboolean(L, Strings::ToBool(std::to_string(extra_data)));
lua_setfield(L, -2, "resurrect");
}
@@ -950,7 +949,7 @@ void handle_player_packet(
l_packet_o.push(L);
lua_setfield(L, -2, "packet");
lua_pushboolean(L, extra_data == 1 ? true : false);
lua_pushboolean(L, Strings::ToBool(std::to_string(extra_data)));
lua_setfield(L, -2, "connecting");
}
@@ -1270,16 +1269,16 @@ void handle_player_damage(
lua_pushnumber(L, Strings::ToInt(sep.arg[3]));
lua_setfield(L, -2, "skill_id");
lua_pushboolean(L, Strings::ToInt(sep.arg[4]) == 0 ? false : true);
lua_pushboolean(L, Strings::ToBool(sep.arg[4]));
lua_setfield(L, -2, "is_damage_shield");
lua_pushboolean(L, Strings::ToInt(sep.arg[5]) == 0 ? false : true);
lua_pushboolean(L, Strings::ToBool(sep.arg[5]));
lua_setfield(L, -2, "is_avoidable");
lua_pushnumber(L, Strings::ToInt(sep.arg[6]));
lua_setfield(L, -2, "buff_slot");
lua_pushboolean(L, Strings::ToInt(sep.arg[7]) == 0 ? false : true);
lua_pushboolean(L, Strings::ToBool(sep.arg[7]));
lua_setfield(L, -2, "is_buff_tic");
lua_pushnumber(L, Strings::ToInt(sep.arg[8]));
@@ -1606,7 +1605,7 @@ void handle_spell_event(
Lua_Mob l_mob(mob);
luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob);
l_mob_o.push(L);
} else if(client) {
} else if (client) {
Lua_Mob l_client(client);
luabind::adl::object l_client_o = luabind::adl::object(L, l_client);
l_client_o.push(L);
@@ -1623,13 +1622,13 @@ void handle_spell_event(
Seperator sep(data.c_str());
lua_pushinteger(L, Strings::ToInt(sep.arg[0]));
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[0]));
lua_setfield(L, -2, "caster_id");
lua_pushinteger(L, Strings::ToInt(sep.arg[1]));
lua_setfield(L, -2, "tics_remaining");
lua_pushinteger(L, Strings::ToInt(sep.arg[2]));
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[2]));
lua_setfield(L, -2, "caster_level");
lua_pushinteger(L, Strings::ToInt(sep.arg[3]));
@@ -1655,7 +1654,7 @@ void handle_translocate_finish(
Lua_Mob l_mob(mob);
luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob);
l_mob_o.push(L);
} else if(client) {
} else if (client) {
Lua_Mob l_client(client);
luabind::adl::object l_client_o = luabind::adl::object(L, l_client);
l_client_o.push(L);
@@ -1945,23 +1944,16 @@ void handle_bot_cast(
) {
Seperator sep(data.c_str());
int spell_id = Strings::ToInt(sep.arg[0]);
if (IsValidSpell(spell_id)) {
Lua_Spell l_spell(&spells[spell_id]);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
} else {
Lua_Spell l_spell(nullptr);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
}
const uint32 spell_id = Strings::ToUnsignedInt(sep.arg[0]);
Lua_Spell l_spell(IsValidSpell(spell_id) ? &spells[spell_id] : nullptr);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
lua_setfield(L, -2, "spell");
lua_pushinteger(L, Strings::ToInt(sep.arg[1]));
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[1]));
lua_setfield(L, -2, "caster_id");
lua_pushinteger(L, Strings::ToInt(sep.arg[2]));
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[2]));
lua_setfield(L, -2, "caster_level");
}
@@ -1979,7 +1971,7 @@ void handle_bot_combat(
l_mob_o.push(L);
lua_setfield(L, -2, "other");
lua_pushboolean(L, Strings::ToInt(data) == 0 ? false : true);
lua_pushboolean(L, Strings::ToBool(data));
lua_setfield(L, -2, "joined");
}
@@ -2003,7 +1995,7 @@ void handle_bot_death(
lua_pushinteger(L, Strings::ToInt(sep.arg[1]));
lua_setfield(L, -2, "damage");
int spell_id = Strings::ToInt(sep.arg[2]);
const uint32 spell_id = Strings::ToUnsignedInt(sep.arg[2]);
if (IsValidSpell(spell_id)) {
Lua_Spell l_spell(&spells[spell_id]);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
@@ -2257,16 +2249,16 @@ void handle_bot_damage(
lua_pushnumber(L, Strings::ToInt(sep.arg[3]));
lua_setfield(L, -2, "skill_id");
lua_pushboolean(L, Strings::ToInt(sep.arg[4]) == 0 ? false : true);
lua_pushboolean(L, Strings::ToBool(sep.arg[4]));
lua_setfield(L, -2, "is_damage_shield");
lua_pushboolean(L, Strings::ToInt(sep.arg[5]) == 0 ? false : true);
lua_pushboolean(L, Strings::ToBool(sep.arg[5]));
lua_setfield(L, -2, "is_avoidable");
lua_pushnumber(L, Strings::ToInt(sep.arg[6]));
lua_setfield(L, -2, "buff_slot");
lua_pushboolean(L, Strings::ToInt(sep.arg[7]) == 0 ? false : true);
lua_pushboolean(L, Strings::ToBool(sep.arg[7]));
lua_setfield(L, -2, "is_buff_tic");
lua_pushnumber(L, Strings::ToInt(sep.arg[8]));
+2 -2
View File
@@ -4323,7 +4323,7 @@ Merc* Merc::LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id,
npc_type->race = merc_template->RaceID;
// Use the Gender and Size of the Merchant if possible
uint8 tmpgender = 0;
uint8 tmpgender = MALE;
float tmpsize = 6.0f;
if(merchant_id > 0)
{
@@ -5624,7 +5624,7 @@ void Client::SetMerc(Merc* newmerc) {
GetMercInfo().myTemplate = nullptr;
GetMercInfo().IsSuspended = false;
GetMercInfo().SuspendedTime = 0;
GetMercInfo().Gender = 0;
GetMercInfo().Gender = MALE;
GetMercInfo().State = 0;
memset(GetMercInfo().merc_name, 0, 64);
Log(Logs::General, Logs::Mercenaries, "SetMerc No Merc for %s.", GetName());
+173 -147
View File
@@ -2105,7 +2105,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
"{}: {}{}{}",
!faction_name.empty() ? faction_name : "Unknown Faction",
sign,
Strings::Commify(f.second),
f.second,
DialogueWindow::Break(1)
);
}
@@ -3475,7 +3475,7 @@ void Mob::ShowBuffs(Client* c) {
"{}{}{}{}{}",
DialogueWindow::TableCell(std::to_string(i)),
DialogueWindow::TableCell(GetSpellName(spell_id)),
DialogueWindow::TableCell(Strings::Commify(spell_id)),
DialogueWindow::TableCell(std::to_string(spell_id)),
DialogueWindow::TableCell(is_permanent ? "Permanent" : time),
DialogueWindow::TableCell(std::to_string(buffs[i].hit_number))
)
@@ -3518,135 +3518,100 @@ void Mob::GMMove(const glm::vec4 &position) {
}
}
void Mob::SendIllusionPacket(
uint16 in_race,
uint8 in_gender,
uint8 in_texture,
uint8 in_helmtexture,
uint8 in_haircolor,
uint8 in_beardcolor,
uint8 in_eyecolor1,
uint8 in_eyecolor2,
uint8 in_hairstyle,
uint8 in_luclinface,
uint8 in_beard,
uint8 in_aa_title,
uint32 in_drakkin_heritage,
uint32 in_drakkin_tattoo,
uint32 in_drakkin_details,
float in_size,
bool send_appearance_effects,
Client* target
)
void Mob::SendIllusionPacket(const AppearanceStruct& a)
{
uint8 new_texture = in_texture;
uint8 new_helmtexture = in_helmtexture;
uint8 new_haircolor;
uint8 new_beardcolor;
uint8 new_eyecolor1;
uint8 new_eyecolor2;
uint8 new_hairstyle;
uint8 new_luclinface;
uint8 new_beard;
uint8 new_aa_title;
uint32 new_drakkin_heritage;
uint32 new_drakkin_tattoo;
uint32 new_drakkin_details;
uint16 new_race = (
a.race_id != RACE_DOUG_0 ?
a.race_id :
(use_model ? use_model : GetBaseRace())
);
race = in_race;
if (race == 0) {
race = use_model ? use_model : GetBaseRace();
}
uint8 new_gender = (
a.gender_id != UINT8_MAX ?
a.gender_id :
(a.race_id ? GetDefaultGender(a.race_id, a.gender_id) : GetBaseGender())
);
if (in_gender != 0xFF) {
gender = in_gender;
}
else {
gender = in_race ? GetDefaultGender(race, gender) : GetBaseGender();
}
float new_size = a.size <= 0.0f ? GetRaceGenderDefaultHeight(race, gender) : a.size;
if (in_texture == 0xFF && !IsPlayerRace(race)) {
new_texture = GetTexture();
}
uint8 new_texture = a.texture == UINT8_MAX && !IsPlayerRace(a.race_id) ? GetTexture() : a.texture;
uint8 new_helmet_texture = a.helmet_texture == UINT8_MAX && !IsPlayerRace(race) ? GetHelmTexture() : a.helmet_texture;
if (in_helmtexture == 0xFF && !IsPlayerRace(race)) {
new_helmtexture = GetHelmTexture();
}
uint8 new_hair = a.hair == UINT8_MAX ? GetHairStyle() : a.hair;
uint8 new_hair_color = a.hair_color == UINT8_MAX ? GetHairColor() : a.hair_color;
new_haircolor = (in_haircolor == 0xFF) ? GetHairColor() : in_haircolor;
new_beardcolor = (in_beardcolor == 0xFF) ? GetBeardColor() : in_beardcolor;
new_eyecolor1 = (in_eyecolor1 == 0xFF) ? GetEyeColor1() : in_eyecolor1;
new_eyecolor2 = (in_eyecolor2 == 0xFF) ? GetEyeColor2() : in_eyecolor2;
new_hairstyle = (in_hairstyle == 0xFF) ? GetHairStyle() : in_hairstyle;
new_luclinface = (in_luclinface == 0xFF) ? GetLuclinFace() : in_luclinface;
new_beard = (in_beard == 0xFF) ? GetBeard() : in_beard;
new_drakkin_heritage = (in_drakkin_heritage == 0xFFFFFFFF) ? GetDrakkinHeritage() : in_drakkin_heritage;
new_drakkin_tattoo = (in_drakkin_tattoo == 0xFFFFFFFF) ? GetDrakkinTattoo() : in_drakkin_tattoo;
new_drakkin_details = (in_drakkin_details == 0xFFFFFFFF) ? GetDrakkinDetails() : in_drakkin_details;
new_aa_title = in_aa_title;
uint8 new_beard = a.beard == UINT8_MAX ? GetBeard() : a.beard;
uint8 new_beard_color = a.beard_color == UINT8_MAX ? GetBeardColor() : a.beard_color;
uint8 new_eye_color_one = a.eye_color_one == UINT8_MAX ? GetEyeColor1() : a.eye_color_one;
uint8 new_eye_color_two = a.eye_color_two == UINT8_MAX ? GetEyeColor2() : a.eye_color_two;
uint8 new_face = a.face == UINT8_MAX ? GetLuclinFace() : a.face;
uint32 new_drakkin_details = a.drakkin_details == UINT32_MAX ? GetDrakkinDetails() : a.drakkin_details;
uint32 new_drakkin_heritage = a.drakkin_heritage == UINT32_MAX ? GetDrakkinHeritage() : a.drakkin_heritage;
uint32 new_drakkin_tattoo = a.drakkin_tattoo == UINT32_MAX ? GetDrakkinTattoo() : a.drakkin_tattoo;
// Reset features to Base from the Player Profile
if (IsClient() && in_race == 0) {
race = CastToClient()->GetBaseRace();
gender = CastToClient()->GetBaseGender();
new_texture = texture = 0xFF;
new_helmtexture = helmtexture = 0xFF;
new_haircolor = haircolor = CastToClient()->GetBaseHairColor();
new_beardcolor = beardcolor = CastToClient()->GetBaseBeardColor();
new_eyecolor1 = eyecolor1 = CastToClient()->GetBaseEyeColor();
new_eyecolor2 = eyecolor2 = CastToClient()->GetBaseEyeColor();
new_hairstyle = hairstyle = CastToClient()->GetBaseHairStyle();
new_luclinface = luclinface = CastToClient()->GetBaseFace();
new_beard = beard = CastToClient()->GetBaseBeard();
new_aa_title = aa_title = 0xFF;
new_drakkin_heritage = drakkin_heritage = CastToClient()->GetBaseHeritage();
new_drakkin_tattoo = drakkin_tattoo = CastToClient()->GetBaseTattoo();
new_drakkin_details = drakkin_details = CastToClient()->GetBaseDetails();
if (IsClient() && a.race_id == RACE_DOUG_0) {
new_beard = CastToClient()->GetBaseBeard();
new_beard_color = CastToClient()->GetBaseBeardColor();
new_drakkin_details = CastToClient()->GetBaseDetails();
new_drakkin_heritage = CastToClient()->GetBaseHeritage();
new_drakkin_tattoo = CastToClient()->GetBaseTattoo();
new_eye_color_one = CastToClient()->GetBaseEyeColor();
new_eye_color_two = CastToClient()->GetBaseEyeColor();
new_face = CastToClient()->GetBaseFace();
new_gender = CastToClient()->GetBaseGender();
new_helmet_texture = UINT8_MAX;
new_hair = CastToClient()->GetBaseHairStyle();
new_hair_color = CastToClient()->GetBaseHairColor();
new_race = CastToClient()->GetBaseRace();
new_size = CastToClient()->GetSize();
new_texture = UINT8_MAX;
}
// update internal values for mob
size = (in_size <= 0.0f) ? GetRaceGenderDefaultHeight(race, gender) : in_size;
if (new_texture != 0xFF) {
texture = new_texture;
}
if (new_helmtexture != 0xFF) {
helmtexture = new_helmtexture;
}
haircolor = new_haircolor;
beardcolor = new_beardcolor;
eyecolor1 = new_eyecolor1;
eyecolor2 = new_eyecolor2;
hairstyle = new_hairstyle;
luclinface = new_luclinface;
beard = new_beard;
beardcolor = new_beard_color;
drakkin_heritage = new_drakkin_heritage;
drakkin_tattoo = new_drakkin_tattoo;
drakkin_details = new_drakkin_details;
eyecolor1 = new_eye_color_one;
eyecolor2 = new_eye_color_two;
luclinface = new_face;
gender = new_gender;
hairstyle = new_hair;
haircolor = new_hair_color;
helmtexture = new_helmet_texture;
race = new_race;
size = new_size;
texture = new_texture;
auto outapp = new EQApplicationPacket(OP_Illusion, sizeof(Illusion_Struct));
auto is = (Illusion_Struct *) outapp->pBuffer;
auto outapp = new EQApplicationPacket(OP_Illusion, sizeof(Illusion_Struct));
Illusion_Struct *is = (Illusion_Struct *) outapp->pBuffer;
is->spawnid = GetID();
strcpy(is->charname, GetCleanName());
is->race = race;
is->gender = gender;
is->texture = new_texture;
is->helmtexture = new_helmtexture;
is->haircolor = new_haircolor;
is->beardcolor = new_beardcolor;
strn0cpy(is->charname, GetCleanName(), sizeof(is->charname));
is->beardcolor = new_beard_color;
is->beard = new_beard;
is->eyecolor1 = new_eyecolor1;
is->eyecolor2 = new_eyecolor2;
is->hairstyle = new_hairstyle;
is->face = new_luclinface;
is->drakkin_heritage = new_drakkin_heritage;
is->drakkin_tattoo = new_drakkin_tattoo;
is->drakkin_details = new_drakkin_details;
is->size = size;
is->eyecolor1 = new_eye_color_one;
is->eyecolor2 = new_eye_color_two;
is->face = new_face;
is->gender = new_gender;
is->hairstyle = new_hair;
is->haircolor = new_hair_color;
is->helmtexture = new_helmet_texture;
is->race = new_race;
is->size = new_size;
is->texture = new_texture;
if (!target) {
if (!a.target) {
entity_list.QueueClients(this, outapp);
} else {
target->QueuePacket(outapp, false);
a.target->QueuePacket(outapp, false);
}
safe_delete(outapp);
@@ -3654,7 +3619,7 @@ void Mob::SendIllusionPacket(
/* Refresh armor and tints after send illusion packet */
SendArmorAppearance();
if (send_appearance_effects) {
if (a.send_effects) {
SendSavedAppearenceEffects(nullptr);
}
@@ -3663,17 +3628,17 @@ void Mob::SendIllusionPacket(
race,
gender,
new_texture,
new_helmtexture,
new_haircolor,
new_beardcolor,
new_eyecolor1,
new_eyecolor2,
new_hairstyle,
new_luclinface,
new_helmet_texture,
new_hair_color,
new_beard_color,
new_eye_color_one,
new_eye_color_two,
new_hair,
new_face,
new_drakkin_heritage,
new_drakkin_tattoo,
new_drakkin_details,
size,
new_size,
target ? target->GetCleanName() : "No Target"
);
}
@@ -3903,21 +3868,22 @@ bool Mob::RandomizeFeatures(bool send_illusion, bool set_variables)
if (send_illusion) {
SendIllusionPacket(
GetRace(),
current_gender,
new_texture,
new_helm_texture,
new_hair_color,
new_beard_color,
new_eye_color_one,
new_eye_color_two,
new_hair_style,
new_luclin_face,
new_beard,
0xFF,
new_drakkin_heritage,
new_drakkin_tattoo,
new_drakkin_details
AppearanceStruct{
.beard = new_beard,
.beard_color = new_beard_color,
.drakkin_details = new_drakkin_details,
.drakkin_heritage = new_drakkin_heritage,
.drakkin_tattoo = new_drakkin_tattoo,
.eye_color_one = new_eye_color_one,
.eye_color_two = new_eye_color_two,
.face = new_luclin_face,
.gender_id = current_gender,
.hair = new_hair_style,
.hair_color = new_hair_color,
.helmet_texture = new_helm_texture,
.race_id = GetRace(),
.texture = new_texture,
}
);
}
@@ -8153,21 +8119,23 @@ void Mob::CloneAppearance(Mob* other, bool clone_name)
}
SendIllusionPacket(
other->GetRace(),
other->GetGender(),
other->GetTexture(),
other->GetHelmTexture(),
other->GetHairColor(),
other->GetBeardColor(),
other->GetEyeColor1(),
other->GetEyeColor2(),
other->GetHairStyle(),
other->GetBeard(),
0xFF,
other->GetRace() == DRAKKIN ? other->GetDrakkinHeritage() : 0xFFFFFFFF,
other->GetRace() == DRAKKIN ? other->GetDrakkinTattoo() : 0xFFFFFFFF,
other->GetRace() == DRAKKIN ? other->GetDrakkinDetails() : 0xFFFFFFFF,
other->GetSize()
AppearanceStruct{
.beard = other->GetBeard(),
.beard_color = other->GetBeardColor(),
.drakkin_details = other->GetDrakkinDetails(),
.drakkin_heritage = other->GetDrakkinHeritage(),
.drakkin_tattoo = other->GetDrakkinTattoo(),
.eye_color_one = other->GetEyeColor1(),
.eye_color_two = other->GetEyeColor2(),
.face = other->GetLuclinFace(),
.gender_id = other->GetGender(),
.hair = other->GetHairStyle(),
.hair_color = other->GetHairColor(),
.helmet_texture = other->GetHelmTexture(),
.race_id = other->GetRace(),
.size = other->GetSize(),
.texture = other->GetTexture(),
}
);
for (
@@ -8352,3 +8320,61 @@ DataBucketKey Mob::GetScopedBucketKeys()
return k;
}
uint32 Mob::GetMobTypeIdentifier()
{
if (IsClient()) {
return CastToClient()->CharacterID();
} else if (IsNPC()) {
return GetNPCTypeID();
} else if (IsBot()) {
return CastToBot()->GetBotID();
}
return 0;
}
void Mob::HandleDoorOpen()
{
for (auto e : entity_list.GetDoorsList()) {
Doors *d = e.second;
if (d->GetKeyItem()) {
continue;
}
if (d->GetLockpick()) {
continue;
}
if (d->IsDoorOpen()) {
continue;
}
if (d->IsDoorBlacklisted()) {
continue;
}
// If the door is a trigger door, check if the trigger door is open
if (d->GetTriggerDoorID() > 0) {
auto td = entity_list.GetDoorsByDoorID(d->GetTriggerDoorID());
if (td) {
if (Strings::RemoveNumbers(d->GetDoorName()) != Strings::RemoveNumbers(td->GetDoorName())) {
continue;
}
}
}
if (d->GetDoorParam() > 0) {
continue;
}
float distance = DistanceSquared(m_Position, d->GetPosition());
float distance_scan_door_open = 20;
if (distance <= (distance_scan_door_open * distance_scan_door_open)) {
// Make sure we're opening a door within height relevance and not platforms above or below us
if (std::abs(m_Position.z - d->GetPosition().z) > 10) {
continue;
}
d->ForceOpen(this);
}
}
}
+26 -22
View File
@@ -69,6 +69,27 @@ enum class eSpecialAttacks : int {
ChaoticStab
};
struct AppearanceStruct {
uint8 aa_title = UINT8_MAX;
uint8 beard = UINT8_MAX;
uint8 beard_color = UINT8_MAX;
uint32 drakkin_details = UINT32_MAX;
uint32 drakkin_heritage = UINT32_MAX;
uint32 drakkin_tattoo = UINT32_MAX;
uint8 eye_color_one = UINT8_MAX;
uint8 eye_color_two = UINT8_MAX;
uint8 face = UINT8_MAX;
uint8 gender_id = UINT8_MAX;
uint8 hair = UINT8_MAX;
uint8 hair_color = UINT8_MAX;
uint8 helmet_texture = UINT8_MAX;
uint16 race_id = RACE_DOUG_0;
bool send_effects = true;
float size = -1.0f;
Client *target = nullptr;
uint8 texture = UINT8_MAX;
};
class DataBucketKey;
class Mob : public Entity {
public:
@@ -889,26 +910,7 @@ public:
int64 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false, uint16 casterid = 0, Mob *caster = nullptr);
uint8 IsFocusEffect(uint16 spellid, int effect_index, bool AA=false,uint32 aa_effect=0);
void SendIllusionPacket(
uint16 in_race,
uint8 in_gender = 0xFF,
uint8 in_texture = 0xFF,
uint8 in_helmtexture = 0xFF,
uint8 in_haircolor = 0xFF,
uint8 in_beardcolor = 0xFF,
uint8 in_eyecolor1 = 0xFF,
uint8 in_eyecolor2 = 0xFF,
uint8 in_hairstyle = 0xFF,
uint8 in_luclinface = 0xFF,
uint8 in_beard = 0xFF,
uint8 in_aa_title = 0xFF,
uint32 in_drakkin_heritage = 0xFFFFFFFF,
uint32 in_drakkin_tattoo = 0xFFFFFFFF,
uint32 in_drakkin_details = 0xFFFFFFFF,
float in_size = -1.0f,
bool send_appearance_effects = true,
Client* target = nullptr
);
void SendIllusionPacket(const AppearanceStruct& a);
void CloneAppearance(Mob* other, bool clone_name = false);
void SetFaceAppearance(const FaceChange_Struct& face, bool skip_sender = false);
bool RandomizeFeatures(bool send_illusion = true, bool set_variables = true);
@@ -1400,6 +1402,7 @@ public:
int64 GetHPRegen() const;
int64 GetHPRegenPerSecond() const;
int64 GetManaRegen() const;
int64 GetEnduranceRegen() const;
bool CanOpenDoors() const;
void SetCanOpenDoors(bool can_open);
@@ -1408,8 +1411,6 @@ public:
/// this cures timing issues cuz dead animation isn't done but server side feigning is?
inline bool GetFeigned() const { return(feigned); }
std::vector<DataBucketCache> m_data_bucket_cache;
// Data Bucket Methods
void DeleteBucket(std::string bucket_name);
std::string GetBucket(std::string bucket_name);
@@ -1417,6 +1418,8 @@ public:
std::string GetBucketRemaining(std::string bucket_name);
void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration = "");
uint32 GetMobTypeIdentifier();
// Heroic Stat Benefits
float CheckHeroicBonusesDataBuckets(std::string bucket_name);
@@ -1866,6 +1869,7 @@ private:
void SetHeroicWisBonuses(StatBonuses* n);
void DoSpellInterrupt(uint16 spell_id, int32 mana_cost, int my_curmana);
void HandleDoorOpen();
};
#endif
+1 -46
View File
@@ -981,52 +981,7 @@ void Mob::AI_Process() {
if (moving && CanOpenDoors()) {
if (AI_scan_door_open_timer->Check()) {
auto &door_list = entity_list.GetDoorsList();
for (auto itr : door_list) {
Doors *door = itr.second;
if (door->GetKeyItem()) {
continue;
}
if (door->GetLockpick()) {
continue;
}
if (door->IsDoorOpen()) {
continue;
}
if (door->GetTriggerDoorID() > 0) {
auto trigger_door = entity_list.GetDoorsByDoorID(door->GetTriggerDoorID());
if (trigger_door) {
if (Strings::RemoveNumbers(door->GetDoorName()) !=
Strings::RemoveNumbers(trigger_door->GetDoorName())) {
continue;
}
}
}
if (door->GetDoorParam() > 0) {
continue;
}
float distance = DistanceSquared(m_Position, door->GetPosition());
float distance_scan_door_open = 20;
if (distance <= (distance_scan_door_open * distance_scan_door_open)) {
/**
* Make sure we're opening a door within height relevance and not platforms
* above or below
*/
if (std::abs(m_Position.z - door->GetPosition().z) > 10) {
continue;
}
door->ForceOpen(this);
}
}
HandleDoorOpen();
}
}
+5 -19
View File
@@ -1219,7 +1219,7 @@ void NPC::SpawnGridNodeNPC(const glm::vec4 &position, int32 grid_id, int32 grid_
npc_type->current_hp = 4000000;
npc_type->max_hp = 4000000;
npc_type->race = 2254;
npc_type->gender = 2;
npc_type->gender = NEUTER;
npc_type->class_ = 9;
npc_type->deity = 1;
npc_type->level = 200;
@@ -2583,30 +2583,16 @@ void NPC::ModifyNPCStat(const std::string& stat, const std::string& value)
}
else if (stat_lower == "min_hit") {
min_dmg = Strings::ToInt(value);
// TODO: fix DB
if (min_dmg > max_dmg) {
const auto temporary_damage = max_dmg;
max_dmg = min_dmg;
min_dmg = temporary_damage;
}
// Clamp max_dmg to be >= min_dmg
max_dmg = std::max(min_dmg, max_dmg);
base_damage = round((max_dmg - min_dmg) / 1.9);
min_damage = min_dmg - round(base_damage / 10.0);
return;
}
else if (stat_lower == "max_hit") {
max_dmg = Strings::ToInt(value);
// TODO: fix DB
if (max_dmg < min_dmg) {
const auto temporary_damage = min_dmg;
min_dmg = max_dmg;
max_dmg = temporary_damage;
}
// Clamp min_dmg to be <= max_dmg
min_dmg = std::min(min_dmg, max_dmg);
base_damage = round((max_dmg - min_dmg) / 1.9);
min_damage = min_dmg - round(base_damage / 10.0);
return;
+9 -16
View File
@@ -135,31 +135,24 @@ void NpcScaleManager::ScaleNPC(
npc->ModifyNPCStat("phr", std::to_string(scale_data.physical_resist));
}
auto min_damage_set = false;
// If either is scaled, both need to be. The values for base_damage and min_damage will be in flux until
// both are complete.
if (always_scale || npc->GetMinDMG() == 0) {
if (always_scale || npc->GetMinDMG() == 0 || npc->GetMaxDMG() == 0) {
int64 min_dmg = scale_data.min_dmg;
int64 max_dmg = scale_data.max_dmg;
if (RuleB(Combat, UseNPCDamageClassLevelMods)) {
uint32 class_level_damage_mod = GetClassLevelDamageMod(npc->GetLevel(), npc->GetClass());
min_dmg = (min_dmg * class_level_damage_mod) / 220;
LogNPCScaling("ClassLevelDamageMod::min_dmg base: [{}] calc: [{}]", scale_data.min_dmg, min_dmg);
max_dmg = (max_dmg * class_level_damage_mod) / 220;
}
npc->ModifyNPCStat("min_hit", std::to_string(min_dmg));
min_damage_set = true;
}
if (always_scale || npc->GetMaxDMG() == 0 || min_damage_set) {
int64 max_dmg = scale_data.max_dmg;
if (RuleB(Combat, UseNPCDamageClassLevelMods)) {
uint32 class_level_damage_mod = GetClassLevelDamageMod(npc->GetLevel(), npc->GetClass());
max_dmg = (scale_data.max_dmg * class_level_damage_mod) / 220;
LogNPCScaling("ClassLevelDamageMod::max_dmg base: [{}] calc: [{}]", scale_data.max_dmg, max_dmg);
}
npc->ModifyNPCStat("max_hit", std::to_string(max_dmg));
LogNPCScaling("ClassLevelDamageMod::min_dmg base: [{}] calc: [{}]", scale_data.min_dmg, min_dmg);
LogNPCScaling("ClassLevelDamageMod::max_dmg base: [{}] calc: [{}]", scale_data.max_dmg, max_dmg);
}
if (always_scale || (npc->GetHPRegen() == 0 && is_auto_scaled)) {
+1 -1
View File
@@ -529,7 +529,7 @@ void PathfinderWaypoint::ShowNode(const Node &n) {
npc_type->current_hp = 4000000;
npc_type->max_hp = 4000000;
npc_type->race = 2254;
npc_type->gender = 2;
npc_type->gender = NEUTER;
npc_type->class_ = 9;
npc_type->deity = 1;
npc_type->level = 75;
+2 -15
View File
@@ -614,11 +614,8 @@ Bot* Perl_EntityList_GetRandomBot(EntityList* self, float x, float y, float z, f
perl::array Perl_EntityList_GetCloseMobList(EntityList* self, Mob* mob)
{
perl::array result;
const auto& l = self->GetCloseMobList(mob);
result.reserve(l.size());
for (const auto& e : l) {
result.push_back(e.second);
}
@@ -629,12 +626,7 @@ perl::array Perl_EntityList_GetCloseMobList(EntityList* self, Mob* mob)
perl::array Perl_EntityList_GetCloseMobList(EntityList* self, Mob* mob, float distance)
{
perl::array result;
const auto& l = self->GetCloseMobList(mob, distance);
result.reserve(l.size());
for (const auto& e : l) {
for (const auto& e : self->GetCloseMobList(mob, distance)) {
if (mob->CalculateDistance(e.second) <= distance) {
result.push_back(e.second);
}
@@ -646,12 +638,7 @@ perl::array Perl_EntityList_GetCloseMobList(EntityList* self, Mob* mob, float di
perl::array Perl_EntityList_GetCloseMobList(EntityList* self, Mob* mob, float distance, bool ignore_self)
{
perl::array result;
const auto& l = self->GetCloseMobList(mob, distance);
result.reserve(l.size());
for (const auto& e : l) {
for (const auto& e : self->GetCloseMobList(mob, distance)) {
if (ignore_self && e.second == mob) {
continue;
}
+378 -74
View File
@@ -1809,10 +1809,26 @@ void Perl_Mob_SendAppearanceEffectGround(Mob* self, int32 parm1, int32 parm2, in
void Perl_Mob_RemoveAllAppearanceEffects(Mob* self) // @categories Script Utility
{
self->SendIllusionPacket(self->GetRace(), self->GetGender(), self->GetTexture(), self->GetHelmTexture(),
self->GetHairColor(), self->GetBeardColor(), self->GetEyeColor1(), self->GetEyeColor2(),
self->GetHairStyle(), self->GetLuclinFace(), self->GetBeard(), 0xFF,
self->GetDrakkinHeritage(), self->GetDrakkinTattoo(), self->GetDrakkinDetails(), self->GetSize(), false);
self->SendIllusionPacket(
AppearanceStruct{
.beard = self->GetBeard(),
.beard_color = self->GetBeardColor(),
.drakkin_details = self->GetDrakkinDetails(),
.drakkin_heritage = self->GetDrakkinHeritage(),
.drakkin_tattoo = self->GetDrakkinTattoo(),
.eye_color_one = self->GetEyeColor1(),
.eye_color_two = self->GetEyeColor2(),
.face = self->GetLuclinFace(),
.gender_id = self->GetGender(),
.hair = self->GetHairStyle(),
.hair_color = self->GetHairColor(),
.helmet_texture = self->GetHelmTexture(),
.race_id = self->GetRace(),
.send_effects = false,
.size = self->GetSize(),
.texture = self->GetTexture(),
}
);
self->ClearAppearenceEffects();
}
@@ -1821,134 +1837,416 @@ void Perl_Mob_SetFlyMode(Mob* self, int flymode) // @categories Script Utility
self->SetFlyMode(static_cast<GravityBehavior>(flymode));
}
void Perl_Mob_SetTexture(Mob* self, int32 texture) // @categories Stats and Attributes
void Perl_Mob_SetTexture(Mob* self, uint8 texture) // @categories Stats and Attributes
{
self->SendIllusionPacket(self->GetRace(), 0xFF, texture);
self->SendIllusionPacket(
AppearanceStruct{
.race_id = self->GetRace(),
.texture = texture,
}
);
}
void Perl_Mob_SetRace(Mob* self, int32 race) // @categories Stats and Attributes
void Perl_Mob_SetRace(Mob* self, uint16 race_id) // @categories Stats and Attributes
{
self->SendIllusionPacket(race);
self->SendIllusionPacket(
AppearanceStruct{
.race_id = race_id,
}
);
}
void Perl_Mob_SetGender(Mob* self, int32 gender) // @categories Stats and Attributes
void Perl_Mob_SetGender(Mob* self, uint8 gender_id) // @categories Stats and Attributes
{
self->SendIllusionPacket(self->GetRace(), gender);
self->SendIllusionPacket(
AppearanceStruct{
.gender_id = gender_id,
.race_id = self->GetRace(),
}
);
}
// todo: SendIllusion should be sent in a hash like lua
void Perl_Mob_SendIllusion(Mob* self, uint16 race) // @categories Script Utility
void Perl_Mob_SendIllusion(Mob *self, uint16 race_id) // @categories Script Utility
{
self->SendIllusionPacket(race);
self->SendIllusionPacket(
AppearanceStruct{
.race_id = race_id,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender) // @categories Script Utility
void Perl_Mob_SendIllusion(Mob *self, uint16 race_id, uint8 gender_id) // @categories Script Utility
{
self->SendIllusionPacket(race, gender);
self->SendIllusionPacket(
AppearanceStruct{
.gender_id = gender_id,
.race_id = race_id,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender, uint8 texture) // @categories Script Utility
void Perl_Mob_SendIllusion(Mob *self, uint16 race_id, uint8 gender_id, uint8 texture) // @categories Script Utility
{
self->SendIllusionPacket(race, gender, texture);
self->SendIllusionPacket(
AppearanceStruct{
.gender_id = gender_id,
.race_id = race_id,
.texture = texture,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender, uint8 texture, uint8 helmtexture) // @categories Script Utility
void Perl_Mob_SendIllusion(
Mob *self,
uint16 race_id,
uint8 gender_id,
uint8 texture,
uint8 helmet_texture
) // @categories Script Utility
{
self->SendIllusionPacket(race, gender, texture, helmtexture);
self->SendIllusionPacket(
AppearanceStruct{
.gender_id = gender_id,
.helmet_texture = helmet_texture,
.race_id = race_id,
.texture = texture,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender, uint8 texture, uint8 helmtexture, uint8 face) // @categories Script Utility
void Perl_Mob_SendIllusion(
Mob *self,
uint16 race_id,
uint8 gender_id,
uint8 texture,
uint8 helmet_texture,
uint8 face
) // @categories Script Utility
{
self->SendIllusionPacket(race, gender, texture, helmtexture, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, face);
self->SendIllusionPacket(
AppearanceStruct{
.face = face,
.gender_id = gender_id,
.helmet_texture = helmet_texture,
.race_id = race_id,
.texture = texture,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender, uint8 texture, uint8 helmtexture, uint8 face, uint8 hairstyle) // @categories Script Utility
void Perl_Mob_SendIllusion(
Mob *self,
uint16 race_id,
uint8 gender_id,
uint8 texture,
uint8 helmet_texture,
uint8 face,
uint8 hair
) // @categories Script Utility
{
self->SendIllusionPacket(race, gender, texture, helmtexture, 0xFF, 0xFF, 0xFF, 0xFF, hairstyle, face);
self->SendIllusionPacket(
AppearanceStruct{
.face = face,
.gender_id = gender_id,
.hair = hair,
.helmet_texture = helmet_texture,
.race_id = race_id,
.texture = texture,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender, uint8 texture, uint8 helmtexture, uint8 face, uint8 hairstyle, uint8 haircolor) // @categories Script Utility
void Perl_Mob_SendIllusion(
Mob *self,
uint16 race_id,
uint8 gender_id,
uint8 texture,
uint8 helmet_texture,
uint8 face,
uint8 hair,
uint8 hair_color
) // @categories Script Utility
{
self->SendIllusionPacket(race, gender, texture, helmtexture, haircolor, 0xFF, 0xFF, 0xFF, hairstyle, face);
self->SendIllusionPacket(
AppearanceStruct{
.face = face,
.gender_id = gender_id,
.hair = hair,
.hair_color = hair_color,
.helmet_texture = helmet_texture,
.race_id = race_id,
.texture = texture,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender, uint8 texture, uint8 helmtexture, uint8 face, uint8 hairstyle, uint8 haircolor, uint8 beard) // @categories Script Utility
void Perl_Mob_SendIllusion(
Mob *self,
uint16 race_id,
uint8 gender_id,
uint8 texture,
uint8 helmet_texture,
uint8 face,
uint8 hair,
uint8 hair_color,
uint8 beard
) // @categories Script Utility
{
self->SendIllusionPacket(race, gender, texture, helmtexture, haircolor, 0xFF, 0xFF, 0xFF, hairstyle, face, beard);
self->SendIllusionPacket(
AppearanceStruct{
.beard = beard,
.face = face,
.gender_id = gender_id,
.hair = hair,
.hair_color = hair_color,
.helmet_texture = helmet_texture,
.race_id = race_id,
.texture = texture,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender, uint8 texture, uint8 helmtexture, uint8 face, uint8 hairstyle, uint8 haircolor, uint8 beard, uint8 beardcolor) // @categories Script Utility
void Perl_Mob_SendIllusion(
Mob *self,
uint16 race_id,
uint8 gender_id,
uint8 texture,
uint8 helmet_texture,
uint8 face,
uint8 hair,
uint8 hair_color,
uint8 beard,
uint8 beard_color
) // @categories Script Utility
{
self->SendIllusionPacket(race, gender, texture, helmtexture, haircolor, beardcolor, 0xFF, 0xFF, hairstyle, face, beard);
self->SendIllusionPacket(
AppearanceStruct{
.beard = beard,
.beard_color = beard_color,
.face = face,
.gender_id = gender_id,
.hair = hair,
.hair_color = hair_color,
.helmet_texture = helmet_texture,
.race_id = race_id,
.texture = texture,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender, uint8 texture, uint8 helmtexture, uint8 face, uint8 hairstyle, uint8 haircolor, uint8 beard, uint8 beardcolor, uint32 drakkin_heritage) // @categories Script Utility
void Perl_Mob_SendIllusion(
Mob *self,
uint16 race_id,
uint8 gender_id,
uint8 texture,
uint8 helmet_texture,
uint8 face,
uint8 hair,
uint8 hair_color,
uint8 beard,
uint8 beard_color,
uint32 drakkin_heritage
) // @categories Script Utility
{
self->SendIllusionPacket(race, gender, texture, helmtexture, haircolor, beardcolor, 0xFF, 0xFF, hairstyle, face, beard, 0xFF, drakkin_heritage);
self->SendIllusionPacket(
AppearanceStruct{
.beard = beard,
.beard_color = beard_color,
.drakkin_heritage = drakkin_heritage,
.face = face,
.gender_id = gender_id,
.hair = hair,
.hair_color = hair_color,
.helmet_texture = helmet_texture,
.race_id = race_id,
.texture = texture,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender, uint8 texture, uint8 helmtexture, uint8 face, uint8 hairstyle, uint8 haircolor, uint8 beard, uint8 beardcolor, uint32 drakkin_heritage, uint32 drakkin_tattoo) // @categories Script Utility
void Perl_Mob_SendIllusion(
Mob *self,
uint16 race_id,
uint8 gender_id,
uint8 texture,
uint8 helmet_texture,
uint8 face,
uint8 hair,
uint8 hair_color,
uint8 beard,
uint8 beard_color,
uint32 drakkin_heritage,
uint32 drakkin_tattoo
) // @categories Script Utility
{
self->SendIllusionPacket(race, gender, texture, helmtexture, haircolor, beardcolor, 0xFF, 0xFF, hairstyle, face, beard, 0xFF, drakkin_heritage, drakkin_tattoo);
self->SendIllusionPacket(
AppearanceStruct{
.beard = beard,
.beard_color = beard_color,
.drakkin_heritage = drakkin_heritage,
.drakkin_tattoo = drakkin_tattoo,
.face = face,
.gender_id = gender_id,
.hair = hair,
.hair_color = hair_color,
.helmet_texture = helmet_texture,
.race_id = race_id,
.texture = texture,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender, uint8 texture, uint8 helmtexture, uint8 face, uint8 hairstyle, uint8 haircolor, uint8 beard, uint8 beardcolor, uint32 drakkin_heritage, uint32 drakkin_tattoo, uint32 drakkin_details) // @categories Script Utility
void Perl_Mob_SendIllusion(
Mob *self,
uint16 race_id,
uint8 gender_id,
uint8 texture,
uint8 helmet_texture,
uint8 face,
uint8 hair,
uint8 hair_color,
uint8 beard,
uint8 beard_color,
uint32 drakkin_heritage,
uint32 drakkin_tattoo,
uint32 drakkin_details
) // @categories Script Utility
{
self->SendIllusionPacket(race, gender, texture, helmtexture, haircolor, beardcolor, 0xFF, 0xFF, hairstyle, face, beard, 0xFF, drakkin_heritage, drakkin_tattoo, drakkin_details);
self->SendIllusionPacket(
AppearanceStruct{
.beard = beard,
.beard_color = beard_color,
.drakkin_details = drakkin_details,
.drakkin_heritage = drakkin_heritage,
.drakkin_tattoo = drakkin_tattoo,
.face = face,
.gender_id = gender_id,
.hair = hair,
.hair_color = hair_color,
.helmet_texture = helmet_texture,
.race_id = race_id,
.texture = texture,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender, uint8 texture, uint8 helmtexture, uint8 face, uint8 hairstyle, uint8 haircolor, uint8 beard, uint8 beardcolor, uint32 drakkin_heritage, uint32 drakkin_tattoo, uint32 drakkin_details, float size) // @categories Script Utility
void Perl_Mob_SendIllusion(
Mob *self,
uint16 race_id,
uint8 gender_id,
uint8 texture,
uint8 helmet_texture,
uint8 face,
uint8 hair,
uint8 hair_color,
uint8 beard,
uint8 beard_color,
uint32 drakkin_heritage,
uint32 drakkin_tattoo,
uint32 drakkin_details,
float size
) // @categories Script Utility
{
self->SendIllusionPacket(race, gender, texture, helmtexture, haircolor, beardcolor, 0xFF, 0xFF, hairstyle, face, beard, 0xFF, drakkin_heritage, drakkin_tattoo, drakkin_details, size);
self->SendIllusionPacket(
AppearanceStruct{
.beard = beard,
.beard_color = beard_color,
.drakkin_details = drakkin_details,
.drakkin_heritage = drakkin_heritage,
.drakkin_tattoo = drakkin_tattoo,
.face = face,
.gender_id = gender_id,
.hair = hair,
.hair_color = hair_color,
.helmet_texture = helmet_texture,
.race_id = race_id,
.size = size,
.texture = texture,
}
);
}
void Perl_Mob_SendIllusion(Mob* self, uint16 race, uint8 gender, uint8 texture, uint8 helmtexture, uint8 face, uint8 hairstyle, uint8 haircolor, uint8 beard, uint8 beardcolor, uint32 drakkin_heritage, uint32 drakkin_tattoo, uint32 drakkin_details, float size, Client* target) // @categories Script Utility
void Perl_Mob_SendIllusion(
Mob *self,
uint16 race_id,
uint8 gender_id,
uint8 texture,
uint8 helmet_texture,
uint8 face,
uint8 hair,
uint8 hair_color,
uint8 beard,
uint8 beard_color,
uint32 drakkin_heritage,
uint32 drakkin_tattoo,
uint32 drakkin_details,
float size,
Client *target
) // @categories Script Utility
{
self->SendIllusionPacket(race, gender, texture, helmtexture, haircolor, beardcolor, 0xFF, 0xFF, hairstyle, face, beard, 0xFF, drakkin_heritage, drakkin_tattoo, drakkin_details, size, true, target);
self->SendIllusionPacket(
AppearanceStruct{
.beard = beard,
.beard_color = beard_color,
.drakkin_details = drakkin_details,
.drakkin_heritage = drakkin_heritage,
.drakkin_tattoo = drakkin_tattoo,
.face = face,
.gender_id = gender_id,
.hair = hair,
.hair_color = hair_color,
.helmet_texture = helmet_texture,
.race_id = race_id,
.size = size,
.target = target,
.texture = texture,
}
);
}
void Perl_Mob_SendIllusionPacket(Mob* self, perl::reference table_ref)
{
perl::hash table = table_ref;
uint16 race = table.exists("race") ? table["race"] : self->GetRace();
uint8 gender = table.exists("gender") ? table["gender"] : self->GetGender();
uint8 texture = table.exists("texture") ? table["texture"] : self->GetTexture();
uint8 helmtexture = table.exists("helmtexture") ? table["helmtexture"] : self->GetHelmTexture();
uint8 haircolor = table.exists("haircolor") ? table["haircolor"] : self->GetHairColor();
uint8 beardcolor = table.exists("beardcolor") ? table["beardcolor"] : self->GetBeardColor();
uint8 eyecolor1 = table.exists("eyecolor1") ? table["eyecolor1"] : self->GetEyeColor1();
uint8 eyecolor2 = table.exists("eyecolor2") ? table["eyecolor2"] : self->GetEyeColor2();
uint8 hairstyle = table.exists("hairstyle") ? table["hairstyle"] : self->GetHairStyle();
uint8 luclinface = table.exists("luclinface") ? table["luclinface"] : self->GetLuclinFace();
uint8 beard = table.exists("beard") ? table["beard"] : self->GetBeard();
uint8 aa_title = table.exists("aa_title") ? table["aa_title"] : 255;
uint32 drakkin_heritage = table.exists("drakkin_heritage") ? table["drakkin_heritage"] : self->GetDrakkinHeritage();
uint32 drakkin_tattoo = table.exists("drakkin_tattoo") ? table["drakkin_tattoo"] : self->GetDrakkinTattoo();
uint32 drakkin_details = table.exists("drakkin_details") ? table["drakkin_details"] : self->GetDrakkinDetails();
float size = table.exists("size") ? table["size"] : self->GetSize();
bool send_appearance_effects = table.exists("send_appearance_effects") ? table["send_appearance_effects"] : true;
Client* target = table.exists("target") ? static_cast<Client *>(table["target"]) : nullptr;
uint16 race_id = table.exists("race") ? table["race"] : self->GetRace();
uint8 gender_id = table.exists("gender") ? table["gender"] : self->GetGender();
uint8 texture = table.exists("texture") ? table["texture"] : self->GetTexture();
uint8 helmet_texture = table.exists("helmtexture") ? table["helmtexture"] : self->GetHelmTexture();
uint8 hair_color = table.exists("haircolor") ? table["haircolor"] : self->GetHairColor();
uint8 beard_color = table.exists("beardcolor") ? table["beardcolor"] : self->GetBeardColor();
uint8 eye_color_one = table.exists("eyecolor1") ? table["eyecolor1"] : self->GetEyeColor1();
uint8 eye_color_two = table.exists("eyecolor2") ? table["eyecolor2"] : self->GetEyeColor2();
uint8 hair = table.exists("hairstyle") ? table["hairstyle"] : self->GetHairStyle();
uint8 face = table.exists("luclinface") ? table["luclinface"] : self->GetLuclinFace();
uint8 beard = table.exists("beard") ? table["beard"] : self->GetBeard();
uint8 aa_title = table.exists("aa_title") ? table["aa_title"] : 255;
uint32 drakkin_heritage = table.exists("drakkin_heritage") ? table["drakkin_heritage"] : self->GetDrakkinHeritage();
uint32 drakkin_tattoo = table.exists("drakkin_tattoo") ? table["drakkin_tattoo"] : self->GetDrakkinTattoo();
uint32 drakkin_details = table.exists("drakkin_details") ? table["drakkin_details"] : self->GetDrakkinDetails();
float size = table.exists("size") ? table["size"] : self->GetSize();
bool send_appearance_effects = table.exists("send_appearance_effects") ? table["send_appearance_effects"] : true;
Client *target = table.exists("target") ? static_cast<Client *>(table["target"]) : nullptr;
self->SendIllusionPacket(
race,
gender,
texture,
helmtexture,
haircolor,
beardcolor,
eyecolor1,
eyecolor2,
hairstyle,
luclinface,
beard,
aa_title,
drakkin_heritage,
drakkin_tattoo,
drakkin_details,
size,
send_appearance_effects,
target
AppearanceStruct{
.beard = beard,
.beard_color = beard_color,
.drakkin_details = drakkin_details,
.drakkin_heritage = drakkin_heritage,
.drakkin_tattoo = drakkin_tattoo,
.face = face,
.gender_id = gender_id,
.hair = hair,
.hair_color = hair_color,
.helmet_texture = helmet_texture,
.race_id = race_id,
.send_effects = send_appearance_effects,
.size = size,
.target = target,
.texture = texture,
}
);
}
@@ -3113,6 +3411,11 @@ std::string Perl_Mob_GetClassPlural(Mob* self)
return self->GetClassPlural();
}
uint32 Perl_Mob_GetMobTypeIdentifier(Mob* self)
{
return self->GetMobTypeIdentifier();
}
std::string Perl_Mob_GetRacePlural(Mob* self)
{
return self->GetRacePlural();
@@ -3399,6 +3702,7 @@ void perl_register_mob()
package.add("GetMaxSTR", &Perl_Mob_GetMaxSTR);
package.add("GetMaxWIS", &Perl_Mob_GetMaxWIS);
package.add("GetMeleeMitigation", &Perl_Mob_GetMeleeMitigation);
package.add("GetMobTypeIdentifier", &Perl_Mob_GetMobTypeIdentifier);
package.add("GetModSkillDmgTaken", &Perl_Mob_GetModSkillDmgTaken);
package.add("GetModVulnerability", &Perl_Mob_GetModVulnerability);
package.add("GetNPCTypeID", &Perl_Mob_GetNPCTypeID);
+78 -48
View File
@@ -2187,7 +2187,7 @@ bool QuestManager::isdooropen(uint32 doorid) {
return false;
}
void QuestManager::npcrace(int race_id)
void QuestManager::npcrace(uint16 race_id)
{
QuestManagerCurrentQuestVars();
@@ -2195,10 +2195,14 @@ void QuestManager::npcrace(int race_id)
return;
}
owner->SendIllusionPacket(race_id);
owner->SendIllusionPacket(
AppearanceStruct{
.race_id = race_id,
}
);
}
void QuestManager::npcgender(int gender_id)
void QuestManager::npcgender(uint8 gender_id)
{
QuestManagerCurrentQuestVars();
@@ -2206,10 +2210,15 @@ void QuestManager::npcgender(int gender_id)
return;
}
owner->SendIllusionPacket(owner->GetRace(), gender_id);
owner->SendIllusionPacket(
AppearanceStruct{
.gender_id = gender_id,
.race_id = owner->GetRace(),
}
);
}
void QuestManager::npcsize(int newsize)
void QuestManager::npcsize(float size)
{
QuestManagerCurrentQuestVars();
@@ -2217,10 +2226,10 @@ void QuestManager::npcsize(int newsize)
return;
}
owner->ChangeSize(newsize, true);
owner->ChangeSize(size, true);
}
void QuestManager::npctexture(int newtexture)
void QuestManager::npctexture(uint8 texture)
{
QuestManagerCurrentQuestVars();
@@ -2228,10 +2237,15 @@ void QuestManager::npctexture(int newtexture)
return;
}
owner->SendIllusionPacket(owner->GetRace(), 0xFF, newtexture);
owner->SendIllusionPacket(
AppearanceStruct{
.race_id = owner->GetRace(),
.texture = texture,
}
);
}
void QuestManager::playerrace(int race_id)
void QuestManager::playerrace(uint16 race_id)
{
QuestManagerCurrentQuestVars();
@@ -2239,10 +2253,14 @@ void QuestManager::playerrace(int race_id)
return;
}
initiator->SendIllusionPacket(race_id);
initiator->SendIllusionPacket(
AppearanceStruct{
.race_id = race_id,
}
);
}
void QuestManager::playergender(int gender_id)
void QuestManager::playergender(uint8 gender_id)
{
QuestManagerCurrentQuestVars();
@@ -2250,10 +2268,15 @@ void QuestManager::playergender(int gender_id)
return;
}
initiator->SendIllusionPacket(initiator->GetRace(), gender_id);
initiator->SendIllusionPacket(
AppearanceStruct{
.gender_id = gender_id,
.race_id = initiator->GetRace(),
}
);
}
void QuestManager::playersize(int newsize)
void QuestManager::playersize(float size)
{
QuestManagerCurrentQuestVars();
@@ -2261,10 +2284,10 @@ void QuestManager::playersize(int newsize)
return;
}
initiator->ChangeSize(newsize, true);
initiator->ChangeSize(size, true);
}
void QuestManager::playertexture(int newtexture)
void QuestManager::playertexture(uint8 texture)
{
QuestManagerCurrentQuestVars();
@@ -2272,7 +2295,12 @@ void QuestManager::playertexture(int newtexture)
return;
}
initiator->SendIllusionPacket(initiator->GetRace(), 0xFF, newtexture);
initiator->SendIllusionPacket(
AppearanceStruct{
.race_id = initiator->GetRace(),
.texture = texture,
}
);
}
void QuestManager::playerfeature(const char* feature, int setting)
@@ -2334,22 +2362,23 @@ void QuestManager::playerfeature(const char* feature, int setting)
}
initiator->SendIllusionPacket(
Race,
Gender,
Texture,
HelmTexture,
HairColor,
BeardColor,
EyeColor1,
EyeColor2,
HairStyle,
LuclinFace,
Beard,
0xFF,
DrakkinHeritage,
DrakkinTattoo,
DrakkinDetails,
Size
AppearanceStruct{
.beard = Beard,
.beard_color = BeardColor,
.drakkin_details = DrakkinDetails,
.drakkin_heritage = DrakkinHeritage,
.drakkin_tattoo = DrakkinTattoo,
.eye_color_one = EyeColor1,
.eye_color_two = EyeColor2,
.face = LuclinFace,
.gender_id = Gender,
.hair = HairStyle,
.hair_color = HairColor,
.helmet_texture = HelmTexture,
.race_id = Race,
.size = Size,
.texture = Texture,
}
);
}
@@ -2412,22 +2441,23 @@ void QuestManager::npcfeature(const char* feature, int setting)
}
owner->SendIllusionPacket(
Race,
Gender,
Texture,
HelmTexture,
HairColor,
BeardColor,
EyeColor1,
EyeColor2,
HairStyle,
LuclinFace,
Beard,
0xFF,
DrakkinHeritage,
DrakkinTattoo,
DrakkinDetails,
Size
AppearanceStruct{
.beard = Beard,
.beard_color = BeardColor,
.drakkin_details = DrakkinDetails,
.drakkin_heritage = DrakkinHeritage,
.drakkin_tattoo = DrakkinTattoo,
.eye_color_one = EyeColor1,
.eye_color_two = EyeColor2,
.face = LuclinFace,
.gender_id = Gender,
.hair = HairStyle,
.hair_color = HairColor,
.helmet_texture = HelmTexture,
.race_id = Race,
.size = Size,
.texture = Texture,
}
);
}
+8 -7
View File
@@ -201,13 +201,14 @@ public:
void forcedoorclose(uint32 doorid, bool altmode);
void toggledoorstate(uint32 doorid);
bool isdooropen(uint32 doorid);
void npcrace(int race_id);
void npcgender(int gender_id); void npcsize(int newsize);
void npctexture(int newtexture);
void playerrace(int race_id);
void playergender(int gender_id);
void playersize(int newsize);
void playertexture(int newtexture);
void npcrace(uint16 race_id);
void npcgender(uint8 gender_id);
void npcsize(float size);
void npctexture(uint8 texture);
void playerrace(uint16 race_id);
void playergender(uint8 gender_id);
void playersize(float size);
void playertexture(uint8 texture);
void playerfeature(const char* feature, int setting);
void npcfeature(const char* feature, int setting);
void popup(const char *title, const char *text, uint32 popupid, uint32 buttons, uint32 Duration);
+49 -28
View File
@@ -55,7 +55,9 @@ Raid::Raid(uint32 raidID)
for (int i = 0; i < MAX_NO_RAID_MAIN_ASSISTERS; i++) {
memset(main_assister_pcs[i], 0, 64);
memset(main_marker_pcs[i], 0, 64);
marked_npcs[i] = 0;
marked_npcs[i].entity_id = 0;
marked_npcs[i].zone_id = 0;
marked_npcs[i].instance_id = 0;
}
}
@@ -80,7 +82,9 @@ Raid::Raid(Client* nLeader)
for (int i = 0; i < MAX_NO_RAID_MAIN_ASSISTERS; i++) {
memset(main_assister_pcs[i], 0, 64);
memset(main_marker_pcs[i], 0, 64);
marked_npcs[i] = 0;
marked_npcs[i].entity_id = 0;
marked_npcs[i].zone_id = 0;
marked_npcs[i].instance_id = 0;
}
}
@@ -1678,9 +1682,15 @@ void Raid::GetRaidDetails()
locked = raid_details.locked;
LootType = raid_details.loottype;
motd = raid_details.motd;
marked_npcs[0] = raid_details.marked_npc_1;
marked_npcs[1] = raid_details.marked_npc_2;
marked_npcs[2] = raid_details.marked_npc_3;
marked_npcs[0].entity_id = raid_details.marked_npc_1_entity_id;
marked_npcs[0].zone_id = raid_details.marked_npc_1_zone_id;
marked_npcs[0].instance_id = raid_details.marked_npc_1_instance_id;
marked_npcs[1].entity_id = raid_details.marked_npc_2_entity_id;
marked_npcs[1].zone_id = raid_details.marked_npc_2_zone_id;
marked_npcs[1].instance_id = raid_details.marked_npc_2_instance_id;
marked_npcs[2].entity_id = raid_details.marked_npc_3_entity_id;
marked_npcs[2].zone_id = raid_details.marked_npc_3_zone_id;
marked_npcs[2].instance_id = raid_details.marked_npc_3_instance_id;
}
void Raid::SaveRaidMOTD()
@@ -2419,8 +2429,8 @@ void Raid::UpdateRaidXTargets()
};
for (auto& u : marked_updates) {
if (marked_npcs[u.slot]) {
auto m = entity_list.GetMob(marked_npcs[u.slot]);
if (marked_npcs[u.slot].entity_id) {
auto m = entity_list.GetMob(marked_npcs[u.slot].entity_id);
if (m && m->GetHP() > 0) {
UpdateXTargetType(u.mark_target, m, m->GetName());
}
@@ -2562,13 +2572,17 @@ void Raid::RaidMarkNPC(Mob* mob, uint32 parameter)
for (int i = 0; i < MAX_NO_RAID_MAIN_MARKERS; i++) {
auto cname = c->GetCleanName();
if (strcasecmp(main_marker_pcs[i], cname) == 0 || strcasecmp(leadername, cname) == 0) {
marked_npcs[parameter - 1] = c->GetTarget()->GetID();
marked_npcs[parameter - 1].entity_id = c->GetTarget()->GetID();
marked_npcs[parameter - 1].zone_id = c->GetTarget()->GetZoneID();
marked_npcs[parameter - 1].instance_id = c->GetTarget()->GetInstanceVersion();
auto result = RaidDetailsRepository::UpdateRaidMarkedNPC(
database,
GetID(),
parameter,
marked_npcs[parameter - 1]
);
marked_npcs[parameter - 1].entity_id,
marked_npcs[parameter - 1].zone_id,
marked_npcs[parameter - 1].instance_id,
parameter
);
if (!result) {
LogError("Unable to set MarkedNPC{} from slot: [{}] for guild [{}].",
parameter,
@@ -2579,7 +2593,7 @@ void Raid::RaidMarkNPC(Mob* mob, uint32 parameter)
auto outapp = new EQApplicationPacket(OP_MarkRaidNPC, sizeof(MarkNPC_Struct));
MarkNPC_Struct* mnpcs = (MarkNPC_Struct*)outapp->pBuffer;
mnpcs->TargetID = marked_npcs[parameter - 1];
mnpcs->TargetID = marked_npcs[parameter - 1].entity_id;
mnpcs->Number = parameter;
strcpy(mnpcs->Name, c->GetTarget()->GetCleanName());
QueuePacket(outapp);
@@ -2596,7 +2610,7 @@ void Raid::RaidMarkNPC(Mob* mob, uint32 parameter)
void Raid::UpdateXtargetMarkedNPC()
{
for (int i = 0; i < MAX_MARKED_NPCS; i++) {
auto mm = entity_list.GetNPCByID(marked_npcs[i]);
auto mm = entity_list.GetNPCByID(marked_npcs[i].entity_id);
if (mm) {
UpdateXTargetType(static_cast<XTargetType>(RaidMarkTarget1 + i), mm->CastToMob(), mm->CastToMob()->GetName());
}
@@ -2614,19 +2628,25 @@ void Raid::RaidClearNPCMarks(Client* c)
Strings::EqualFold(main_marker_pcs[MAIN_MARKER_2_SLOT], c->GetCleanName()) ||
Strings::EqualFold(main_marker_pcs[MAIN_MARKER_3_SLOT], c->GetCleanName())) {
for (int i = 0; i < MAX_MARKED_NPCS; i++) {
if (marked_npcs[i]) {
auto npc_name = entity_list.GetNPCByID(marked_npcs[i])->GetCleanName();
if (marked_npcs[i].entity_id > 0 && marked_npcs[i].zone_id == c->GetZoneID()
&& marked_npcs[i].instance_id == c->GetInstanceID())
{
auto npc_name = entity_list.GetNPCByID(marked_npcs[i].entity_id)->GetCleanName();
RaidMessageString(nullptr, Chat::Cyan, RAID_NO_LONGER_MARKED, npc_name);
}
marked_npcs[i] = 0;
auto result = RaidDetailsRepository::UpdateRaidMarkedNPC(
database,
GetID(),
i + 1,
0
);
if (!result) {
LogError("Unable to clear MarkedNPC{} from slot: [{}] for guild [{}].", i + 1, i, GetID());
marked_npcs[i].entity_id = 0;
marked_npcs[i].zone_id = 0;
marked_npcs[i].instance_id = 0;
auto result = RaidDetailsRepository::UpdateRaidMarkedNPC(
database,
GetID(),
0,
0,
0,
i + 1
);
if (!result) {
LogError("Unable to clear MarkedNPC{} from slot: [{}] for guild [{}].", i + 1, i, GetID());
}
}
}
@@ -2934,12 +2954,13 @@ void Raid::SendMarkTargets(Client* c)
}
for (int i = 0; i < MAX_MARKED_NPCS; i++) {
if (marked_npcs[i] > 0) {
auto marked_mob = entity_list.GetMob(marked_npcs[i]);
if (marked_npcs[i].entity_id > 0 && marked_npcs[i].zone_id == c->GetZoneID()
&& marked_npcs[i].instance_id == c->GetInstanceID()) {
auto marked_mob = entity_list.GetMob(marked_npcs[i].entity_id);
if (marked_mob) {
auto outapp = new EQApplicationPacket(OP_MarkRaidNPC, sizeof(MarkNPC_Struct));
MarkNPC_Struct* mnpcs = (MarkNPC_Struct*)outapp->pBuffer;
mnpcs->TargetID = marked_npcs[i];
mnpcs->TargetID = marked_mob->GetID();
mnpcs->Number = i + 1;
strcpy(mnpcs->Name, marked_mob->GetCleanName());
QueuePacket(outapp);
+7 -1
View File
@@ -110,6 +110,12 @@ enum {
DELEGATE_ON = 1
};
struct Raid_Marked_NPC {
uint32 entity_id;
uint32 zone_id;
uint32 instance_id;
};
constexpr uint8_t MAX_RAID_GROUPS = 12;
constexpr uint8_t MAX_RAID_MEMBERS = 72;
@@ -326,7 +332,7 @@ public:
char leadername[64];
char main_assister_pcs[MAX_NO_RAID_MAIN_ASSISTERS][64];
char main_marker_pcs[MAX_NO_RAID_MAIN_MARKERS][64];
uint32 marked_npcs[MAX_MARKED_NPCS];
Raid_Marked_NPC marked_npcs[MAX_MARKED_NPCS];
protected:
Client *leader;
bool locked;
+79 -56
View File
@@ -1474,9 +1474,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
if(caster && caster->GetTarget()){
SendIllusionPacket
(
caster->GetTarget()->GetRace(),
caster->GetTarget()->GetGender(),
caster->GetTarget()->GetTexture()
AppearanceStruct{
.gender_id = caster->GetTarget()->GetGender(),
.race_id = caster->GetTarget()->GetRace(),
.texture = caster->GetTarget()->GetTexture(),
}
);
caster->SendAppearancePacket(AT_Size, static_cast<uint32>(caster->GetTarget()->GetSize()));
@@ -4265,7 +4267,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
case SE_IllusionCopy:
case SE_Illusion:
{
SendIllusionPacket(0, GetBaseGender());
SendIllusionPacket(AppearanceStruct{});
// The GetSize below works because the above setting race to zero sets size back.
SendAppearancePacket(AT_Size, GetSize());
@@ -10199,36 +10201,29 @@ void Mob::ApplySpellEffectIllusion(int32 spell_id, Mob *caster, int buffslot, in
if (base == -1) {
// Specific Gender Illusions
if (spell_id == SPELL_ILLUSION_MALE || spell_id == SPELL_ILLUSION_FEMALE) {
int specific_gender = -1;
// Male
if (spell_id == SPELL_ILLUSION_MALE)
specific_gender = 0;
// Female
else if (spell_id == SPELL_ILLUSION_FEMALE)
specific_gender = 1;
if (specific_gender > -1) {
if (caster && caster->GetTarget()) {
SendIllusionPacket
(
caster->GetTarget()->GetBaseRace(),
specific_gender,
caster->GetTarget()->GetTexture()
);
}
uint8 specific_gender = spell_id == SPELL_ILLUSION_MALE ? MALE : FEMALE;
if (caster && caster->GetTarget()) {
SendIllusionPacket(
AppearanceStruct{
.gender_id = specific_gender,
.race_id = caster->GetTarget()->GetBaseRace(),
.texture = caster->GetTarget()->GetTexture(),
}
);
}
}
// Change Gender Illusions
// Change Gender Illusions
else {
if (caster && caster->GetTarget()) {
int opposite_gender = 0;
if (caster->GetTarget()->GetGender() == 0)
opposite_gender = 1;
uint8 opposite_gender = caster->GetTarget()->GetGender() == MALE ? FEMALE : MALE;
SendIllusionPacket
(
caster->GetTarget()->GetRace(),
opposite_gender,
caster->GetTarget()->GetTexture()
SendIllusionPacket(
AppearanceStruct{
.gender_id = opposite_gender,
.race_id = caster->GetTarget()->GetRace(),
.texture = caster->GetTarget()->GetTexture(),
}
);
}
}
@@ -10250,50 +10245,78 @@ void Mob::ApplySpellEffectIllusion(int32 spell_id, Mob *caster, int buffslot, in
gender_id
);
if (base != RACE_ELEMENTAL_75) {
if (base != RACE_ELEMENTAL_75 && base != RACE_DRAKKIN_522) {
if (max > 0) {
if (limit == 0) {
SendIllusionPacket(
base,
gender_id
AppearanceStruct{
.gender_id = static_cast<uint8>(gender_id),
.race_id = static_cast<uint16>(base),
}
);
}
else {
} else {
if (max != 3) {
SendIllusionPacket(
base,
gender_id,
limit,
max
AppearanceStruct{
.gender_id = static_cast<uint8>(gender_id),
.helmet_texture = static_cast<uint8>(max),
.race_id = static_cast<uint16>(base),
.texture = static_cast<uint8>(limit),
}
);
}
else {
} else {
SendIllusionPacket(
base,
gender_id,
limit,
limit
AppearanceStruct{
.gender_id = static_cast<uint8>(gender_id),
.helmet_texture = static_cast<uint8>(limit),
.race_id = static_cast<uint16>(base),
.texture = static_cast<uint8>(limit),
}
);
}
}
}
else {
} else {
SendIllusionPacket(
base,
gender_id,
limit,
max
AppearanceStruct{
.gender_id = static_cast<uint8>(gender_id),
.helmet_texture = static_cast<uint8>(max),
.race_id = static_cast<uint16>(base),
.texture = static_cast<uint8>(limit),
}
);
}
}
else {
} else if (base == RACE_ELEMENTAL_75){
SendIllusionPacket(
base,
gender_id,
limit
AppearanceStruct{
.gender_id = static_cast<uint8>(gender_id),
.race_id = static_cast<uint16>(base),
.texture = static_cast<uint8>(limit),
}
);
} else if (base == RACE_DRAKKIN_522) {
FaceChange_Struct f{
.haircolor = GetHairColor(),
.beardcolor = GetBeardColor(),
.eyecolor1 = GetEyeColor1(),
.eyecolor2 = GetEyeColor2(),
.hairstyle = GetHairStyle(),
.beard = GetBeard(),
.face = GetLuclinFace(),
.drakkin_heritage = static_cast<uint32>(limit),
.drakkin_tattoo = GetDrakkinTattoo(),
.drakkin_details = GetDrakkinDetails(),
};
SendIllusionPacket(
AppearanceStruct{
.gender_id = static_cast<uint8>(gender_id),
.race_id = static_cast<uint16>(base),
}
);
SetFaceAppearance(f);
}
SendAppearancePacket(AT_Size, race_size);
}
+1 -1
View File
@@ -5886,7 +5886,7 @@ bool Client::SpellBucketCheck(uint16 spell_id, uint32 character_id) {
spell_bucket_name
);
auto bucket_value = DataBucket::GetData(old_bucket_name);
std::string bucket_value = DataBucket::GetData(old_bucket_name);
if (!bucket_value.empty()) {
if (Strings::IsNumber(bucket_value) && Strings::IsNumber(spell_bucket_value)) {
if (Strings::ToInt(bucket_value) >= Strings::ToInt(spell_bucket_value)) {
+1 -1
View File
@@ -510,7 +510,7 @@ void Trap::CreateHiddenTrigger()
make_npc->runspeed = 0.0f;
make_npc->bodytype = BT_Special;
make_npc->race = 127;
make_npc->gender = 0;
make_npc->gender = MALE;
make_npc->loottable_id = 0;
make_npc->npc_spells_id = 0;
make_npc->d_melee_texture1 = 0;
+11
View File
@@ -2004,6 +2004,12 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true);
break;
}
case ServerOP_ReloadDataBucketsCache:
{
zone->SendReloadMessage("Data buckets cache");
DataBucket::ClearCache();
break;
}
case ServerOP_ReloadDoors:
case ServerOP_ReloadGroundSpawns:
case ServerOP_ReloadObjects:
@@ -3329,6 +3335,11 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
SharedTaskZoneMessaging::HandleWorldMessage(pack);
break;
}
case ServerOP_DataBucketCacheUpdate:
{
DataBucket::HandleWorldMessage(pack);
break;
}
default: {
LogInfo("Unknown ZS Opcode [{}] size [{}]", (int)pack->opcode, pack->size);
break;
+11 -1
View File
@@ -1927,6 +1927,8 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
);
}
std::vector<uint32> npc_ids;
for (NpcTypesRepository::NpcTypes &n : NpcTypesRepository::GetWhere((Database &) content_db, filter)) {
NPCType *t;
t = new NPCType;
@@ -2137,8 +2139,15 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
zone->npctable[t->npc_id] = t;
npc = t;
// If NPC ID is not in npc_ids, add to vector
if (!std::count(npc_ids.begin(), npc_ids.end(), t->npc_id)) {
npc_ids.emplace_back(t->npc_id);
}
}
DataBucket::BulkLoadEntities(DataBucketLoadType::NPC, npc_ids);
return npc;
}
@@ -3303,7 +3312,8 @@ void ZoneDatabase::SavePetInfo(Client *client)
// build pet buffs into struct
int pet_buff_count = 0;
int max_slots = RuleI(Spells, MaxTotalSlotsPET);
// Guard against setting the maximum pet slots above the client allowed maximum.
int max_slots = RuleI(Spells, MaxTotalSlotsPET) > PET_BUFF_COUNT ? PET_BUFF_COUNT : RuleI(Spells, MaxTotalSlotsPET);
// count pet buffs
for (int index = 0; index < max_slots; index++) {
+2 -1
View File
@@ -11,6 +11,7 @@
#include "aa_ability.h"
#include "event_codes.h"
#include "../common/repositories/doors_repository.h"
#include "../common/races.h"
#include "bot_database.h"
@@ -339,7 +340,7 @@ namespace BeastlordPetData {
uint16 race_id = WOLF;
uint8 texture = 0;
uint8 helm_texture = 0;
uint8 gender = 2;
uint8 gender = NEUTER;
float size_modifier = 1.0f;
uint8 face = 0;
};