Compare commits

...

39 Commits

Author SHA1 Message Date
KimLS b894d7ee3a update base character data repo 2024-10-12 12:52:07 -07:00
KimLS eecfce6514 update allocations table base repo 2024-10-12 12:41:38 -07:00
KimLS 2d6950149a Fix for server side everything showing as MR 2024-10-12 12:12:57 -07:00
KimLS 3945a8c0c0 Add titanium support 2024-10-12 12:02:40 -07:00
KimLS f27209a812 Add sof support 2024-10-12 11:27:37 -07:00
KimLS d4982743bf Add sod support 2024-10-12 11:23:59 -07:00
KimLS e2dd8f5f60 Add support to rof 2024-10-12 11:13:24 -07:00
KimLS bfc0cceecc Basics work for rof2 2024-10-12 11:06:36 -07:00
KimLS c9902881b7 Generate repo; create will save resists to db 2024-10-12 10:47:03 -07:00
KimLS 62bb426847 working on loading resists from character create. 2024-10-11 23:33:23 -07:00
Alex King c873fe5a22 [Bug Fix] Fix Mercenary Encounter Crash (#4509) 2024-10-11 23:00:09 -04:00
Fryguy e06b0c4b0c [Bug Fix] Master of Disguise should apply to illusions casted by others. (#4506)
Many era comments outline how Master of Disguise would apply to Project Illusion spells on you:

https://thesafehouse.org/forums/forum/everquest-wing/main-lounge/14249-new-aa-master-of-disguise/page4

https://thesafehouse.org/forums/forum/everquest-wing/training-studios/18143-master-of-disguise-broken

```
Im not a big fan of wolf form, but having a 1200 min NDT is pretty nice  I also agree its great to shrink on a raid once and not have to worry about it. 7 aa is a little steep imho, but with a name change and some frog potions, I may reapply to my guild as the servers only froggy rogue /cackle.
```

```
share form of the great wolf gave a 1500min timer.
```
2024-10-11 13:39:36 -04:00
catapultam-habeo ed2130f649 [Bug Fix] Correctly limit max targets of PBAOE (#4507)
* fix pbaoe max targets incorrectly set

* fix scratch copy
2024-10-11 13:15:19 -04:00
Alex King 448a33a60c [Quest API] Add Scripting Support to Mercenaries (#4500)
* [Quest API] Add Scripting Support to Mercenaries

* Cleanup

* Cleanup

* Update lua_merc.h

* Update mob.cpp

* XYZH

* Final

* Update attack.cpp

* Update attack.cpp

* Simplify event invocation

* Inline example

* Nullptr init example

* EVENT_TIMER simplify add EventPlayerNpcBotMerc

* EVENT_TIMER_START

* Remove has_start_event

* EVENT_TIMER_START with settimerMS

* EVENT_POPUP_RESPONSE

* Consolidation

* Update attack.cpp

* Push

* Update quest_parser_collection.h

* Comments

* Cleanup per comments

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2024-10-10 21:29:29 -04:00
Fryguy 8f86cb353e [Bug Fix] Spells - Self Only (Yellow) cast when non group member is targeted (#4503)
* [Bug Fix] Spells - Self Only (Yellow) cast when non group member is targeted

When using a Yellow gem invis spell, it should cast on yourself regardless of the targetted entity.

* Update spells.cpp

---------

Co-authored-by: Alex King <89047260+Kinglykrab@users.noreply.github.com>
2024-10-10 21:27:49 -04:00
Alex 178129443f [Loginserver] Login Fatal Error Spamming (#4476)
Co-authored-by: KimLS <KimLS@peqtgc.com>
2024-10-09 02:15:49 -05:00
Alex King a7c3b41afc [Quest API] Add Buff Fade Methods to Perl/Lua (#4501)
* [Quest API] Add Buff Fade Methods to Perl/Lua

* BuffFadeSongs()
2024-10-09 02:12:33 -05:00
Alex King a5a568d548 [Bug Fix] Fix character_exp_modifiers Default Values (#4502) 2024-10-09 02:11:57 -05:00
Alex King e3198edb86 [Quest API] Add EVENT_READ_ITEM to Perl/Lua (#4497)
* [Quest API] Add EVENT_READ_ITEM to Perl/Lua

* Add item_id export

* Add item export.

* Update client.cpp
2024-10-08 18:25:14 -04:00
Alex King 8568cf7d49 [Bug Fix] Fix NPC::CanTalk() Crash (#4499)
* [Bug FIx] Fix NPC::CanTalk() Crash

* Update npc.cpp

* Update mob.cpp

* Update npc.cpp
2024-10-07 00:17:49 -05:00
Alex King 1fb7a860a1 [Bug Fix] Fix #set motd Crash (#4495) 2024-10-05 07:58:22 -05:00
nytmyr 7eaee2649e [Bots] Add "silent" option to ^spawn and mute raid spawn (#4494)
When zoning or forming a raid, bots would spam their spawn message. They will now be muted.

Adds an optional argument "silent" to the ^spawn command. This will bypass ^oo spawnmessage settings and not send a spawn message. Example: ^spawn Warbot silent
2024-10-04 20:20:52 -04:00
Alex King a17f467b98 [Quest API] Add NPC List Filter Methods to Perl/Lua (#4493)
* [Quest API] Add GetNPCsByNPCIDs to Perl/Lua

* Push

* Update entity.cpp

* Separate methods.
2024-10-03 20:28:57 -04:00
Alex King 3359839a9b [Bug Fix] Fix Targeted AOE Max Targets Rule (#4488) 2024-10-02 20:25:35 -05:00
Alex 7e51e629f9 [Loginserver] Larion loginserver support (#4492)
* Add larion version and opcode path

* WIP: getting server to work

* Identify server_id

* Add missing opcode, add opcodes file

---------

Co-authored-by: KimLS <KimLS@peqtgc.com>
2024-10-02 20:20:13 -05:00
Alex King dc6c28a52d [Cleanup] Remove Extra Skill in EQ::skills::GetExtraDamageSkills() (#4486) 2024-10-02 20:07:19 -05:00
Alex King 78aee0780a [Bug Fix] Fix Group ID 0 in Group::SaveGroupLeaderAA() (#4487) 2024-10-02 20:06:56 -05:00
Chris Miles bcd943a964 [Code Cleanup] Optimization Code Cleanup (#4489)
* Initial push

* More

* More

* Further simplify

* More cleanup

* More consolidation

* Fix

* Update

* Update npc.cpp

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2024-09-30 18:34:42 -04:00
nytmyr 56608e84bd [Bots] Add attack flag when told to attack (#4490)
This adds a flag to mobs that are told to attack by their owner to prevent unintended attacks.

Previously, if you were to send your bots to attack a target and then switch targets: before casters land their spell or if melee (especially anyone with pets)  hasn't engaged before the target switch, they could switch to your new target and attack.

This adds a flag upon attack and bots will only attack flagged targets.
2024-09-29 17:59:26 -04:00
regneq 8d23e710ce [Bug Fix] fixed a bug where it would use npc value instead of faction value in the database. (#4491) 2024-09-29 17:42:43 -04:00
Morzain 4d11077b21 [Bug Fix] Add character_instance_safereturns to tables_to_zero_id (#4485)
Co-authored-by: morzain <morzain@users.noreply.github.com>
2024-09-26 18:14:32 -04:00
Alex King 5c0bdfdc4c [Bug Fix] Fix issue with Client::SaveDisciplines() not specifying character ID (#4481) 2024-09-23 23:00:52 -05:00
Alex King 6130e10831 [Release] 22.56.2 (#4480) 2024-09-19 21:59:53 -05:00
Alex King c3e1c531d2 [Bug Fix] Fix Issue with Database::ReserveName (#4477) 2024-09-19 21:15:14 -05:00
Alex King b52719a535 [Quest API] Add GrantAllAAPoints() Overload To Perl/Lua (#4474) 2024-09-19 21:09:24 -05:00
Alex King 1af252466f [Bug Fix] Fix Untrained Disciplines in Client::SaveDisciplines() (#4472)
* [Bug Fix] Fix Untrained Disciplines in Client::SaveDisciplines()

* [Bug Fix] Fix Infinite Loop in Adventure::Finished() (#4473)

Fix infinite loop condition when bot encountered

* [Bug Fix] Fix Untrained Disciplines in Client::SaveDisciplines()

* Change to release

---------

Co-authored-by: oddx2k <103136558+oddx2k@users.noreply.github.com>
2024-09-19 21:09:09 -05:00
catapultam-habeo 699d22fc28 [Bug Fix] Fix 'Teleport Doors' from being blocked by GM flag (#4475)
* gm flag blocks teleport doors with keys from working instead of allowing them to work

* correct coniditional logic
2024-09-18 17:18:07 -04:00
Mitch Freeman 5d1fe68906 [Bug Fix] Parcel purchase of bazaar items with unlimited charges (#4479)
Fix for unlimited charges in bazaar
2024-09-18 09:36:00 -04:00
oddx2k 52dcf35425 [Bug Fix] Fix Infinite Loop in Adventure::Finished() (#4473)
Fix infinite loop condition when bot encountered
2024-09-13 13:20:55 -04:00
95 changed files with 3723 additions and 2088 deletions
+23
View File
@@ -1,3 +1,26 @@
## [22.56.3] 9/23/2024
### Fixes
* Fix issue with Client::SaveDisciplines() not specifying character ID ([#4481](https://github.com/EQEmu/Server/pull/4477)) @Kinglykrab 2024-09-23
## [22.56.2] 9/20/2024
### Fixes
* Fix Issue with Database::ReserveName ([#4477](https://github.com/EQEmu/Server/pull/4477)) @Kinglykrab 2024-09-20
### Quest API
* Add GrantAllAAPoints() Overload To Perl/Lua ([#4474](https://github.com/EQEmu/Server/pull/4474)) @Kinglykrab 2024-09-20
## [22.56.1] 9/20/2024
### Fixes
* Fix Untrained Disciplines in Client::SaveDisciplines() ([#4472](https://github.com/EQEmu/Server/pull/4472)) @Kinglykrab 2024-09-13
* Fix Infinite Loop in Adventure::Finished() ([#4473](https://github.com/EQEmu/Server/pull/4473)) @oddx2k 2024-09-13
## [22.56.0] 9/12/2024 ## [22.56.0] 9/12/2024
### Code ### Code
+28 -7
View File
@@ -285,16 +285,31 @@ bool Database::SetAccountStatus(const std::string& account_name, int16 status)
bool Database::ReserveName(uint32 account_id, const std::string& name) bool Database::ReserveName(uint32 account_id, const std::string& name)
{ {
const auto& l = CharacterDataRepository::GetWhere( const std::string& where_filter = fmt::format(
*this,
fmt::format(
"`name` = '{}'", "`name` = '{}'",
Strings::Escape(name) Strings::Escape(name)
)
); );
if (!l.empty()) { if (RuleB(Bots, Enabled)) {
LogInfo("Account: [{}] tried to request name: [{}], but it is already taken", account_id, name); const auto& b = BotDataRepository::GetWhere(*this, where_filter);
if (!b.empty()) {
LogInfo("Account [{}] requested name [{}] but name is already taken by a bot", account_id, name);
return false;
}
}
const auto& c = CharacterDataRepository::GetWhere(*this, where_filter);
if (!c.empty()) {
LogInfo("Account [{}] requested name [{}] but name is already taken by a character", account_id, name);
return false;
}
const auto& n = NpcTypesRepository::GetWhere(*this, where_filter);
if (!n.empty()) {
LogInfo("Account [{}] requested name [{}] but name is already taken by an NPC", account_id, name);
return false; return false;
} }
@@ -499,6 +514,12 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe
c.raid_auto_consent = pp->raidAutoconsent; c.raid_auto_consent = pp->raidAutoconsent;
c.guild_auto_consent = pp->guildAutoconsent; c.guild_auto_consent = pp->guildAutoconsent;
c.RestTimer = pp->RestTimer; c.RestTimer = pp->RestTimer;
c.cold_resist = pp->cold_resist;
c.fire_resist = pp->fire_resist;
c.magic_resist = pp->magic_resist;
c.disease_resist = pp->disease_resist;
c.poison_resist = pp->poison_resist;
c.corruption_resist = pp->corruption_resist;
CharacterDataRepository::ReplaceOne(*this, c); CharacterDataRepository::ReplaceOne(*this, c);
@@ -1845,7 +1866,7 @@ bool Database::CopyCharacter(
const int64 new_character_id = (CharacterDataRepository::GetMaxId(*this) + 1); const int64 new_character_id = (CharacterDataRepository::GetMaxId(*this) + 1);
std::vector<std::string> tables_to_zero_id = { "keyring", "data_buckets" }; std::vector<std::string> tables_to_zero_id = { "keyring", "data_buckets", "character_instance_safereturns" };
TransactionBegin(); TransactionBegin();
@@ -5746,6 +5746,18 @@ ALTER TABLE `inventory`
ADD COLUMN `guid` BIGINT UNSIGNED NULL DEFAULT '0' AFTER `ornament_hero_model`; ADD COLUMN `guid` BIGINT UNSIGNED NULL DEFAULT '0' AFTER `ornament_hero_model`;
ALTER TABLE `inventory_snapshots` ALTER TABLE `inventory_snapshots`
ADD COLUMN `guid` BIGINT UNSIGNED NULL DEFAULT '0' AFTER `ornament_hero_model`; ADD COLUMN `guid` BIGINT UNSIGNED NULL DEFAULT '0' AFTER `ornament_hero_model`;
)"
},
ManifestEntry{
.version = 9284,
.description = "2024_10_08_character_exp_modifiers_default.sql",
.check = "SHOW CREATE TABLE `character_exp_modifiers`",
.condition = "contains",
.match = "`exp_modifier` float NOT NULL,",
.sql = R"(
ALTER TABLE `character_exp_modifiers`
MODIFY COLUMN `aa_modifier` float NOT NULL DEFAULT 1.0 AFTER `instance_version`,
MODIFY COLUMN `exp_modifier` float NOT NULL DEFAULT 1.0 AFTER `aa_modifier`;
)" )"
} }
// -- template; copy/paste this when you need to create a new entry // -- template; copy/paste this when you need to create a new entry
+6
View File
@@ -756,4 +756,10 @@ namespace PCNPCOnlyFlagType {
constexpr int NPC = 2; constexpr int NPC = 2;
} }
namespace BookType {
constexpr uint8 Scroll = 0;
constexpr uint8 Book = 1;
constexpr uint8 ItemInfo = 2;
}
#endif /*COMMON_EMU_CONSTANTS_H*/ #endif /*COMMON_EMU_CONSTANTS_H*/
+1
View File
@@ -534,6 +534,7 @@ N(OP_Stamina),
N(OP_Stun), N(OP_Stun),
N(OP_Surname), N(OP_Surname),
N(OP_SwapSpell), N(OP_SwapSpell),
N(OP_SystemFingerprint),
N(OP_TargetBuffs), N(OP_TargetBuffs),
N(OP_TargetCommand), N(OP_TargetCommand),
N(OP_TargetHoTT), N(OP_TargetHoTT),
+6
View File
@@ -1121,6 +1121,12 @@ struct PlayerProfile_Struct
/*19559*/ uint8 unknown19595[5]; // ***Placeholder (6/29/2005) /*19559*/ uint8 unknown19595[5]; // ***Placeholder (6/29/2005)
/*19564*/ uint32 RestTimer; /*19564*/ uint32 RestTimer;
/*19568*/ uint32 char_id; // Found as part of bazaar revamp (5/15/2024) /*19568*/ uint32 char_id; // Found as part of bazaar revamp (5/15/2024)
/*19572*/ uint32 cold_resist;
/*19576*/ uint32 fire_resist;
/*19580*/ uint32 magic_resist;
/*19584*/ uint32 disease_resist;
/*19588*/ uint32 poison_resist;
/*19592*/ uint32 corruption_resist;
// All player profile packets are translated and this overhead is ignored in out-bound packets // All player profile packets are translated and this overhead is ignored in out-bound packets
PlayerProfile_Struct() : m_player_profile_version(EQ::versions::MobVersion::Unknown) { } PlayerProfile_Struct() : m_player_profile_version(EQ::versions::MobVersion::Unknown) { }
+6 -6
View File
@@ -2288,13 +2288,13 @@ namespace RoF
outapp->WriteSInt32(345); // Mana Total ? outapp->WriteSInt32(345); // Mana Total ?
// these are needed to fix display bugs // these are needed to fix display bugs
outapp->WriteUInt32(0x19); // base CR outapp->WriteUInt32(emu->cold_resist); // base CR
outapp->WriteUInt32(0x19); // base FR outapp->WriteUInt32(emu->fire_resist); // base FR
outapp->WriteUInt32(0x19); // base MR outapp->WriteUInt32(emu->magic_resist); // base MR
outapp->WriteUInt32(0xf); // base DR outapp->WriteUInt32(emu->disease_resist); // base DR
outapp->WriteUInt32(0xf); // base PR outapp->WriteUInt32(emu->poison_resist); // base PR
outapp->WriteUInt32(0xf); // base PhR? outapp->WriteUInt32(0xf); // base PhR?
outapp->WriteUInt32(0xf); // base Corrup outapp->WriteUInt32(emu->corruption_resist); // base Corrup
outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown
+6 -6
View File
@@ -2806,13 +2806,13 @@ namespace RoF2
outapp->WriteSInt32(345); // Mana Total ? outapp->WriteSInt32(345); // Mana Total ?
// these are needed to fix display bugs // these are needed to fix display bugs
outapp->WriteUInt32(0x19); // base CR outapp->WriteUInt32(emu->cold_resist); // base CR
outapp->WriteUInt32(0x19); // base FR outapp->WriteUInt32(emu->fire_resist); // base FR
outapp->WriteUInt32(0x19); // base MR outapp->WriteUInt32(emu->magic_resist); // base MR
outapp->WriteUInt32(0xf); // base DR outapp->WriteUInt32(emu->disease_resist); // base DR
outapp->WriteUInt32(0xf); // base PR outapp->WriteUInt32(emu->poison_resist); // base PR
outapp->WriteUInt32(0xf); // base PhR? outapp->WriteUInt32(0xf); // base PhR?
outapp->WriteUInt32(0xf); // base Corrup outapp->WriteUInt32(emu->corruption_resist); // base Corrup
outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown
+19 -7
View File
@@ -1669,15 +1669,27 @@ namespace SoD
// OUT(unknown19584[4]); // OUT(unknown19584[4]);
// OUT(unknown19588); // OUT(unknown19588);
const uint8 bytes[] = { const uint8 unknown12864_bytes[] = {
0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F,
0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}; };
memcpy(eq->unknown12864, bytes, sizeof(bytes)); memcpy(eq->unknown12864, unknown12864_bytes, sizeof(unknown12864_bytes));
eq->cold_resist = emu->cold_resist;
eq->fire_resist = emu->fire_resist;
eq->magic_resist = emu->magic_resist;
eq->disease_resist = emu->disease_resist;
eq->poison_resist = emu->poison_resist;
eq->physical_resist = 15;
eq->corruption_resist = emu->corruption_resist;
const uint8 unknown15112_bytes[] = {
0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
memcpy(eq->unknown15112, unknown15112_bytes, sizeof(unknown15112_bytes));
//set the checksum... //set the checksum...
CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4);
+9 -1
View File
@@ -944,7 +944,15 @@ struct PlayerProfile_Struct
/*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot /*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot
/*15060*/ uint8 unknown12852[8]; /*15060*/ uint8 unknown12852[8];
/*15068*/ uint32 available_slots; /*15068*/ uint32 available_slots;
/*15072*/ uint8 unknown12864[80]; //#### uint8 uint8 unknown12864[76]; in Titanium ####[80] /*15072*/ uint8 unknown12864[12]; //#### uint8 uint8 unknown12864[76]; in Titanium ####[80]
/*15084*/ uint32 cold_resist;
/*15088*/ uint32 fire_resist;
/*15092*/ uint32 magic_resist;
/*15096*/ uint32 disease_resist;
/*15100*/ uint32 poison_resist;
/*15104*/ uint32 physical_resist;
/*15108*/ uint32 corruption_resist;
/*15112*/ uint8 unknown15112[40];
//END SUB-STRUCT used for shrouding. //END SUB-STRUCT used for shrouding.
/*15152*/ char name[64]; // Name of player /*15152*/ char name[64]; // Name of player
/*15216*/ char last_name[32]; // Last name of player /*15216*/ char last_name[32]; // Last name of player
+19 -7
View File
@@ -1339,15 +1339,27 @@ namespace SoF
// OUT(unknown19584[4]); // OUT(unknown19584[4]);
// OUT(unknown19588); // OUT(unknown19588);
const uint8 bytes[] = { const uint8 unknown12864_bytes[] = {
0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F,
0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}; };
memcpy(eq->unknown12864, bytes, sizeof(bytes)); memcpy(eq->unknown12864, unknown12864_bytes, sizeof(unknown12864_bytes));
eq->cold_resist = emu->cold_resist;
eq->fire_resist = emu->fire_resist;
eq->magic_resist = emu->magic_resist;
eq->disease_resist = emu->disease_resist;
eq->poison_resist = emu->poison_resist;
eq->physical_resist = 15;
eq->corruption_resist = emu->corruption_resist;
const uint8 unknown15112_bytes[] = {
0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
memcpy(eq->unknown15112, unknown15112_bytes, sizeof(unknown15112_bytes));
//set the checksum... //set the checksum...
CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4);
+10 -1
View File
@@ -944,7 +944,16 @@ struct PlayerProfile_Struct //23576 Octets
/*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot /*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot
/*15060*/ uint8 unknown12852[8]; /*15060*/ uint8 unknown12852[8];
/*15068*/ uint32 available_slots; /*15068*/ uint32 available_slots;
/*15072*/ uint8 unknown12864[80]; //#### uint8 uint8 unknown12864[76]; in Titanium ####[80] /*15072*/ uint8 unknown12864[12]; //#### uint8 uint8 unknown12864[76]; in Titanium ####[80]
/*15084*/ uint32 cold_resist;
/*15088*/ uint32 fire_resist;
/*15092*/ uint32 magic_resist;
/*15096*/ uint32 disease_resist;
/*15100*/ uint32 poison_resist;
/*15104*/ uint32 physical_resist;
/*15108*/ uint32 corruption_resist;
/*15112*/ uint8 unknown15112[40];
//END SUB-STRUCT used for shrouding. //END SUB-STRUCT used for shrouding.
/*15120*/ char name[64]; // Name of player /*15120*/ char name[64]; // Name of player
/*15184*/ char last_name[32]; // Last name of player /*15184*/ char last_name[32]; // Last name of player
+16 -5
View File
@@ -1600,14 +1600,25 @@ namespace Titanium
// OUT(unknown19584[4]); // OUT(unknown19584[4]);
// OUT(unknown19588); // OUT(unknown19588);
const uint8 unknown12864_bytes[] = {
0x78, 0x03, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00
};
const uint8 bytes[] = { memcpy(eq->unknown12864, unknown12864_bytes, sizeof(unknown12864_bytes));
0x78, 0x03, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, eq->cold_resist = emu->cold_resist;
0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, 0x09, 0x00, 0x00, 0x00, eq->fire_resist = emu->fire_resist;
eq->magic_resist = emu->magic_resist;
eq->disease_resist = emu->disease_resist;
eq->poison_resist = emu->poison_resist;
eq->physical_resist = 15;
const uint8 unknown12900_bytes[] = {
0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, 0x09, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14
}; };
memcpy(eq->unknown12864, bytes, sizeof(bytes));
memcpy(eq->unknown12900, unknown12900_bytes, sizeof(unknown12900_bytes));
//set the checksum... //set the checksum...
CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4);
+8 -1
View File
@@ -879,7 +879,14 @@ struct PlayerProfile_Struct
/*12564*/ PotionBelt_Struct potionbelt; // potion belt /*12564*/ PotionBelt_Struct potionbelt; // potion belt
/*12852*/ uint8 unknown12852[8]; /*12852*/ uint8 unknown12852[8];
/*12860*/ uint32 available_slots; /*12860*/ uint32 available_slots;
/*12864*/ uint8 unknown12864[76]; /*12864*/ uint8 unknown12864[12];
/*12876*/ uint32 cold_resist;
/*12880*/ uint32 fire_resist;
/*12884*/ uint32 magic_resist;
/*12888*/ uint32 disease_resist;
/*12892*/ uint32 poison_resist;
/*12896*/ uint32 physical_resist;
/*12900*/ uint8 unknown12900[40];
/*12940*/ char name[64]; // Name of player /*12940*/ char name[64]; // Name of player
/*13004*/ char last_name[32]; // Last name of player /*13004*/ char last_name[32]; // Last name of player
/*13036*/ uint32 guild_id; // guildid /*13036*/ uint32 guild_id; // guildid
@@ -34,6 +34,12 @@ public:
uint32_t alloc_int; uint32_t alloc_int;
uint32_t alloc_wis; uint32_t alloc_wis;
uint32_t alloc_cha; uint32_t alloc_cha;
uint32_t base_cr;
uint32_t base_fr;
uint32_t base_mr;
uint32_t base_dr;
uint32_t base_pr;
uint32_t base_corrup;
}; };
static std::string PrimaryKey() static std::string PrimaryKey()
@@ -59,6 +65,12 @@ public:
"alloc_int", "alloc_int",
"alloc_wis", "alloc_wis",
"alloc_cha", "alloc_cha",
"base_cr",
"base_fr",
"base_mr",
"base_dr",
"base_pr",
"base_corrup",
}; };
} }
@@ -80,6 +92,12 @@ public:
"alloc_int", "alloc_int",
"alloc_wis", "alloc_wis",
"alloc_cha", "alloc_cha",
"base_cr",
"base_fr",
"base_mr",
"base_dr",
"base_pr",
"base_corrup",
}; };
} }
@@ -135,6 +153,12 @@ public:
e.alloc_int = 0; e.alloc_int = 0;
e.alloc_wis = 0; e.alloc_wis = 0;
e.alloc_cha = 0; e.alloc_cha = 0;
e.base_cr = 0;
e.base_fr = 0;
e.base_mr = 0;
e.base_dr = 0;
e.base_pr = 0;
e.base_corrup = 0;
return e; return e;
} }
@@ -186,6 +210,12 @@ public:
e.alloc_int = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0; e.alloc_int = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
e.alloc_wis = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0; e.alloc_wis = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.alloc_cha = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0; e.alloc_cha = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.base_cr = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
e.base_fr = row[16] ? static_cast<uint32_t>(strtoul(row[16], nullptr, 10)) : 0;
e.base_mr = row[17] ? static_cast<uint32_t>(strtoul(row[17], nullptr, 10)) : 0;
e.base_dr = row[18] ? static_cast<uint32_t>(strtoul(row[18], nullptr, 10)) : 0;
e.base_pr = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
e.base_corrup = row[20] ? static_cast<uint32_t>(strtoul(row[20], nullptr, 10)) : 0;
return e; return e;
} }
@@ -234,6 +264,12 @@ public:
v.push_back(columns[12] + " = " + std::to_string(e.alloc_int)); v.push_back(columns[12] + " = " + std::to_string(e.alloc_int));
v.push_back(columns[13] + " = " + std::to_string(e.alloc_wis)); v.push_back(columns[13] + " = " + std::to_string(e.alloc_wis));
v.push_back(columns[14] + " = " + std::to_string(e.alloc_cha)); v.push_back(columns[14] + " = " + std::to_string(e.alloc_cha));
v.push_back(columns[15] + " = " + std::to_string(e.base_cr));
v.push_back(columns[16] + " = " + std::to_string(e.base_fr));
v.push_back(columns[17] + " = " + std::to_string(e.base_mr));
v.push_back(columns[18] + " = " + std::to_string(e.base_dr));
v.push_back(columns[19] + " = " + std::to_string(e.base_pr));
v.push_back(columns[20] + " = " + std::to_string(e.base_corrup));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -270,6 +306,12 @@ public:
v.push_back(std::to_string(e.alloc_int)); v.push_back(std::to_string(e.alloc_int));
v.push_back(std::to_string(e.alloc_wis)); v.push_back(std::to_string(e.alloc_wis));
v.push_back(std::to_string(e.alloc_cha)); v.push_back(std::to_string(e.alloc_cha));
v.push_back(std::to_string(e.base_cr));
v.push_back(std::to_string(e.base_fr));
v.push_back(std::to_string(e.base_mr));
v.push_back(std::to_string(e.base_dr));
v.push_back(std::to_string(e.base_pr));
v.push_back(std::to_string(e.base_corrup));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -314,6 +356,12 @@ public:
v.push_back(std::to_string(e.alloc_int)); v.push_back(std::to_string(e.alloc_int));
v.push_back(std::to_string(e.alloc_wis)); v.push_back(std::to_string(e.alloc_wis));
v.push_back(std::to_string(e.alloc_cha)); v.push_back(std::to_string(e.alloc_cha));
v.push_back(std::to_string(e.base_cr));
v.push_back(std::to_string(e.base_fr));
v.push_back(std::to_string(e.base_mr));
v.push_back(std::to_string(e.base_dr));
v.push_back(std::to_string(e.base_pr));
v.push_back(std::to_string(e.base_corrup));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -362,6 +410,12 @@ public:
e.alloc_int = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0; e.alloc_int = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
e.alloc_wis = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0; e.alloc_wis = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.alloc_cha = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0; e.alloc_cha = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.base_cr = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
e.base_fr = row[16] ? static_cast<uint32_t>(strtoul(row[16], nullptr, 10)) : 0;
e.base_mr = row[17] ? static_cast<uint32_t>(strtoul(row[17], nullptr, 10)) : 0;
e.base_dr = row[18] ? static_cast<uint32_t>(strtoul(row[18], nullptr, 10)) : 0;
e.base_pr = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
e.base_corrup = row[20] ? static_cast<uint32_t>(strtoul(row[20], nullptr, 10)) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -401,6 +455,12 @@ public:
e.alloc_int = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0; e.alloc_int = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
e.alloc_wis = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0; e.alloc_wis = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.alloc_cha = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0; e.alloc_cha = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.base_cr = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
e.base_fr = row[16] ? static_cast<uint32_t>(strtoul(row[16], nullptr, 10)) : 0;
e.base_mr = row[17] ? static_cast<uint32_t>(strtoul(row[17], nullptr, 10)) : 0;
e.base_dr = row[18] ? static_cast<uint32_t>(strtoul(row[18], nullptr, 10)) : 0;
e.base_pr = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
e.base_corrup = row[20] ? static_cast<uint32_t>(strtoul(row[20], nullptr, 10)) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -490,6 +550,12 @@ public:
v.push_back(std::to_string(e.alloc_int)); v.push_back(std::to_string(e.alloc_int));
v.push_back(std::to_string(e.alloc_wis)); v.push_back(std::to_string(e.alloc_wis));
v.push_back(std::to_string(e.alloc_cha)); v.push_back(std::to_string(e.alloc_cha));
v.push_back(std::to_string(e.base_cr));
v.push_back(std::to_string(e.base_fr));
v.push_back(std::to_string(e.base_mr));
v.push_back(std::to_string(e.base_dr));
v.push_back(std::to_string(e.base_pr));
v.push_back(std::to_string(e.base_corrup));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -527,6 +593,12 @@ public:
v.push_back(std::to_string(e.alloc_int)); v.push_back(std::to_string(e.alloc_int));
v.push_back(std::to_string(e.alloc_wis)); v.push_back(std::to_string(e.alloc_wis));
v.push_back(std::to_string(e.alloc_cha)); v.push_back(std::to_string(e.alloc_cha));
v.push_back(std::to_string(e.base_cr));
v.push_back(std::to_string(e.base_fr));
v.push_back(std::to_string(e.base_mr));
v.push_back(std::to_string(e.base_dr));
v.push_back(std::to_string(e.base_pr));
v.push_back(std::to_string(e.base_corrup));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -123,6 +123,12 @@ public:
uint32_t aa_points_old; uint32_t aa_points_old;
uint32_t e_last_invsnapshot; uint32_t e_last_invsnapshot;
time_t deleted_at; time_t deleted_at;
uint32_t cold_resist;
uint32_t fire_resist;
uint32_t magic_resist;
uint32_t disease_resist;
uint32_t poison_resist;
uint32_t corruption_resist;
}; };
static std::string PrimaryKey() static std::string PrimaryKey()
@@ -237,6 +243,12 @@ public:
"aa_points_old", "aa_points_old",
"e_last_invsnapshot", "e_last_invsnapshot",
"deleted_at", "deleted_at",
"cold_resist",
"fire_resist",
"magic_resist",
"disease_resist",
"poison_resist",
"corruption_resist",
}; };
} }
@@ -347,6 +359,12 @@ public:
"aa_points_old", "aa_points_old",
"e_last_invsnapshot", "e_last_invsnapshot",
"UNIX_TIMESTAMP(deleted_at)", "UNIX_TIMESTAMP(deleted_at)",
"cold_resist",
"fire_resist",
"magic_resist",
"disease_resist",
"poison_resist",
"corruption_resist",
}; };
} }
@@ -491,6 +509,12 @@ public:
e.aa_points_old = 0; e.aa_points_old = 0;
e.e_last_invsnapshot = 0; e.e_last_invsnapshot = 0;
e.deleted_at = 0; e.deleted_at = 0;
e.cold_resist = 0;
e.fire_resist = 0;
e.magic_resist = 0;
e.disease_resist = 0;
e.poison_resist = 0;
e.corruption_resist = 0;
return e; return e;
} }
@@ -631,6 +655,12 @@ public:
e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0; e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0;
e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0; e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0;
e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10); e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10);
e.cold_resist = row[104] ? static_cast<uint32_t>(strtoul(row[104], nullptr, 10)) : 0;
e.fire_resist = row[105] ? static_cast<uint32_t>(strtoul(row[105], nullptr, 10)) : 0;
e.magic_resist = row[106] ? static_cast<uint32_t>(strtoul(row[106], nullptr, 10)) : 0;
e.disease_resist = row[107] ? static_cast<uint32_t>(strtoul(row[107], nullptr, 10)) : 0;
e.poison_resist = row[108] ? static_cast<uint32_t>(strtoul(row[108], nullptr, 10)) : 0;
e.corruption_resist = row[109] ? static_cast<uint32_t>(strtoul(row[109], nullptr, 10)) : 0;
return e; return e;
} }
@@ -767,6 +797,12 @@ public:
v.push_back(columns[101] + " = " + std::to_string(e.aa_points_old)); v.push_back(columns[101] + " = " + std::to_string(e.aa_points_old));
v.push_back(columns[102] + " = " + std::to_string(e.e_last_invsnapshot)); v.push_back(columns[102] + " = " + std::to_string(e.e_last_invsnapshot));
v.push_back(columns[103] + " = FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); v.push_back(columns[103] + " = FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
v.push_back(columns[104] + " = " + std::to_string(e.cold_resist));
v.push_back(columns[105] + " = " + std::to_string(e.fire_resist));
v.push_back(columns[106] + " = " + std::to_string(e.magic_resist));
v.push_back(columns[107] + " = " + std::to_string(e.disease_resist));
v.push_back(columns[108] + " = " + std::to_string(e.poison_resist));
v.push_back(columns[109] + " = " + std::to_string(e.corruption_resist));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -892,6 +928,12 @@ public:
v.push_back(std::to_string(e.aa_points_old)); v.push_back(std::to_string(e.aa_points_old));
v.push_back(std::to_string(e.e_last_invsnapshot)); v.push_back(std::to_string(e.e_last_invsnapshot));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
v.push_back(std::to_string(e.cold_resist));
v.push_back(std::to_string(e.fire_resist));
v.push_back(std::to_string(e.magic_resist));
v.push_back(std::to_string(e.disease_resist));
v.push_back(std::to_string(e.poison_resist));
v.push_back(std::to_string(e.corruption_resist));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -1025,6 +1067,12 @@ public:
v.push_back(std::to_string(e.aa_points_old)); v.push_back(std::to_string(e.aa_points_old));
v.push_back(std::to_string(e.e_last_invsnapshot)); v.push_back(std::to_string(e.e_last_invsnapshot));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
v.push_back(std::to_string(e.cold_resist));
v.push_back(std::to_string(e.fire_resist));
v.push_back(std::to_string(e.magic_resist));
v.push_back(std::to_string(e.disease_resist));
v.push_back(std::to_string(e.poison_resist));
v.push_back(std::to_string(e.corruption_resist));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -1162,6 +1210,12 @@ public:
e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0; e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0;
e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0; e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0;
e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10); e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10);
e.cold_resist = row[104] ? static_cast<uint32_t>(strtoul(row[104], nullptr, 10)) : 0;
e.fire_resist = row[105] ? static_cast<uint32_t>(strtoul(row[105], nullptr, 10)) : 0;
e.magic_resist = row[106] ? static_cast<uint32_t>(strtoul(row[106], nullptr, 10)) : 0;
e.disease_resist = row[107] ? static_cast<uint32_t>(strtoul(row[107], nullptr, 10)) : 0;
e.poison_resist = row[108] ? static_cast<uint32_t>(strtoul(row[108], nullptr, 10)) : 0;
e.corruption_resist = row[109] ? static_cast<uint32_t>(strtoul(row[109], nullptr, 10)) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -1290,6 +1344,12 @@ public:
e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0; e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0;
e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0; e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0;
e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10); e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10);
e.cold_resist = row[104] ? static_cast<uint32_t>(strtoul(row[104], nullptr, 10)) : 0;
e.fire_resist = row[105] ? static_cast<uint32_t>(strtoul(row[105], nullptr, 10)) : 0;
e.magic_resist = row[106] ? static_cast<uint32_t>(strtoul(row[106], nullptr, 10)) : 0;
e.disease_resist = row[107] ? static_cast<uint32_t>(strtoul(row[107], nullptr, 10)) : 0;
e.poison_resist = row[108] ? static_cast<uint32_t>(strtoul(row[108], nullptr, 10)) : 0;
e.corruption_resist = row[109] ? static_cast<uint32_t>(strtoul(row[109], nullptr, 10)) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -1468,6 +1528,12 @@ public:
v.push_back(std::to_string(e.aa_points_old)); v.push_back(std::to_string(e.aa_points_old));
v.push_back(std::to_string(e.e_last_invsnapshot)); v.push_back(std::to_string(e.e_last_invsnapshot));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
v.push_back(std::to_string(e.cold_resist));
v.push_back(std::to_string(e.fire_resist));
v.push_back(std::to_string(e.magic_resist));
v.push_back(std::to_string(e.disease_resist));
v.push_back(std::to_string(e.poison_resist));
v.push_back(std::to_string(e.corruption_resist));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -1594,6 +1660,12 @@ public:
v.push_back(std::to_string(e.aa_points_old)); v.push_back(std::to_string(e.aa_points_old));
v.push_back(std::to_string(e.e_last_invsnapshot)); v.push_back(std::to_string(e.e_last_invsnapshot));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
v.push_back(std::to_string(e.cold_resist));
v.push_back(std::to_string(e.fire_resist));
v.push_back(std::to_string(e.magic_resist));
v.push_back(std::to_string(e.disease_resist));
v.push_back(std::to_string(e.poison_resist));
v.push_back(std::to_string(e.corruption_resist));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
+3 -1
View File
@@ -475,7 +475,6 @@ RULE_BOOL(Spells, OldRainTargets, false, "Use old incorrectly implemented maximu
RULE_REAL(Spells, CallOfTheHeroAggroClearDist, 250.0, "Distance at which CoTH will wipe aggro. To disable and always enable aggro wipe on any distance of CoTH, set to 0.") RULE_REAL(Spells, CallOfTheHeroAggroClearDist, 250.0, "Distance at which CoTH will wipe aggro. To disable and always enable aggro wipe on any distance of CoTH, set to 0.")
RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs") RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs")
RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002") RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002")
RULE_INT(Spells, AOEMaxTargets, 0, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.")
RULE_BOOL(Spells, CazicTouchTargetsPetOwner, true, "If True, causes Cazic Touch to swap targets from pet to pet owner if a pet is tanking.") RULE_BOOL(Spells, CazicTouchTargetsPetOwner, true, "If True, causes Cazic Touch to swap targets from pet to pet owner if a pet is tanking.")
RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts and dot removal on charm break to prevent faction wars.") RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts and dot removal on charm break to prevent faction wars.")
RULE_BOOL(Spells, AllowDoubleInvis, true, "Allows you to cast invisibility spells on a player that is already invisible, live like behavior.") RULE_BOOL(Spells, AllowDoubleInvis, true, "Allows you to cast invisibility spells on a player that is already invisible, live like behavior.")
@@ -515,6 +514,9 @@ RULE_BOOL(Spells, UseClassicSpellFocus, false, "Enabling will tell the server to
RULE_BOOL(Spells, ManaTapsOnAnyClass, false, "Enabling this will allow you to cast mana taps on any class, this will bypass ManaTapsRequireNPCMana rule.") RULE_BOOL(Spells, ManaTapsOnAnyClass, false, "Enabling this will allow you to cast mana taps on any class, this will bypass ManaTapsRequireNPCMana rule.")
RULE_INT(Spells, HealAmountMessageFilterThreshold, 100, "Lifetaps below this threshold will not have a message sent to the client (Heal will still process) 0 to Disable.") RULE_INT(Spells, HealAmountMessageFilterThreshold, 100, "Lifetaps below this threshold will not have a message sent to the client (Heal will still process) 0 to Disable.")
RULE_BOOL(Spells, SnareOverridesSpeedBonuses, false, "Enabling will allow snares to override any speed bonuses the entity may have. Default: False") RULE_BOOL(Spells, SnareOverridesSpeedBonuses, false, "Enabling will allow snares to override any speed bonuses the entity may have. Default: False")
RULE_INT(Spells, TargetedAOEMaxTargets, 4, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.")
RULE_INT(Spells, PointBlankAOEMaxTargets, 0, "Max number of targets a Point-Blank AOE spell can cast on. Set to 0 for no limit.")
RULE_INT(Spells, DefaultAOEMaxTargets, 4, "Max number of targets that an AOE spell which does not meet other descriptions can cast on. Set to 0 for no limit.")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Combat) RULE_CATEGORY(Combat)
+17 -18
View File
@@ -47,6 +47,7 @@
#include "repositories/character_corpses_repository.h" #include "repositories/character_corpses_repository.h"
#include "repositories/skill_caps_repository.h" #include "repositories/skill_caps_repository.h"
#include "repositories/inventory_repository.h" #include "repositories/inventory_repository.h"
#include "repositories/books_repository.h"
namespace ItemField namespace ItemField
{ {
@@ -1391,30 +1392,28 @@ const EQ::ItemData* SharedDatabase::IterateItems(uint32* id) const
return nullptr; return nullptr;
} }
std::string SharedDatabase::GetBook(const char *txtfile, int16 *language) Book_Struct SharedDatabase::GetBook(const std::string& text_file)
{ {
char txtfile2[20]; const auto& l = BooksRepository::GetWhere(
std::string txtout; *this,
strcpy(txtfile2, txtfile); fmt::format(
"`name` = '{}'",
Strings::Escape(text_file)
)
);
const std::string query = StringFormat("SELECT txtfile, language FROM books WHERE name = '%s'", txtfile2); Book_Struct b;
auto results = QueryDatabase(query);
if (!results.Success()) { if (l.empty()) {
txtout.assign(" ",1); return b;
return txtout;
} }
if (results.RowCount() == 0) { const auto& e = l.front();
LogError("No book to send, ({})", txtfile);
txtout.assign(" ",1);
return txtout;
}
auto& row = results.begin(); b.language = e.language;
txtout.assign(row[0],strlen(row[0])); b.text = e.txtfile;
*language = static_cast<int16>(Strings::ToInt(row[1]));
return txtout; return b;
} }
// Create appropriate EQ::ItemInstance class // Create appropriate EQ::ItemInstance class
+8 -3
View File
@@ -41,8 +41,7 @@ struct NPCFactionList;
struct FactionAssociations; struct FactionAssociations;
namespace EQ namespace EQ {
{
struct ItemData; struct ItemData;
class ItemInstance; class ItemInstance;
@@ -50,6 +49,12 @@ namespace EQ
class MemoryMappedFile; class MemoryMappedFile;
} }
struct Book_Struct
{
uint8 language;
std::string text;
};
/* /*
This object is inherited by world and zone's DB object, This object is inherited by world and zone's DB object,
and is mainly here to facilitate shared memory, and other and is mainly here to facilitate shared memory, and other
@@ -114,7 +119,7 @@ public:
int admin int admin
); );
std::string GetBook(const char *txtfile, int16 *language); Book_Struct GetBook(const std::string& text_file);
/** /**
* items * items
-1
View File
@@ -249,7 +249,6 @@ const std::vector<EQ::skills::SkillType>& EQ::skills::GetExtraDamageSkills()
EQ::skills::SkillFlyingKick, EQ::skills::SkillFlyingKick,
EQ::skills::SkillKick, EQ::skills::SkillKick,
EQ::skills::SkillRoundKick, EQ::skills::SkillRoundKick,
EQ::skills::SkillRoundKick,
EQ::skills::SkillTigerClaw, EQ::skills::SkillTigerClaw,
EQ::skills::SkillFrenzy EQ::skills::SkillFrenzy
}; };
+2 -2
View File
@@ -25,7 +25,7 @@
// Build variables // Build variables
// these get injected during the build pipeline // these get injected during the build pipeline
#define CURRENT_VERSION "22.56.0-dev" // always append -dev to the current version for custom-builds #define CURRENT_VERSION "22.56.3-dev" // always append -dev to the current version for custom-builds
#define LOGIN_VERSION "0.8.0" #define LOGIN_VERSION "0.8.0"
#define COMPILE_DATE __DATE__ #define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__ #define COMPILE_TIME __TIME__
@@ -42,7 +42,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/ */
#define CURRENT_BINARY_DATABASE_VERSION 9283 #define CURRENT_BINARY_DATABASE_VERSION 9284
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045
#endif #endif
+7
View File
@@ -138,6 +138,13 @@ public:
*/ */
unsigned int GetPlaySequence() const { return m_play_sequence_id; } unsigned int GetPlaySequence() const { return m_play_sequence_id; }
/**
* Gets the client version
*
* @return
*/
LSClientVersion GetClientVersion() const { return m_client_version; }
/** /**
* Gets the connection for this client * Gets the connection for this client
* *
+40
View File
@@ -88,6 +88,46 @@ ClientManager::ClientManager()
clients.push_back(c); clients.push_back(c);
} }
); );
int larion_port = server.config.GetVariableInt("client_configuration", "larion_port", 15900);
EQStreamManagerInterfaceOptions larion_opts(larion_port, false, false);
larion_stream = new EQ::Net::EQStreamManager(larion_opts);
larion_ops = new RegularOpcodeManager;
opcodes_path = fmt::format(
"{}/{}",
path.GetServerPath(),
server.config.GetVariableString(
"client_configuration",
"larion_opcodes",
"login_opcodes.conf"
)
);
if (!larion_ops->LoadOpcodes(opcodes_path.c_str())) {
LogError(
"ClientManager fatal error: couldn't load opcodes for Larion file [{0}]",
server.config.GetVariableString("client_configuration", "larion_opcodes", "login_opcodes.conf")
);
run_server = false;
}
larion_stream->OnNewConnection(
[this](std::shared_ptr<EQ::Net::EQStream> stream) {
LogInfo(
"New Larion client connection from [{0}:{1}]",
long2ip(stream->GetRemoteIP()),
stream->GetRemotePort()
);
stream->SetOpcodeManager(&larion_ops);
Client* c = new Client(stream, cv_larion);
clients.push_back(c);
}
);
} }
ClientManager::~ClientManager() ClientManager::~ClientManager()
+2
View File
@@ -55,6 +55,8 @@ private:
EQ::Net::EQStreamManager *titanium_stream; EQ::Net::EQStreamManager *titanium_stream;
OpcodeManager *sod_ops; OpcodeManager *sod_ops;
EQ::Net::EQStreamManager *sod_stream; EQ::Net::EQStreamManager *sod_stream;
OpcodeManager *larion_ops;
EQ::Net::EQStreamManager* larion_stream;
}; };
#endif #endif
+2 -1
View File
@@ -83,7 +83,8 @@ struct PlayEverquestResponse_Struct {
enum LSClientVersion { enum LSClientVersion {
cv_titanium, cv_titanium,
cv_sod cv_sod,
cv_larion
}; };
enum LSClientStatus { enum LSClientStatus {
@@ -0,0 +1,14 @@
#EQEmu Public Login Server OPCodes
OP_SessionReady=0x0001
OP_Login=0x0002
OP_ServerListRequest=0x0004
OP_PlayEverquestRequest=0x000d
OP_PlayEverquestResponse=0x0022
OP_ChatMessage=0x0017
OP_LoginAccepted=0x0018
OP_ServerListResponse=0x0019
OP_Poll=0x0029
OP_EnterChat=0x000f
OP_PollResponse=0x0011
OP_SystemFingerprint=0x0016
OP_ExpansionList=0x0030
+1 -1
View File
@@ -137,7 +137,7 @@ std::unique_ptr<EQApplicationPacket> ServerManager::CreateServerListPacket(Clien
use_local_ip ? "Local" : "Remote" use_local_ip ? "Local" : "Remote"
); );
world_server->SerializeForClientServerList(buf, use_local_ip); world_server->SerializeForClientServerList(buf, use_local_ip, client->GetClientVersion());
} }
return std::make_unique<EQApplicationPacket>(OP_ServerListResponse, buf); return std::make_unique<EQApplicationPacket>(OP_ServerListResponse, buf);
+14 -2
View File
@@ -977,7 +977,7 @@ bool WorldServer::ValidateWorldServerAdminLogin(
return false; return false;
} }
void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_local_ip) const void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const
{ {
// see LoginClientServerData_Struct // see LoginClientServerData_Struct
if (use_local_ip) { if (use_local_ip) {
@@ -987,6 +987,10 @@ void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_lo
out.WriteString(GetRemoteIP()); out.WriteString(GetRemoteIP());
} }
if (version == cv_larion) {
out.WriteUInt32(9000);
}
switch (GetServerListID()) { switch (GetServerListID()) {
case LS::ServerType::Legends: case LS::ServerType::Legends:
out.WriteInt32(LS::ServerTypeFlags::Legends); out.WriteInt32(LS::ServerTypeFlags::Legends);
@@ -998,8 +1002,16 @@ void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_lo
out.WriteInt32(LS::ServerTypeFlags::Standard); out.WriteInt32(LS::ServerTypeFlags::Standard);
break; break;
} }
if (version == cv_larion) {
auto server_id = GetServerId();
//if this is 0, the client will not show the server in the list
out.WriteUInt32(1);
out.WriteUInt32(server_id);
}
else {
out.WriteUInt32(GetServerId()); out.WriteUInt32(GetServerId());
}
out.WriteString(GetServerLongName()); out.WriteString(GetServerLongName());
out.WriteString("us"); // country code out.WriteString("us"); // country code
out.WriteString("en"); // language code out.WriteString("en"); // language code
+2 -1
View File
@@ -7,6 +7,7 @@
#include "../common/packet_dump.h" #include "../common/packet_dump.h"
#include "database.h" #include "database.h"
#include "../common/event/timer.h" #include "../common/event/timer.h"
#include "login_types.h"
#include <string> #include <string>
#include <memory> #include <memory>
@@ -149,7 +150,7 @@ public:
bool HandleNewLoginserverRegisteredOnly(Database::DbWorldRegistration &world_registration); bool HandleNewLoginserverRegisteredOnly(Database::DbWorldRegistration &world_registration);
bool HandleNewLoginserverInfoUnregisteredAllowed(Database::DbWorldRegistration &world_registration); bool HandleNewLoginserverInfoUnregisteredAllowed(Database::DbWorldRegistration &world_registration);
void SerializeForClientServerList(class SerializeBuffer& out, bool use_local_ip) const; void SerializeForClientServerList(class SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const;
private: private:
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "eqemu-server", "name": "eqemu-server",
"version": "22.56.0", "version": "22.56.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/EQEmu/Server.git" "url": "https://github.com/EQEmu/Server.git"
+1
View File
@@ -56,6 +56,7 @@ SET(world_headers
login_server.h login_server.h
login_server_list.h login_server_list.h
queryserv.h queryserv.h
race_combos.h
shared_task_manager.h shared_task_manager.h
shared_task_world_messaging.h shared_task_world_messaging.h
sof_char_create_data.h sof_char_create_data.h
+1
View File
@@ -287,6 +287,7 @@ void Adventure::Finished(AdventureWinStatus ws)
auto character_id = database.GetCharacterID(*iter); auto character_id = database.GetCharacterID(*iter);
if (character_id == 0) { if (character_id == 0) {
++iter;
continue; continue;
} }
+354 -269
View File
@@ -47,6 +47,7 @@
#include "clientlist.h" #include "clientlist.h"
#include "wguild_mgr.h" #include "wguild_mgr.h"
#include "sof_char_create_data.h" #include "sof_char_create_data.h"
#include "race_combos.h"
#include "../common/zone_store.h" #include "../common/zone_store.h"
#include "../common/repositories/account_repository.h" #include "../common/repositories/account_repository.h"
#include "../common/repositories/player_event_logs_repository.h" #include "../common/repositories/player_event_logs_repository.h"
@@ -85,8 +86,8 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
std::vector<RaceClassAllocation> character_create_allocations; std::vector<CharCreatePointAllocation> character_create_allocations;
std::vector<RaceClassCombos> character_create_race_class_combos; std::vector<CharCreateCombination> character_create_race_class_combos;
extern ZSList zoneserver_list; extern ZSList zoneserver_list;
extern LoginServerList loginserverlist; extern LoginServerList loginserverlist;
@@ -1642,6 +1643,348 @@ void Client::SendApproveWorld()
safe_delete(outapp); safe_delete(outapp);
} }
// returns true if the request is ok, false if there's an error
bool CheckCharCreateInfoSoF(CharCreate_Struct* cc)
{
if (!cc)
return false;
LogInfo("Validating char creation info");
CharCreateCombination class_combo;
bool found = false;
int combos = character_create_race_class_combos.size();
for (int i = 0; i < combos; ++i) {
if (character_create_race_class_combos[i].Class == cc->class_ &&
character_create_race_class_combos[i].Race == cc->race &&
character_create_race_class_combos[i].Deity == cc->deity &&
character_create_race_class_combos[i].Zone == cc->start_zone) {
class_combo = character_create_race_class_combos[i];
found = true;
break;
}
}
if (!found) {
LogInfo("Could not find class/race/deity/start_zone combination");
return false;
}
uint32 allocs = character_create_allocations.size();
CharCreatePointAllocation allocation = { 0 };
found = false;
for (int i = 0; i < allocs; ++i) {
if (character_create_allocations[i].Index == class_combo.AllocationIndex) {
allocation = character_create_allocations[i];
found = true;
break;
}
}
if (!found) {
LogInfo("Could not find starting stats for selected character combo, cannot verify stats");
return false;
}
uint32 max_stats = allocation.DefaultPointAllocation[0] +
allocation.DefaultPointAllocation[1] +
allocation.DefaultPointAllocation[2] +
allocation.DefaultPointAllocation[3] +
allocation.DefaultPointAllocation[4] +
allocation.DefaultPointAllocation[5] +
allocation.DefaultPointAllocation[6];
if (cc->STR > allocation.BaseStats[0] + max_stats || cc->STR < allocation.BaseStats[0]) {
LogInfo("Strength out of range");
return false;
}
if (cc->DEX > allocation.BaseStats[1] + max_stats || cc->DEX < allocation.BaseStats[1]) {
LogInfo("Dexterity out of range");
return false;
}
if (cc->AGI > allocation.BaseStats[2] + max_stats || cc->AGI < allocation.BaseStats[2]) {
LogInfo("Agility out of range");
return false;
}
if (cc->STA > allocation.BaseStats[3] + max_stats || cc->STA < allocation.BaseStats[3]) {
LogInfo("Stamina out of range");
return false;
}
if (cc->INT > allocation.BaseStats[4] + max_stats || cc->INT < allocation.BaseStats[4]) {
LogInfo("Intelligence out of range");
return false;
}
if (cc->WIS > allocation.BaseStats[5] + max_stats || cc->WIS < allocation.BaseStats[5]) {
LogInfo("Wisdom out of range");
return false;
}
if (cc->CHA > allocation.BaseStats[6] + max_stats || cc->CHA < allocation.BaseStats[6]) {
LogInfo("Charisma out of range");
return false;
}
uint32 current_stats = 0;
current_stats += cc->STR - allocation.BaseStats[0];
current_stats += cc->DEX - allocation.BaseStats[1];
current_stats += cc->AGI - allocation.BaseStats[2];
current_stats += cc->STA - allocation.BaseStats[3];
current_stats += cc->INT - allocation.BaseStats[4];
current_stats += cc->WIS - allocation.BaseStats[5];
current_stats += cc->CHA - allocation.BaseStats[6];
if (current_stats > max_stats) {
LogInfo("Current Stats > Maximum Stats");
return false;
}
return true;
}
bool CheckCharCreateInfoTitanium(CharCreate_Struct* cc)
{
uint32 bSTR, bSTA, bAGI, bDEX, bWIS, bINT, bCHA, bTOTAL, cTOTAL, stat_points; //these are all uint32 in CharCreate_Struct, so we'll make them uint32 here to make the compiler shut up
int classtemp, racetemp;
int Charerrors = 0;
// if this is increased you'll have to add a column to the classrace
// table below
#define _TABLE_RACES 16
static const int BaseRace[_TABLE_RACES][7] =
{ /* STR STA AGI DEX WIS INT CHR */
{ /*Human*/ 75, 75, 75, 75, 75, 75, 75},
{ /*Barbarian*/ 103, 95, 82, 70, 70, 60, 55},
{ /*Erudite*/ 60, 70, 70, 70, 83, 107, 70},
{ /*Wood Elf*/ 65, 65, 95, 80, 80, 75, 75},
{ /*High Elf*/ 55, 65, 85, 70, 95, 92, 80},
{ /*Dark Elf*/ 60, 65, 90, 75, 83, 99, 60},
{ /*Half Elf*/ 70, 70, 90, 85, 60, 75, 75},
{ /*Dwarf*/ 90, 90, 70, 90, 83, 60, 45},
{ /*Troll*/ 108, 109, 83, 75, 60, 52, 40},
{ /*Ogre*/ 130, 122, 70, 70, 67, 60, 37},
{ /*Halfling*/ 70, 75, 95, 90, 80, 67, 50},
{ /*Gnome*/ 60, 70, 85, 85, 67, 98, 60},
{ /*Iksar*/ 70, 70, 90, 85, 80, 75, 55},
{ /*Vah Shir*/ 90, 75, 90, 70, 70, 65, 65},
{ /*Froglok*/ 70, 80, 100, 100, 75, 75, 50},
{ /*Drakkin*/ 70, 80, 85, 75, 80, 85, 75}
};
static const int BaseClass[Class::PLAYER_CLASS_COUNT][8] =
{ /* STR STA AGI DEX WIS INT CHR ADD*/
{ /*Warrior*/ 10, 10, 5, 0, 0, 0, 0, 25},
{ /*Cleric*/ 5, 5, 0, 0, 10, 0, 0, 30},
{ /*Paladin*/ 10, 5, 0, 0, 5, 0, 10, 20},
{ /*Ranger*/ 5, 10, 10, 0, 5, 0, 0, 20},
{ /*ShadowKnight*/ 10, 5, 0, 0, 0, 10, 5, 20},
{ /*Druid*/ 0, 10, 0, 0, 10, 0, 0, 30},
{ /*Monk*/ 5, 5, 10, 10, 0, 0, 0, 20},
{ /*Bard*/ 5, 0, 0, 10, 0, 0, 10, 25},
{ /*Rouge*/ 0, 0, 10, 10, 0, 0, 0, 30},
{ /*Shaman*/ 0, 5, 0, 0, 10, 0, 5, 30},
{ /*Necromancer*/ 0, 0, 0, 10, 0, 10, 0, 30},
{ /*Wizard*/ 0, 10, 0, 0, 0, 10, 0, 30},
{ /*Magician*/ 0, 10, 0, 0, 0, 10, 0, 30},
{ /*Enchanter*/ 0, 0, 0, 0, 0, 10, 10, 30},
{ /*Beastlord*/ 0, 10, 5, 0, 10, 0, 5, 20},
{ /*Berserker*/ 10, 5, 0, 10, 0, 0, 0, 25}
};
static const bool ClassRaceLookupTable[Class::PLAYER_CLASS_COUNT][_TABLE_RACES] =
{ /*Human Barbarian Erudite Woodelf Highelf Darkelf Halfelf Dwarf Troll Ogre Halfling Gnome Iksar Vahshir Froglok Drakkin*/
{ /*Warrior*/ true, true, false, true, false, true, true, true, true, true, true, true, true, true, true, true},
{ /*Cleric*/ true, false, true, false, true, true, true, true, false, false, true, true, false, false, true, true},
{ /*Paladin*/ true, false, true, false, true, false, true, true, false, false, true, true, false, false, true, true},
{ /*Ranger*/ true, false, false, true, false, false, true, false, false, false, true, false, false, false, false, true},
{ /*ShadowKnight*/ true, false, true, false, false, true, false, false, true, true, false, true, true, false, true, true},
{ /*Druid*/ true, false, false, true, false, false, true, false, false, false, true, false, false, false, false, true},
{ /*Monk*/ true, false, false, false, false, false, false, false, false, false, false, false, true, false, false, true},
{ /*Bard*/ true, false, false, true, false, false, true, false, false, false, false, false, false, true, false, true},
{ /*Rogue*/ true, true, false, true, false, true, true, true, false, false, true, true, false, true, true, true},
{ /*Shaman*/ false, true, false, false, false, false, false, false, true, true, false, false, true, true, true, false},
{ /*Necromancer*/ true, false, true, false, false, true, false, false, false, false, false, true, true, false, true, true},
{ /*Wizard*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, true, true},
{ /*Magician*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, false, true},
{ /*Enchanter*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, false, true},
{ /*Beastlord*/ false, true, false, false, false, false, false, false, true, true, false, false, true, true, false, false},
{ /*Berserker*/ false, true, false, false, false, false, false, true, true, true, false, false, false, true, false, false}
};
if (!cc)
return false;
LogInfo("Validating char creation info");
classtemp = cc->class_ - 1;
racetemp = cc->race - 1;
// these have non sequential race numbers so they need to be mapped
if (cc->race == FROGLOK) racetemp = 14;
if (cc->race == VAHSHIR) racetemp = 13;
if (cc->race == IKSAR) racetemp = 12;
if (cc->race == DRAKKIN) racetemp = 15;
// if out of range looking it up in the table would crash stuff
// so we return from these
if (classtemp >= Class::PLAYER_CLASS_COUNT) {
LogInfo(" class is out of range");
return false;
}
if (racetemp >= _TABLE_RACES) {
LogInfo(" race is out of range");
return false;
}
if (!ClassRaceLookupTable[classtemp][racetemp]) { //Lookup table better than a bunch of ifs?
LogInfo(" invalid race/class combination");
// we return from this one, since if it's an invalid combination our table
// doesn't have meaningful values for the stats
return false;
}
// add up the base values for this class/race
// this is what they start with, and they have stat_points more
// that can distributed
bSTR = BaseClass[classtemp][0] + BaseRace[racetemp][0];
bSTA = BaseClass[classtemp][1] + BaseRace[racetemp][1];
bAGI = BaseClass[classtemp][2] + BaseRace[racetemp][2];
bDEX = BaseClass[classtemp][3] + BaseRace[racetemp][3];
bWIS = BaseClass[classtemp][4] + BaseRace[racetemp][4];
bINT = BaseClass[classtemp][5] + BaseRace[racetemp][5];
bCHA = BaseClass[classtemp][6] + BaseRace[racetemp][6];
stat_points = BaseClass[classtemp][7];
bTOTAL = bSTR + bSTA + bAGI + bDEX + bWIS + bINT + bCHA;
cTOTAL = cc->STR + cc->STA + cc->AGI + cc->DEX + cc->WIS + cc->INT + cc->CHA;
// the first check makes sure the total is exactly what was expected.
// this will catch all the stat cheating, but there's still the issue
// of reducing CHA or INT or something, to use for STR, so we check
// that none are lower than the base or higher than base + stat_points
// NOTE: these could just be else if, but i want to see all the stats
// that are messed up not just the first hit
if (bTOTAL + stat_points != cTOTAL) {
LogInfo(" stat points total doesn't match expected value: expecting [{}] got [{}]", bTOTAL + stat_points, cTOTAL);
Charerrors++;
}
if (cc->STR > bSTR + stat_points || cc->STR < bSTR) {
LogInfo(" stat STR is out of range");
Charerrors++;
}
if (cc->STA > bSTA + stat_points || cc->STA < bSTA) {
LogInfo(" stat STA is out of range");
Charerrors++;
}
if (cc->AGI > bAGI + stat_points || cc->AGI < bAGI) {
LogInfo(" stat AGI is out of range");
Charerrors++;
}
if (cc->DEX > bDEX + stat_points || cc->DEX < bDEX) {
LogInfo(" stat DEX is out of range");
Charerrors++;
}
if (cc->WIS > bWIS + stat_points || cc->WIS < bWIS) {
LogInfo(" stat WIS is out of range");
Charerrors++;
}
if (cc->INT > bINT + stat_points || cc->INT < bINT) {
LogInfo(" stat INT is out of range");
Charerrors++;
}
if (cc->CHA > bCHA + stat_points || cc->CHA < bCHA) {
LogInfo(" stat CHA is out of range");
Charerrors++;
}
/*TODO: Check for deity/class/race.. it'd be nice, but probably of any real use to hack(faction, deity based items are all I can think of)
I am NOT writing those tables - kathgar*/
LogInfo("Found [{}] errors in character creation request", Charerrors);
return Charerrors == 0;
}
//TODO: these hard coded values should be settable somewhere somehow.
//Also they're not 100% accurate so if I don't make them settable somehow
//we need to go back and match them to the logic that was ripped out of zone
void GetResistsForCharacterCreate(CharCreate_Struct* cc,
bool sofAndLater,
uint32 &cold_resist,
uint32& fire_resist,
uint32& magic_resist,
uint32& disease_resist,
uint32& poison_resist,
uint32& corruption_resist)
{
if (!sofAndLater) {
cold_resist = 25;
fire_resist = 25;
magic_resist = 25;
disease_resist = 15;
poison_resist = 15;
corruption_resist = 15;
return;
}
CharCreateCombination class_combo;
bool found = false;
int combos = character_create_race_class_combos.size();
for (int i = 0; i < combos; ++i) {
if (character_create_race_class_combos[i].Class == cc->class_ &&
character_create_race_class_combos[i].Race == cc->race &&
character_create_race_class_combos[i].Deity == cc->deity &&
character_create_race_class_combos[i].Zone == cc->start_zone) {
class_combo = character_create_race_class_combos[i];
found = true;
break;
}
}
if (!found) {
cold_resist = 25;
fire_resist = 25;
magic_resist = 25;
disease_resist = 15;
poison_resist = 15;
corruption_resist = 15;
return;
}
CharCreatePointAllocation allocation;
found = false;
combos = character_create_allocations.size();
for (int i = 0; i < combos; ++i) {
if (character_create_allocations[i].Index == class_combo.AllocationIndex) {
allocation = character_create_allocations[i];
found = true;
break;
}
}
if (!found) {
cold_resist = 25;
fire_resist = 25;
magic_resist = 25;
disease_resist = 15;
poison_resist = 15;
corruption_resist = 15;
return;
}
cold_resist = allocation.BaseResists[0];
fire_resist = allocation.BaseResists[1];
magic_resist = allocation.BaseResists[2];
disease_resist = allocation.BaseResists[3];
poison_resist = allocation.BaseResists[4];
corruption_resist = allocation.BaseResists[5];
}
bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
{ {
PlayerProfile_Struct pp; PlayerProfile_Struct pp;
@@ -1745,6 +2088,15 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
pp.hunger_level = 6000; pp.hunger_level = 6000;
pp.thirst_level = 6000; pp.thirst_level = 6000;
GetResistsForCharacterCreate(cc,
m_ClientVersionBit & EQ::versions::maskSoFAndLater,
pp.cold_resist,
pp.fire_resist,
pp.magic_resist,
pp.disease_resist,
pp.poison_resist,
pp.corruption_resist);
/* Set default skills for everybody */ /* Set default skills for everybody */
pp.skills[EQ::skills::SkillSwimming] = RuleI(Skills, SwimmingStartValue); pp.skills[EQ::skills::SkillSwimming] = RuleI(Skills, SwimmingStartValue);
pp.skills[EQ::skills::SkillSenseHeading] = RuleI(Skills, SenseHeadingStartValue); pp.skills[EQ::skills::SkillSenseHeading] = RuleI(Skills, SenseHeadingStartValue);
@@ -1865,273 +2217,6 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
return success; return success;
} }
// returns true if the request is ok, false if there's an error
bool CheckCharCreateInfoSoF(CharCreate_Struct *cc)
{
if (!cc)
return false;
LogInfo("Validating char creation info");
RaceClassCombos class_combo;
bool found = false;
int combos = character_create_race_class_combos.size();
for (int i = 0; i < combos; ++i) {
if (character_create_race_class_combos[i].Class == cc->class_ &&
character_create_race_class_combos[i].Race == cc->race &&
character_create_race_class_combos[i].Deity == cc->deity &&
character_create_race_class_combos[i].Zone == cc->start_zone) {
class_combo = character_create_race_class_combos[i];
found = true;
break;
}
}
if (!found) {
LogInfo("Could not find class/race/deity/start_zone combination");
return false;
}
uint32 allocs = character_create_allocations.size();
RaceClassAllocation allocation = {0};
found = false;
for (int i = 0; i < allocs; ++i) {
if (character_create_allocations[i].Index == class_combo.AllocationIndex) {
allocation = character_create_allocations[i];
found = true;
break;
}
}
if (!found) {
LogInfo("Could not find starting stats for selected character combo, cannot verify stats");
return false;
}
uint32 max_stats = allocation.DefaultPointAllocation[0] +
allocation.DefaultPointAllocation[1] +
allocation.DefaultPointAllocation[2] +
allocation.DefaultPointAllocation[3] +
allocation.DefaultPointAllocation[4] +
allocation.DefaultPointAllocation[5] +
allocation.DefaultPointAllocation[6];
if (cc->STR > allocation.BaseStats[0] + max_stats || cc->STR < allocation.BaseStats[0]) {
LogInfo("Strength out of range");
return false;
}
if (cc->DEX > allocation.BaseStats[1] + max_stats || cc->DEX < allocation.BaseStats[1]) {
LogInfo("Dexterity out of range");
return false;
}
if (cc->AGI > allocation.BaseStats[2] + max_stats || cc->AGI < allocation.BaseStats[2]) {
LogInfo("Agility out of range");
return false;
}
if (cc->STA > allocation.BaseStats[3] + max_stats || cc->STA < allocation.BaseStats[3]) {
LogInfo("Stamina out of range");
return false;
}
if (cc->INT > allocation.BaseStats[4] + max_stats || cc->INT < allocation.BaseStats[4]) {
LogInfo("Intelligence out of range");
return false;
}
if (cc->WIS > allocation.BaseStats[5] + max_stats || cc->WIS < allocation.BaseStats[5]) {
LogInfo("Wisdom out of range");
return false;
}
if (cc->CHA > allocation.BaseStats[6] + max_stats || cc->CHA < allocation.BaseStats[6]) {
LogInfo("Charisma out of range");
return false;
}
uint32 current_stats = 0;
current_stats += cc->STR - allocation.BaseStats[0];
current_stats += cc->DEX - allocation.BaseStats[1];
current_stats += cc->AGI - allocation.BaseStats[2];
current_stats += cc->STA - allocation.BaseStats[3];
current_stats += cc->INT - allocation.BaseStats[4];
current_stats += cc->WIS - allocation.BaseStats[5];
current_stats += cc->CHA - allocation.BaseStats[6];
if (current_stats > max_stats) {
LogInfo("Current Stats > Maximum Stats");
return false;
}
return true;
}
bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc)
{
uint32 bSTR, bSTA, bAGI, bDEX, bWIS, bINT, bCHA, bTOTAL, cTOTAL, stat_points; //these are all uint32 in CharCreate_Struct, so we'll make them uint32 here to make the compiler shut up
int classtemp, racetemp;
int Charerrors = 0;
// if this is increased you'll have to add a column to the classrace
// table below
#define _TABLE_RACES 16
static const int BaseRace[_TABLE_RACES][7] =
{ /* STR STA AGI DEX WIS INT CHR */
{ /*Human*/ 75, 75, 75, 75, 75, 75, 75},
{ /*Barbarian*/ 103, 95, 82, 70, 70, 60, 55},
{ /*Erudite*/ 60, 70, 70, 70, 83, 107, 70},
{ /*Wood Elf*/ 65, 65, 95, 80, 80, 75, 75},
{ /*High Elf*/ 55, 65, 85, 70, 95, 92, 80},
{ /*Dark Elf*/ 60, 65, 90, 75, 83, 99, 60},
{ /*Half Elf*/ 70, 70, 90, 85, 60, 75, 75},
{ /*Dwarf*/ 90, 90, 70, 90, 83, 60, 45},
{ /*Troll*/ 108, 109, 83, 75, 60, 52, 40},
{ /*Ogre*/ 130, 122, 70, 70, 67, 60, 37},
{ /*Halfling*/ 70, 75, 95, 90, 80, 67, 50},
{ /*Gnome*/ 60, 70, 85, 85, 67, 98, 60},
{ /*Iksar*/ 70, 70, 90, 85, 80, 75, 55},
{ /*Vah Shir*/ 90, 75, 90, 70, 70, 65, 65},
{ /*Froglok*/ 70, 80, 100, 100, 75, 75, 50},
{ /*Drakkin*/ 70, 80, 85, 75, 80, 85, 75}
};
static const int BaseClass[Class::PLAYER_CLASS_COUNT][8] =
{ /* STR STA AGI DEX WIS INT CHR ADD*/
{ /*Warrior*/ 10, 10, 5, 0, 0, 0, 0, 25},
{ /*Cleric*/ 5, 5, 0, 0, 10, 0, 0, 30},
{ /*Paladin*/ 10, 5, 0, 0, 5, 0, 10, 20},
{ /*Ranger*/ 5, 10, 10, 0, 5, 0, 0, 20},
{ /*ShadowKnight*/ 10, 5, 0, 0, 0, 10, 5, 20},
{ /*Druid*/ 0, 10, 0, 0, 10, 0, 0, 30},
{ /*Monk*/ 5, 5, 10, 10, 0, 0, 0, 20},
{ /*Bard*/ 5, 0, 0, 10, 0, 0, 10, 25},
{ /*Rouge*/ 0, 0, 10, 10, 0, 0, 0, 30},
{ /*Shaman*/ 0, 5, 0, 0, 10, 0, 5, 30},
{ /*Necromancer*/ 0, 0, 0, 10, 0, 10, 0, 30},
{ /*Wizard*/ 0, 10, 0, 0, 0, 10, 0, 30},
{ /*Magician*/ 0, 10, 0, 0, 0, 10, 0, 30},
{ /*Enchanter*/ 0, 0, 0, 0, 0, 10, 10, 30},
{ /*Beastlord*/ 0, 10, 5, 0, 10, 0, 5, 20},
{ /*Berserker*/ 10, 5, 0, 10, 0, 0, 0, 25}
};
static const bool ClassRaceLookupTable[Class::PLAYER_CLASS_COUNT][_TABLE_RACES]=
{ /*Human Barbarian Erudite Woodelf Highelf Darkelf Halfelf Dwarf Troll Ogre Halfling Gnome Iksar Vahshir Froglok Drakkin*/
{ /*Warrior*/ true, true, false, true, false, true, true, true, true, true, true, true, true, true, true, true},
{ /*Cleric*/ true, false, true, false, true, true, true, true, false, false, true, true, false, false, true, true},
{ /*Paladin*/ true, false, true, false, true, false, true, true, false, false, true, true, false, false, true, true},
{ /*Ranger*/ true, false, false, true, false, false, true, false, false, false, true, false, false, false, false, true},
{ /*ShadowKnight*/ true, false, true, false, false, true, false, false, true, true, false, true, true, false, true, true},
{ /*Druid*/ true, false, false, true, false, false, true, false, false, false, true, false, false, false, false, true},
{ /*Monk*/ true, false, false, false, false, false, false, false, false, false, false, false, true, false, false, true},
{ /*Bard*/ true, false, false, true, false, false, true, false, false, false, false, false, false, true, false, true},
{ /*Rogue*/ true, true, false, true, false, true, true, true, false, false, true, true, false, true, true, true},
{ /*Shaman*/ false, true, false, false, false, false, false, false, true, true, false, false, true, true, true, false},
{ /*Necromancer*/ true, false, true, false, false, true, false, false, false, false, false, true, true, false, true, true},
{ /*Wizard*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, true, true},
{ /*Magician*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, false, true},
{ /*Enchanter*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, false, true},
{ /*Beastlord*/ false, true, false, false, false, false, false, false, true, true, false, false, true, true, false, false},
{ /*Berserker*/ false, true, false, false, false, false, false, true, true, true, false, false, false, true, false, false}
};
if (!cc)
return false;
LogInfo("Validating char creation info");
classtemp = cc->class_ - 1;
racetemp = cc->race - 1;
// these have non sequential race numbers so they need to be mapped
if (cc->race == FROGLOK) racetemp = 14;
if (cc->race == VAHSHIR) racetemp = 13;
if (cc->race == IKSAR) racetemp = 12;
if (cc->race == DRAKKIN) racetemp = 15;
// if out of range looking it up in the table would crash stuff
// so we return from these
if (classtemp >= Class::PLAYER_CLASS_COUNT) {
LogInfo(" class is out of range");
return false;
}
if (racetemp >= _TABLE_RACES) {
LogInfo(" race is out of range");
return false;
}
if (!ClassRaceLookupTable[classtemp][racetemp]) { //Lookup table better than a bunch of ifs?
LogInfo(" invalid race/class combination");
// we return from this one, since if it's an invalid combination our table
// doesn't have meaningful values for the stats
return false;
}
// add up the base values for this class/race
// this is what they start with, and they have stat_points more
// that can distributed
bSTR = BaseClass[classtemp][0] + BaseRace[racetemp][0];
bSTA = BaseClass[classtemp][1] + BaseRace[racetemp][1];
bAGI = BaseClass[classtemp][2] + BaseRace[racetemp][2];
bDEX = BaseClass[classtemp][3] + BaseRace[racetemp][3];
bWIS = BaseClass[classtemp][4] + BaseRace[racetemp][4];
bINT = BaseClass[classtemp][5] + BaseRace[racetemp][5];
bCHA = BaseClass[classtemp][6] + BaseRace[racetemp][6];
stat_points = BaseClass[classtemp][7];
bTOTAL = bSTR + bSTA + bAGI + bDEX + bWIS + bINT + bCHA;
cTOTAL = cc->STR + cc->STA + cc->AGI + cc->DEX + cc->WIS + cc->INT + cc->CHA;
// the first check makes sure the total is exactly what was expected.
// this will catch all the stat cheating, but there's still the issue
// of reducing CHA or INT or something, to use for STR, so we check
// that none are lower than the base or higher than base + stat_points
// NOTE: these could just be else if, but i want to see all the stats
// that are messed up not just the first hit
if (bTOTAL + stat_points != cTOTAL) {
LogInfo(" stat points total doesn't match expected value: expecting [{}] got [{}]", bTOTAL + stat_points, cTOTAL);
Charerrors++;
}
if (cc->STR > bSTR + stat_points || cc->STR < bSTR) {
LogInfo(" stat STR is out of range");
Charerrors++;
}
if (cc->STA > bSTA + stat_points || cc->STA < bSTA) {
LogInfo(" stat STA is out of range");
Charerrors++;
}
if (cc->AGI > bAGI + stat_points || cc->AGI < bAGI) {
LogInfo(" stat AGI is out of range");
Charerrors++;
}
if (cc->DEX > bDEX + stat_points || cc->DEX < bDEX) {
LogInfo(" stat DEX is out of range");
Charerrors++;
}
if (cc->WIS > bWIS + stat_points || cc->WIS < bWIS) {
LogInfo(" stat WIS is out of range");
Charerrors++;
}
if (cc->INT > bINT + stat_points || cc->INT < bINT) {
LogInfo(" stat INT is out of range");
Charerrors++;
}
if (cc->CHA > bCHA + stat_points || cc->CHA < bCHA) {
LogInfo(" stat CHA is out of range");
Charerrors++;
}
/*TODO: Check for deity/class/race.. it'd be nice, but probably of any real use to hack(faction, deity based items are all I can think of)
I am NOT writing those tables - kathgar*/
LogInfo("Found [{}] errors in character creation request", Charerrors);
return Charerrors == 0;
}
void Client::SetClassStartingSkills(PlayerProfile_Struct *pp) void Client::SetClassStartingSkills(PlayerProfile_Struct *pp)
{ {
for (uint32 i = 0; i <= EQ::skills::HIGHEST_SKILL; ++i) { for (uint32 i = 0; i <= EQ::skills::HIGHEST_SKILL; ++i) {
-3
View File
@@ -122,7 +122,4 @@ private:
void RecordPossibleHack(const std::string& message); void RecordPossibleHack(const std::string& message);
}; };
bool CheckCharCreateInfoSoF(CharCreate_Struct *cc);
bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc);
#endif #endif
+23 -5
View File
@@ -315,6 +315,13 @@ void LoginServer::ProcessLSFatalError(uint16_t opcode, EQ::Net::Packet &p)
error, error,
reason reason
); );
if (m_legacy_client) {
m_legacy_client.release();
}
else if (m_client) {
m_client.release();
}
} }
void LoginServer::ProcessSystemwideMessage(uint16_t opcode, EQ::Net::Packet &p) void LoginServer::ProcessSystemwideMessage(uint16_t opcode, EQ::Net::Packet &p)
@@ -598,6 +605,11 @@ bool LoginServer::Connect()
void LoginServer::SendInfo() void LoginServer::SendInfo()
{ {
if (m_client == nullptr && m_legacy_client == nullptr) {
LogDebug("No client to send info to loginserver");
return;
}
const WorldConfig *Config = WorldConfig::get(); const WorldConfig *Config = WorldConfig::get();
auto pack = new ServerPacket; auto pack = new ServerPacket;
@@ -643,6 +655,11 @@ void LoginServer::SendInfo()
void LoginServer::SendStatus() void LoginServer::SendStatus()
{ {
if (m_client == nullptr && m_legacy_client == nullptr) {
LogDebug("No client to send status to loginserver");
return;
}
auto pack = new ServerPacket; auto pack = new ServerPacket;
pack->opcode = ServerOP_LSStatus; pack->opcode = ServerOP_LSStatus;
pack->size = sizeof(ServerLSStatus_Struct); pack->size = sizeof(ServerLSStatus_Struct);
@@ -671,20 +688,21 @@ void LoginServer::SendStatus()
*/ */
void LoginServer::SendPacket(ServerPacket *pack) void LoginServer::SendPacket(ServerPacket *pack)
{ {
if (m_is_legacy) {
if (m_legacy_client) { if (m_legacy_client) {
m_legacy_client->SendPacket(pack); m_legacy_client->SendPacket(pack);
} }
} else if (m_client) {
else {
if (m_client) {
m_client->SendPacket(pack); m_client->SendPacket(pack);
} }
}
} }
void LoginServer::SendAccountUpdate(ServerPacket *pack) void LoginServer::SendAccountUpdate(ServerPacket *pack)
{ {
if (m_client == nullptr && m_legacy_client == nullptr) {
LogDebug("No client to send account update to loginserver");
return;
}
auto *ls_account_update = (ServerLSAccountUpdate_Struct *) pack->pBuffer; auto *ls_account_update = (ServerLSAccountUpdate_Struct *) pack->pBuffer;
if (CanUpdate()) { if (CanUpdate()) {
LogInfo( LogInfo(
+18
View File
@@ -0,0 +1,18 @@
#pragma once
struct CharCreatePointAllocation
{
unsigned int Index;
unsigned int BaseStats[7];
unsigned int DefaultPointAllocation[7];
unsigned int BaseResists[7];
};
struct CharCreateCombination {
unsigned int ExpansionRequired;
unsigned int Race;
unsigned int Class;
unsigned int Deity;
unsigned int AllocationIndex;
unsigned int Zone;
};
+11 -4
View File
@@ -25,14 +25,15 @@
#include <cstdlib> #include <cstdlib>
#include <vector> #include <vector>
#include "sof_char_create_data.h" #include "sof_char_create_data.h"
#include "race_combos.h"
#include "../common/repositories/character_instance_safereturns_repository.h" #include "../common/repositories/character_instance_safereturns_repository.h"
#include "../common/repositories/criteria/content_filter_criteria.h" #include "../common/repositories/criteria/content_filter_criteria.h"
#include "../common/zone_store.h" #include "../common/zone_store.h"
WorldDatabase database; WorldDatabase database;
WorldDatabase content_db; WorldDatabase content_db;
extern std::vector<RaceClassAllocation> character_create_allocations; extern std::vector<CharCreatePointAllocation> character_create_allocations;
extern std::vector<RaceClassCombos> character_create_race_class_combos; extern std::vector<CharCreateCombination> character_create_race_class_combos;
/** /**
@@ -807,7 +808,7 @@ bool WorldDatabase::LoadCharacterCreateAllocations()
return false; return false;
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
RaceClassAllocation allocate; CharCreatePointAllocation allocate;
allocate.Index = Strings::ToInt(row[0]); allocate.Index = Strings::ToInt(row[0]);
allocate.BaseStats[0] = Strings::ToInt(row[1]); allocate.BaseStats[0] = Strings::ToInt(row[1]);
allocate.BaseStats[3] = Strings::ToInt(row[2]); allocate.BaseStats[3] = Strings::ToInt(row[2]);
@@ -823,6 +824,12 @@ bool WorldDatabase::LoadCharacterCreateAllocations()
allocate.DefaultPointAllocation[4] = Strings::ToInt(row[12]); allocate.DefaultPointAllocation[4] = Strings::ToInt(row[12]);
allocate.DefaultPointAllocation[5] = Strings::ToInt(row[13]); allocate.DefaultPointAllocation[5] = Strings::ToInt(row[13]);
allocate.DefaultPointAllocation[6] = Strings::ToInt(row[14]); allocate.DefaultPointAllocation[6] = Strings::ToInt(row[14]);
allocate.BaseResists[0] = Strings::ToInt(row[15]);
allocate.BaseResists[1] = Strings::ToInt(row[16]);
allocate.BaseResists[2] = Strings::ToInt(row[17]);
allocate.BaseResists[3] = Strings::ToInt(row[18]);
allocate.BaseResists[4] = Strings::ToInt(row[19]);
allocate.BaseResists[5] = Strings::ToInt(row[20]);
character_create_allocations.push_back(allocate); character_create_allocations.push_back(allocate);
} }
@@ -840,7 +847,7 @@ bool WorldDatabase::LoadCharacterCreateCombos()
return false; return false;
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
RaceClassCombos combo; CharCreateCombination combo;
combo.AllocationIndex = Strings::ToInt(row[0]); combo.AllocationIndex = Strings::ToInt(row[0]);
combo.Race = Strings::ToInt(row[1]); combo.Race = Strings::ToInt(row[1]);
combo.Class = Strings::ToInt(row[2]); combo.Class = Strings::ToInt(row[2]);
+3
View File
@@ -65,6 +65,7 @@ SET(zone_sources
lua_inventory.cpp lua_inventory.cpp
lua_item.cpp lua_item.cpp
lua_iteminst.cpp lua_iteminst.cpp
lua_merc.cpp
lua_mob.cpp lua_mob.cpp
lua_mod.cpp lua_mod.cpp
lua_npc.cpp lua_npc.cpp
@@ -115,6 +116,7 @@ SET(zone_sources
perl_groups.cpp perl_groups.cpp
perl_hateentry.cpp perl_hateentry.cpp
perl_inventory.cpp perl_inventory.cpp
perl_merc.cpp
perl_mob.cpp perl_mob.cpp
perl_npc.cpp perl_npc.cpp
perl_object.cpp perl_object.cpp
@@ -224,6 +226,7 @@ SET(zone_headers
lua_inventory.h lua_inventory.h
lua_item.h lua_item.h
lua_iteminst.h lua_iteminst.h
lua_merc.h
lua_mob.h lua_mob.h
lua_mod.h lua_mod.h
lua_npc.h lua_npc.h
+5 -1
View File
@@ -2185,7 +2185,7 @@ void Client::AutoGrantAAPoints() {
SendAlternateAdvancementStats(); SendAlternateAdvancementStats();
} }
void Client::GrantAllAAPoints(uint8 unlock_level) void Client::GrantAllAAPoints(uint8 unlock_level, bool skip_grant_only)
{ {
//iterate through every AA //iterate through every AA
for (auto& aa : zone->aa_abilities) { for (auto& aa : zone->aa_abilities) {
@@ -2195,6 +2195,10 @@ void Client::GrantAllAAPoints(uint8 unlock_level)
continue; continue;
} }
if (ability->grant_only && skip_grant_only) {
continue;
}
const uint8 level = unlock_level ? unlock_level : GetLevel(); const uint8 level = unlock_level ? unlock_level : GetLevel();
AA::Rank* rank = ability->first; AA::Rank* rank = ability->first;
+59 -128
View File
@@ -1550,17 +1550,18 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
hit.damage_done = 0; hit.damage_done = 0;
} }
if (IsBot()) { parse->EventBotMerc(
if (parse->BotHasQuestSub(EVENT_USE_SKILL)) { EVENT_USE_SKILL,
const auto& export_string = fmt::format( this,
nullptr,
[&]() {
return fmt::format(
"{} {}", "{} {}",
hit.skill, hit.skill,
GetSkill(hit.skill) GetSkill(hit.skill)
); );
parse->EventBot(EVENT_USE_SKILL, CastToBot(), nullptr, export_string, 0);
}
} }
);
} }
} }
@@ -1739,7 +1740,7 @@ bool Mob::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
(HasOwner() && GetOwner()->IsClient() && other->IsClient()) (HasOwner() && GetOwner()->IsClient() && other->IsClient())
) )
) { ) {
for (auto const& [id, mob] : entity_list.GetCloseMobList(other)) { for (auto const& [id, mob] : other->GetCloseMobList()) {
if (!mob) { if (!mob) {
continue; continue;
} }
@@ -1922,11 +1923,9 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil
} }
if (killer_mob) { if (killer_mob) {
if (killer_mob->IsNPC()) { parse->EventBotMercNPC(EVENT_SLAY, killer_mob, this);
if (parse->HasQuestSub(killer_mob->GetNPCTypeID(), EVENT_SLAY)) {
parse->EventNPC(EVENT_SLAY, killer_mob->CastToNPC(), this, "", 0);
}
if (killer_mob->IsNPC()) {
killed_by = KilledByTypes::Killed_NPC; killed_by = KilledByTypes::Killed_NPC;
auto emote_id = killer_mob->GetEmoteID(); auto emote_id = killer_mob->GetEmoteID();
@@ -1934,12 +1933,6 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil
killer_mob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this); killer_mob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this);
} }
killer_mob->TrySpellOnKill(killed_level, spell);
} else if (killer_mob->IsBot()) {
if (parse->BotHasQuestSub(EVENT_SLAY)) {
parse->EventBot(EVENT_SLAY, killer_mob->CastToBot(), this, "", 0);
}
killer_mob->TrySpellOnKill(killed_level, spell); killer_mob->TrySpellOnKill(killed_level, spell);
} }
@@ -2319,8 +2312,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
//Guard Assist Code //Guard Assist Code
if (RuleB(Character, PVPEnableGuardFactionAssist)) { if (RuleB(Character, PVPEnableGuardFactionAssist)) {
if (IsClient() && other->IsClient() || (HasOwner() && GetOwner()->IsClient() && other->IsClient())) { if (IsClient() && other->IsClient() || (HasOwner() && GetOwner()->IsClient() && other->IsClient())) {
auto& mob_list = entity_list.GetCloseMobList(other); for (auto& e : other->GetCloseMobList()) {
for (auto& e : mob_list) {
auto mob = e.second; auto mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -2459,14 +2451,10 @@ void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillTyp
spell_id = SPELL_UNKNOWN; spell_id = SPELL_UNKNOWN;
//handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds
if (attacked_timer.Check()) if (attacked_timer.Check()) {
{ parse->EventMercNPC(EVENT_ATTACK, this, other);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_ATTACK)) { }
LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", other ? other->GetName() : "nullptr");
parse->EventNPC(EVENT_ATTACK, this, other, "", 0);
}
}
attacked_timer.Start(CombatEventTimer_expire); attacked_timer.Start(CombatEventTimer_expire);
if (!IsEngaged()) if (!IsEngaged())
@@ -2507,42 +2495,23 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
Mob* owner_or_self = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr; Mob* owner_or_self = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr;
if (IsNPC()) { auto exports = [&]() {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH)) { return fmt::format(
const auto& export_string = fmt::format(
"{} {} {} {}", "{} {} {} {}",
killer_mob ? killer_mob->GetID() : 0, killer_mob ? killer_mob->GetID() : 0,
damage, damage,
spell, spell,
static_cast<int>(attack_skill) static_cast<int>(attack_skill)
); );
};
if (parse->EventNPC(EVENT_DEATH, this, owner_or_self, export_string, 0) != 0) { if (parse->EventBotMercNPC(EVENT_DEATH, this, owner_or_self, exports) != 0) {
if (GetHP() < 0) { if (GetHP() < 0) {
SetHP(0); SetHP(0);
} }
return false; return false;
} }
}
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_DEATH)) {
const auto& export_string = fmt::format(
"{} {} {} {}",
killer_mob ? killer_mob->GetID() : 0,
damage,
spell,
static_cast<int>(attack_skill)
);
if (parse->EventBot(EVENT_DEATH, CastToBot(), owner_or_self, export_string, 0) != 0) {
if (GetHP() < 0) {
SetHP(0);
}
return false;
}
}
}
if (killer_mob && killer_mob->IsOfClientBot() && IsValidSpell(spell) && damage > 0) { if (killer_mob && killer_mob->IsOfClientBot() && IsValidSpell(spell) && damage > 0) {
char val1[20] = { 0 }; char val1[20] = { 0 };
@@ -2973,8 +2942,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
entity_list.UnMarkNPC(GetID()); entity_list.UnMarkNPC(GetID());
entity_list.RemoveNPC(GetID()); entity_list.RemoveNPC(GetID());
// entity_list.RemoveMobFromCloseLists(this); m_close_mobs.clear();
close_mobs.clear();
SetID(0); SetID(0);
ApplyIllusionToCorpse(illusion_spell_id, corpse); ApplyIllusionToCorpse(illusion_spell_id, corpse);
@@ -3079,10 +3047,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
} }
} }
if (killer_mob && killer_mob->IsBot()) { if (killer_mob) {
if (parse->BotHasQuestSub(EVENT_NPC_SLAY)) { parse->EventBotMerc(EVENT_NPC_SLAY, killer_mob, this);
parse->EventBot(EVENT_NPC_SLAY, killer_mob->CastToBot(), this, "", 0);
}
killer_mob->TrySpellOnKill(killed_level, spell); killer_mob->TrySpellOnKill(killed_level, spell);
} }
@@ -3110,8 +3076,14 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
} }
} }
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH_COMPLETE)) { std::vector<std::any> args = { corpse };
const auto& export_string = fmt::format(
parse->EventMercNPC(
EVENT_DEATH_COMPLETE,
this,
owner_or_self,
[&]() {
return fmt::format(
"{} {} {} {} {} {} {} {} {}", "{} {} {} {} {} {} {} {} {}",
killer_mob ? killer_mob->GetID() : 0, killer_mob ? killer_mob->GetID() : 0,
damage, damage,
@@ -3123,11 +3095,10 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
m_combat_record.GetDamageReceived(), m_combat_record.GetDamageReceived(),
m_combat_record.GetHealingReceived() m_combat_record.GetHealingReceived()
); );
},
std::vector<std::any> args = { corpse }; 0,
&args
parse->EventNPC(EVENT_DEATH_COMPLETE, this, owner_or_self, export_string, 0, &args); );
}
// Zone controller process EVENT_DEATH_ZONE (Death events) // Zone controller process EVENT_DEATH_ZONE (Death events)
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DEATH_ZONE)) { if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DEATH_ZONE)) {
@@ -4287,55 +4258,20 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
//final damage has been determined. //final damage has been determined.
int old_hp_ratio = (int)GetHPRatio(); int old_hp_ratio = (int)GetHPRatio();
const auto has_bot_given_event = parse->BotHasQuestSub(EVENT_DAMAGE_GIVEN);
const auto has_bot_taken_event = parse->BotHasQuestSub(EVENT_DAMAGE_TAKEN);
const auto has_npc_given_event = (
(
IsNPC() &&
parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_GIVEN)
) ||
(
attacker &&
attacker->IsNPC() &&
parse->HasQuestSub(attacker->CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_GIVEN)
)
);
const auto has_npc_taken_event = (
(
IsNPC() &&
parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_TAKEN)
) ||
(
attacker &&
attacker->IsNPC() &&
parse->HasQuestSub(attacker->CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_TAKEN)
)
);
const auto has_player_given_event = parse->PlayerHasQuestSub(EVENT_DAMAGE_GIVEN);
const auto has_player_taken_event = parse->PlayerHasQuestSub(EVENT_DAMAGE_TAKEN);
const auto has_given_event = (
has_bot_given_event ||
has_npc_given_event ||
has_player_given_event
);
const auto has_taken_event = (
has_bot_taken_event ||
has_npc_taken_event ||
has_player_taken_event
);
std::vector<std::any> args; std::vector<std::any> args;
int64 damage_override = 0; int64 damage_override = 0;
if (has_given_event && attacker) { if (attacker) {
const auto export_string = fmt::format( args = { this };
parse->EventMob(
EVENT_DAMAGE_GIVEN,
attacker,
this,
[&]() {
return fmt::format(
"{} {} {} {} {} {} {} {} {}", "{} {} {} {} {} {} {} {} {}",
GetID(), GetID(),
damage, damage,
@@ -4347,19 +4283,20 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
iBuffTic ? 1 : 0, iBuffTic ? 1 : 0,
static_cast<int>(special) static_cast<int>(special)
); );
},
if (attacker->IsBot() && has_bot_given_event) { 0,
parse->EventBot(EVENT_DAMAGE_GIVEN, attacker->CastToBot(), this, export_string, 0); &args
} else if (attacker->IsClient() && has_player_given_event) { );
args.push_back(this);
parse->EventPlayer(EVENT_DAMAGE_GIVEN, attacker->CastToClient(), export_string, 0, &args);
} else if (attacker->IsNPC() && has_npc_given_event) {
parse->EventNPC(EVENT_DAMAGE_GIVEN, attacker->CastToNPC(), this, export_string, 0);
}
} }
if (has_taken_event) { args = { attacker };
const auto export_string = fmt::format(
damage_override = parse->EventMob(
EVENT_DAMAGE_TAKEN,
this,
attacker,
[&]() {
return fmt::format(
"{} {} {} {} {} {} {} {} {}", "{} {} {} {} {} {} {} {} {}",
attacker ? attacker->GetID() : 0, attacker ? attacker->GetID() : 0,
damage, damage,
@@ -4371,16 +4308,10 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
iBuffTic ? 1 : 0, iBuffTic ? 1 : 0,
static_cast<int>(special) static_cast<int>(special)
); );
},
if (IsBot() && has_bot_taken_event) { 0,
damage_override = parse->EventBot(EVENT_DAMAGE_TAKEN, CastToBot(), attacker ? attacker : nullptr, export_string, 0); &args
} else if (IsClient() && has_player_taken_event) { );
args.push_back(attacker ? attacker : nullptr);
damage_override = parse->EventPlayer(EVENT_DAMAGE_TAKEN, CastToClient(), export_string, 0, &args);
} else if (IsNPC() && has_npc_taken_event) {
damage_override = parse->EventNPC(EVENT_DAMAGE_TAKEN, CastToNPC(), attacker ? attacker : nullptr, export_string, 0);
}
}
if (damage_override > 0) { if (damage_override > 0) {
damage = damage_override; damage = damage_override;
+7 -12
View File
@@ -72,7 +72,7 @@ Mob *Aura::GetOwner()
// not 100% sure how this one should work and PVP affects ... // not 100% sure how this one should work and PVP affects ...
void Aura::ProcessOnAllFriendlies(Mob *owner) void Aura::ProcessOnAllFriendlies(Mob *owner)
{ {
auto &mob_list = entity_list.GetCloseMobList(this, distance); auto &mob_list = GetCloseMobList(distance);
std::set<int> delayed_remove; std::set<int> delayed_remove;
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
@@ -127,7 +127,7 @@ void Aura::ProcessOnAllFriendlies(Mob *owner)
void Aura::ProcessOnAllGroupMembers(Mob *owner) void Aura::ProcessOnAllGroupMembers(Mob *owner)
{ {
auto &mob_list = entity_list.GetCloseMobList(this, distance); auto &mob_list = GetCloseMobList(distance);
std::set<int> delayed_remove; std::set<int> delayed_remove;
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
@@ -369,7 +369,7 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner)
void Aura::ProcessOnGroupMembersPets(Mob *owner) void Aura::ProcessOnGroupMembersPets(Mob *owner)
{ {
auto &mob_list = entity_list.GetCloseMobList(this,distance); auto &mob_list = GetCloseMobList(distance);
std::set<int> delayed_remove; std::set<int> delayed_remove;
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
// This type can either live on the pet (level 55/70 MAG aura) or on the pet owner (level 85 MAG aura) // This type can either live on the pet (level 55/70 MAG aura) or on the pet owner (level 85 MAG aura)
@@ -576,7 +576,7 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner)
void Aura::ProcessTotem(Mob *owner) void Aura::ProcessTotem(Mob *owner)
{ {
auto &mob_list = entity_list.GetCloseMobList(this, distance); auto &mob_list = GetCloseMobList(distance);
std::set<int> delayed_remove; std::set<int> delayed_remove;
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
@@ -634,9 +634,7 @@ void Aura::ProcessTotem(Mob *owner)
void Aura::ProcessEnterTrap(Mob *owner) void Aura::ProcessEnterTrap(Mob *owner)
{ {
auto &mob_list = entity_list.GetCloseMobList(this, distance); for (auto &e : GetCloseMobList(distance)) {
for (auto &e : mob_list) {
auto mob = e.second; auto mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -656,9 +654,7 @@ void Aura::ProcessEnterTrap(Mob *owner)
void Aura::ProcessExitTrap(Mob *owner) void Aura::ProcessExitTrap(Mob *owner)
{ {
auto &mob_list = entity_list.GetCloseMobList(this, distance); for (auto &e : GetCloseMobList(distance)) {
for (auto &e : mob_list) {
auto mob = e.second; auto mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -689,8 +685,7 @@ void Aura::ProcessExitTrap(Mob *owner)
// and hard to reason about // and hard to reason about
void Aura::ProcessSpawns() void Aura::ProcessSpawns()
{ {
const auto &clients = entity_list.GetCloseMobList(this, distance); for (auto &e: GetCloseMobList(distance)) {
for (auto &e : clients) {
if (!e.second) { if (!e.second) {
continue; continue;
} }
+2 -12
View File
@@ -1578,17 +1578,7 @@ bool Bot::Process()
return false; return false;
} }
if (mob_close_scan_timer.Check()) { ScanCloseMobProcess();
LogAIScanCloseDetail(
"is_moving [{}] bot [{}] timer [{}]",
moving ? "true" : "false",
GetCleanName(),
mob_close_scan_timer.GetDuration()
);
entity_list.ScanCloseMobs(close_mobs, this, IsMoving());
}
SpellProcess(); SpellProcess();
if (tic_timer.Check()) { if (tic_timer.Check()) {
@@ -2978,7 +2968,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) {
if (NOT_HOLDING && NOT_PASSIVE) { if (NOT_HOLDING && NOT_PASSIVE) {
auto attack_target = bot_owner->GetTarget(); auto attack_target = bot_owner->GetTarget();
if (attack_target) { if (attack_target && HasBotAttackFlag(attack_target)) {
InterruptSpell(); InterruptSpell();
WipeHateList(); WipeHateList();
+5
View File
@@ -35,6 +35,11 @@ void bot_command_attack(Client *c, const Seperator *sep)
return; return;
} }
if (!c->HasBotAttackFlag(target_mob)) {
target_mob->SetBotAttackFlag(c->CharacterID());
target_mob->bot_attack_flag_timer.Start(10000);
}
size_t attacker_count = 0; size_t attacker_count = 0;
Bot *first_attacker = nullptr; Bot *first_attacker = nullptr;
sbl.remove(nullptr); sbl.remove(nullptr);
+11 -3
View File
@@ -857,7 +857,7 @@ void bot_command_spawn(Client *c, const Seperator *sep)
c->Message( c->Message(
Chat::White, Chat::White,
fmt::format( fmt::format(
"Usage: {} [bot_name]", "Usage: {} [bot_name] [optional: silent]",
sep->arg[0] sep->arg[0]
).c_str() ).c_str()
); );
@@ -1045,9 +1045,17 @@ void bot_command_spawn(Client *c, const Seperator *sep)
message_index = VALIDATECLASSID(my_bot->GetClass()); message_index = VALIDATECLASSID(my_bot->GetClass());
} }
if (c->GetBotOption(Client::booSpawnMessageSay)) { std::string silent_confirm = sep->arg[2];
bool silentTell = false;
if (!silent_confirm.compare("silent")) {
silentTell = true;
}
if (!silentTell && c->GetBotOption(Client::booSpawnMessageSay)) {
Bot::BotGroupSay(my_bot, bot_spawn_message[message_index].c_str()); Bot::BotGroupSay(my_bot, bot_spawn_message[message_index].c_str());
} else if (c->GetBotOption(Client::booSpawnMessageTell)) { }
else if (!silentTell && c->GetBotOption(Client::booSpawnMessageTell)) {
my_bot->OwnerMessage(bot_spawn_message[message_index]); my_bot->OwnerMessage(bot_spawn_message[message_index]);
} }
} }
+3 -1
View File
@@ -312,10 +312,12 @@ void Client::SpawnRaidBotsOnConnect(Raid* raid) {
for (const auto& m: r_members) { for (const auto& m: r_members) {
if (strlen(m.member_name) != 0) { if (strlen(m.member_name) != 0) {
for (const auto& b: bots_list) { for (const auto& b : bots_list) {
if (strcmp(m.member_name, b.bot_name) == 0) { if (strcmp(m.member_name, b.bot_name) == 0) {
std::string buffer = "^spawn "; std::string buffer = "^spawn ";
buffer.append(m.member_name); buffer.append(m.member_name);
std::string silent = " silent";
buffer.append(silent);
bot_command_real_dispatch(this, buffer.c_str()); bot_command_real_dispatch(this, buffer.c_str());
auto bot = entity_list.GetBotByBotName(m.member_name); auto bot = entity_list.GetBotByBotName(m.member_name);
+201 -106
View File
@@ -150,14 +150,14 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
process_timer(100), process_timer(100),
consume_food_timer(CONSUMPTION_TIMER), consume_food_timer(CONSUMPTION_TIMER),
zoneinpacket_timer(1000), zoneinpacket_timer(1000),
linkdead_timer(RuleI(Zone,ClientLinkdeadMS)), linkdead_timer(RuleI(Zone, ClientLinkdeadMS)),
dead_timer(2000), dead_timer(2000),
global_channel_timer(1000), global_channel_timer(1000),
fishing_timer(8000), fishing_timer(8000),
endupkeep_timer(1000), endupkeep_timer(1000),
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)), m_client_npc_aggro_scan_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)),
client_zone_wide_full_position_update_timer(5 * 60 * 1000), m_client_zone_wide_full_position_update_timer(5 * 60 * 1000),
tribute_timer(Tribute_duration), tribute_timer(Tribute_duration),
proximity_timer(ClientProximity_interval), proximity_timer(ClientProximity_interval),
TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000),
@@ -176,13 +176,12 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
helm_toggle_timer(250), helm_toggle_timer(250),
aggro_meter_timer(AGGRO_METER_UPDATE_MS), aggro_meter_timer(AGGRO_METER_UPDATE_MS),
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f,-2.0f), m_ZoneSummonLocation(-2.0f, -2.0f, -2.0f, -2.0f),
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f), m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f),
last_region_type(RegionTypeUnsupported), last_region_type(RegionTypeUnsupported),
m_dirtyautohaters(false), m_dirtyautohaters(false),
mob_close_scan_timer(6000), m_position_update_timer(10000),
position_update_timer(10000),
consent_throttle_timer(2000), consent_throttle_timer(2000),
tmSitting(0), tmSitting(0),
parcel_timer(RuleI(Parcel, ParcelDeliveryDelay)), parcel_timer(RuleI(Parcel, ParcelDeliveryDelay)),
@@ -446,7 +445,7 @@ Client::~Client() {
m_tradeskill_object = nullptr; m_tradeskill_object = nullptr;
} }
close_mobs.clear(); m_close_mobs.clear();
if(IsDueling() && GetDuelTarget() != 0) { if(IsDueling() && GetDuelTarget() != 0) {
Entity* entity = entity_list.GetID(GetDuelTarget()); Entity* entity = entity_list.GetID(GetDuelTarget());
@@ -1244,45 +1243,28 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
entity_list.ProcessProximitySay(message, this, language); entity_list.ProcessProximitySay(message, this, language);
} }
if ( Mob* t = GetTarget();
GetTarget() &&
GetTarget()->IsNPC() &&
!IsInvisible(GetTarget())
) {
auto* t = GetTarget()->CastToNPC();
if (!t->IsEngaged()) {
CheckLDoNHail(t);
CheckEmoteHail(t, message);
if (DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) { if (
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_SAY)) { t &&
parse->EventNPC(EVENT_SAY, t, this, message, language); !IsInvisible(t) &&
DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)
) {
const bool is_engaged = t->IsEngaged();
if (is_engaged) {
parse->EventBotMercNPC(EVENT_AGGRO_SAY, t, this, [&]() { return message; }, language);
} else {
parse->EventBotMercNPC(EVENT_SAY, t, this, [&]() { return message; }, language);
} }
if (t->IsNPC() && !is_engaged) {
CheckLDoNHail(t->CastToNPC());
CheckEmoteHail(t->CastToNPC(), message);
if (RuleB(TaskSystem, EnableTaskSystem)) { if (RuleB(TaskSystem, EnableTaskSystem)) {
if (UpdateTasksOnSpeakWith(t)) { if (UpdateTasksOnSpeakWith(t->CastToNPC())) {
t->DoQuestPause(this); t->CastToNPC()->DoQuestPause(this);
}
}
}
} else {
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_AGGRO_SAY)) {
if (DistanceSquaredNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) {
parse->EventNPC(EVENT_AGGRO_SAY, t, this, message, language);
}
}
}
}
else if (GetTarget() && GetTarget()->IsBot() && !IsInvisible(GetTarget())) {
if (DistanceNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) {
if (GetTarget()->IsEngaged()) {
if (parse->BotHasQuestSub(EVENT_AGGRO_SAY)) {
parse->EventBot(EVENT_AGGRO_SAY, GetTarget()->CastToBot(), this, message, language);
}
} else {
if (parse->BotHasQuestSub(EVENT_SAY)) {
parse->EventBot(EVENT_SAY, GetTarget()->CastToBot(), this, message, language);
} }
} }
} }
@@ -2300,52 +2282,67 @@ void Client::SetGM(bool toggle) {
UpdateWho(); UpdateWho();
} }
void Client::ReadBook(BookRequest_Struct *book) { void Client::ReadBook(BookRequest_Struct* book)
int16 book_language=0; {
char *txtfile = book->txtfile; const std::string& text_file = book->txtfile;
if(txtfile[0] == '0' && txtfile[1] == '\0') { if (text_file.empty()) {
//invalid book... coming up on non-book items.
return; return;
} }
std::string booktxt2 = content_db.GetBook(txtfile, &book_language); auto b = content_db.GetBook(text_file);
int length = booktxt2.length();
if (booktxt2[0] != '\0') { if (!b.text.empty()) {
#if EQDEBUG >= 6 auto outapp = new EQApplicationPacket(OP_ReadBook, b.text.size() + sizeof(BookText_Struct));
LogInfo("Client::ReadBook() textfile:[{}] Text:[{}]", txtfile, booktxt2.c_str()); auto inst = const_cast<EQ::ItemInstance*>(m_inv[book->invslot]);
#endif
auto outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct));
BookText_Struct *out = (BookText_Struct *) outapp->pBuffer; auto t = (BookText_Struct*) outapp->pBuffer;
out->window = book->window;
out->type = book->type;
out->invslot = book->invslot;
out->target_id = book->target_id;
out->can_cast = 0; // todo: implement
out->can_scribe = false;
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && book->invslot <= EQ::invbag::GENERAL_BAGS_END) t->window = book->window;
{ t->type = book->type;
const EQ::ItemInstance* inst = m_inv[book->invslot]; t->invslot = book->invslot;
if (inst && inst->GetItem()) t->target_id = book->target_id;
{ t->can_cast = 0; // todo: implement
auto recipe = TradeskillRecipeRepository::GetWhere(content_db, t->can_scribe = false;
fmt::format("learned_by_item_id = {} LIMIT 1", inst->GetItem()->ID));
out->type = inst->GetItem()->Book; if (ClientVersion() >= EQ::versions::ClientVersion::SoF && book->invslot <= EQ::invbag::GENERAL_BAGS_END) {
out->can_scribe = !recipe.empty(); if (inst && inst->GetItem()) {
auto recipe = TradeskillRecipeRepository::GetWhere(
content_db,
fmt::format(
"learned_by_item_id = {} LIMIT 1",
inst->GetItem()->ID
)
);
t->type = inst->GetItem()->Book;
t->can_scribe = !recipe.empty();
} }
} }
memcpy(out->booktext, booktxt2.c_str(), length); memcpy(t->booktext, b.text.c_str(), b.text.size());
if (EQ::ValueWithin(book_language, Language::CommonTongue, Language::Unknown27)) { if (EQ::ValueWithin(b.language, Language::CommonTongue, Language::Unknown27)) {
if (m_pp.languages[book_language] < Language::MaxValue) { if (m_pp.languages[b.language] < Language::MaxValue) {
GarbleMessage(out->booktext, (Language::MaxValue - m_pp.languages[book_language])); GarbleMessage(t->booktext, (Language::MaxValue - m_pp.languages[b.language]));
} }
} }
// Send only books and scrolls to this event
if (parse->PlayerHasQuestSub(EVENT_READ_ITEM) && t->type != BookType::ItemInfo) {
std::vector<std::any> args = {
b.text,
t->can_cast,
t->can_scribe,
t->invslot,
t->target_id,
t->type,
inst
};
parse->EventPlayer(EVENT_READ_ITEM, this, book->txtfile, inst ? inst->GetID() : 0, &args);
}
QueuePacket(outapp); QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
} }
@@ -7659,10 +7656,10 @@ void Client::SetFactionLevel(
content_db.GetFactionData(&faction_modifiers, class_id, race_id, deity_id, e.faction_id); content_db.GetFactionData(&faction_modifiers, class_id, race_id, deity_id, e.faction_id);
if (is_quest) { if (is_quest) {
if (e.npc_value > 0) { if (e.value > 0) {
e.npc_value = -std::abs(e.npc_value); e.value = -std::abs(e.value);
} else if (e.npc_value < 0) { } else if (e.value < 0) {
e.npc_value = std::abs(e.npc_value); e.value = std::abs(e.value);
} }
} }
@@ -9253,19 +9250,6 @@ bool Client::GotoPlayerRaid(const std::string& player_name)
return true; return true;
} }
glm::vec4 &Client::GetLastPositionBeforeBulkUpdate()
{
return last_position_before_bulk_update;
}
/**
* @param in_last_position_before_bulk_update
*/
void Client::SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update)
{
Client::last_position_before_bulk_update = in_last_position_before_bulk_update;
}
void Client::SendToGuildHall() void Client::SendToGuildHall()
{ {
std::string zone_short_name = "guildhall"; std::string zone_short_name = "guildhall";
@@ -10930,23 +10914,24 @@ void Client::SetIPExemption(int exemption_amount)
void Client::ReadBookByName(std::string book_name, uint8 book_type) void Client::ReadBookByName(std::string book_name, uint8 book_type)
{ {
int16 book_language = 0; auto b = content_db.GetBook(book_name);
std::string book_text = content_db.GetBook(book_name.c_str(), &book_language);
int length = book_text.length();
if (book_text[0] != '\0') { if (!b.text.empty()) {
LogDebug("Client::ReadBookByName() Book Name: [{}] Text: [{}]", book_name, book_text.c_str()); LogDebug("Book Name: [{}] Text: [{}]", book_name, b.text);
auto outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct));
BookText_Struct *out = (BookText_Struct *) outapp->pBuffer;
out->window = 0xFF;
out->type = book_type;
out->invslot = 0;
memcpy(out->booktext, book_text.c_str(), length); auto outapp = new EQApplicationPacket(OP_ReadBook, b.text.size() + sizeof(BookText_Struct));
if (EQ::ValueWithin(book_language, Language::CommonTongue, Language::Unknown27)) { auto o = (BookText_Struct *) outapp->pBuffer;
if (m_pp.languages[book_language] < Language::MaxValue) {
GarbleMessage(out->booktext, (Language::MaxValue - m_pp.languages[book_language])); o->window = std::numeric_limits<uint8>::max();
o->type = book_type;
o->invslot = 0;
memcpy(o->booktext, b.text.c_str(), b.text.size());
if (EQ::ValueWithin(b.language, Language::CommonTongue, Language::Unknown27)) {
if (m_pp.languages[b.language] < Language::MaxValue) {
GarbleMessage(o->booktext, (Language::MaxValue - m_pp.languages[b.language]));
} }
} }
@@ -11097,7 +11082,9 @@ void Client::SaveDisciplines()
{ {
std::vector<CharacterDisciplinesRepository::CharacterDisciplines> v; std::vector<CharacterDisciplinesRepository::CharacterDisciplines> v;
for (int slot_id = 0; slot_id < MAX_PP_DISCIPLINES; slot_id++) { std::vector<std::string> delete_slots;
for (uint16 slot_id = 0; slot_id < MAX_PP_DISCIPLINES; slot_id++) {
if (IsValidSpell(m_pp.disciplines.values[slot_id])) { if (IsValidSpell(m_pp.disciplines.values[slot_id])) {
auto e = CharacterDisciplinesRepository::NewEntity(); auto e = CharacterDisciplinesRepository::NewEntity();
@@ -11106,9 +11093,22 @@ void Client::SaveDisciplines()
e.disc_id = m_pp.disciplines.values[slot_id]; e.disc_id = m_pp.disciplines.values[slot_id];
v.emplace_back(e); v.emplace_back(e);
} else {
delete_slots.emplace_back(std::to_string(slot_id));
} }
} }
if (!delete_slots.empty()) {
CharacterDisciplinesRepository::DeleteWhere(
database,
fmt::format(
"`id` = {} AND `slot_id` IN ({})",
CharacterID(),
Strings::Join(delete_slots, ", ")
)
);
}
if (!v.empty()) { if (!v.empty()) {
CharacterDisciplinesRepository::ReplaceMany(database, v); CharacterDisciplinesRepository::ReplaceMany(database, v);
} }
@@ -12734,3 +12734,98 @@ void Client::SendTopLevelInventory()
} }
} }
} }
// On a normal basis we limit mob movement updates based on distance
// This ensures we send a periodic full zone update to a client that has started moving after 5 or so minutes
//
// For very large zones we will also force a full update based on distance
//
// We ignore a small distance around us so that we don't interrupt already pathing deltas as those npcs will appear
// to full stop when they are actually still pathing
void Client::CheckSendBulkClientPositionUpdate()
{
float distance_moved = DistanceNoZ(m_last_position_before_bulk_update, GetPosition());
bool moved_far_enough_before_bulk_update = distance_moved >= zone->GetNpcPositionUpdateDistance();
bool is_ready_to_update = (
m_client_zone_wide_full_position_update_timer.Check() || moved_far_enough_before_bulk_update
);
if (IsMoving() && is_ready_to_update) {
LogDebug("[[{}]] Client Zone Wide Position Update NPCs", GetCleanName());
auto &mob_movement_manager = MobMovementManager::Get();
auto &mob_list = entity_list.GetMobList();
for (auto &it : mob_list) {
Mob *entity = it.second;
if (!entity->IsNPC()) {
continue;
}
int animation_speed = 0;
if (entity->IsMoving()) {
if (entity->IsRunning()) {
animation_speed = (entity->IsFeared() ? entity->GetFearSpeed() : entity->GetRunspeed());
}
else {
animation_speed = entity->GetWalkspeed();
}
}
mob_movement_manager.SendCommandToClients(entity, 0.0, 0.0, 0.0, 0.0, animation_speed, ClientRangeAny, this);
}
m_last_position_before_bulk_update = GetPosition();
}
}
const uint16 scan_npc_aggro_timer_idle = RuleI(Aggro, ClientAggroCheckIdleInterval);
const uint16 scan_npc_aggro_timer_moving = RuleI(Aggro, ClientAggroCheckMovingInterval);
void Client::CheckClientToNpcAggroTimer()
{
LogAggroDetail(
"ClientUpdate [{}] {}moving, scan timer [{}]",
GetCleanName(),
IsMoving() ? "" : "NOT ",
m_client_npc_aggro_scan_timer.GetRemainingTime()
);
if (IsMoving()) {
if (m_client_npc_aggro_scan_timer.GetRemainingTime() > scan_npc_aggro_timer_moving) {
LogAggroDetail("Client [{}] Restarting with moving timer", GetCleanName());
m_client_npc_aggro_scan_timer.Disable();
m_client_npc_aggro_scan_timer.Start(scan_npc_aggro_timer_moving);
m_client_npc_aggro_scan_timer.Trigger();
}
}
else if (m_client_npc_aggro_scan_timer.GetDuration() == scan_npc_aggro_timer_moving) {
LogAggroDetail("Client [{}] Restarting with idle timer", GetCleanName());
m_client_npc_aggro_scan_timer.Disable();
m_client_npc_aggro_scan_timer.Start(scan_npc_aggro_timer_idle);
}
}
void Client::ClientToNpcAggroProcess()
{
if (zone->CanDoCombat() && !GetFeigned() && m_client_npc_aggro_scan_timer.Check()) {
int npc_scan_count = 0;
for (auto &close_mob: GetCloseMobList()) {
Mob *mob = close_mob.second;
if (!mob) {
continue;
}
if (mob->IsClient()) {
continue;
}
if (mob->CheckWillAggro(this) && !mob->CheckAggro(this)) {
mob->AddToHateList(this, 25);
}
npc_scan_count++;
}
LogAggro("Checking Reverse Aggro (client->npc) scanned_npcs ([{}])", npc_scan_count);
}
}
+14 -10
View File
@@ -196,6 +196,7 @@ struct RespawnOption
float heading; float heading;
}; };
// do not ask what all these mean because I have no idea! // do not ask what all these mean because I have no idea!
// named from the client's CEverQuest::GetInnateDesc, they're missing some // named from the client's CEverQuest::GetInnateDesc, they're missing some
enum eInnateSkill { enum eInnateSkill {
@@ -596,7 +597,7 @@ public:
inline uint8 GetBaseINT() const { return m_pp.INT; } inline uint8 GetBaseINT() const { return m_pp.INT; }
inline uint8 GetBaseAGI() const { return m_pp.AGI; } inline uint8 GetBaseAGI() const { return m_pp.AGI; }
inline uint8 GetBaseWIS() const { return m_pp.WIS; } inline uint8 GetBaseWIS() const { return m_pp.WIS; }
inline uint8 GetBaseCorrup() const { return 15; } // Same for all inline uint8 GetBaseCorrup() const { return m_pp.corruption_resist; }
inline uint8 GetBasePhR() const { return 0; } // Guessing at 0 as base inline uint8 GetBasePhR() const { return 0; } // Guessing at 0 as base
inline virtual int32 GetHeroicSTR() const { return itembonuses.HeroicSTR; } inline virtual int32 GetHeroicSTR() const { return itembonuses.HeroicSTR; }
@@ -1025,7 +1026,7 @@ public:
int GetSpentAA() { return m_pp.aapoints_spent; } int GetSpentAA() { return m_pp.aapoints_spent; }
uint32 GetRequiredAAExperience(); uint32 GetRequiredAAExperience();
void AutoGrantAAPoints(); void AutoGrantAAPoints();
void GrantAllAAPoints(uint8 unlock_level = 0); void GrantAllAAPoints(uint8 unlock_level = 0, bool skip_grant_only = false);
bool HasAlreadyPurchasedRank(AA::Rank* rank); bool HasAlreadyPurchasedRank(AA::Rank* rank);
void ListPurchasedAAs(Client *to, std::string search_criteria = std::string()); void ListPurchasedAAs(Client *to, std::string search_criteria = std::string());
@@ -1794,9 +1795,6 @@ public:
uint32 trapid; //ID of trap player has triggered. This is cleared when the player leaves the trap's radius, or it despawns. uint32 trapid; //ID of trap player has triggered. This is cleared when the player leaves the trap's radius, or it despawns.
void SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update);
glm::vec4 &GetLastPositionBeforeBulkUpdate();
Raid *p_raid_instance; Raid *p_raid_instance;
void ShowDevToolsMenu(); void ShowDevToolsMenu();
@@ -2033,8 +2031,6 @@ private:
Timer fishing_timer; Timer fishing_timer;
Timer endupkeep_timer; Timer endupkeep_timer;
Timer autosave_timer; Timer autosave_timer;
Timer client_scan_npc_aggro_timer;
Timer client_zone_wide_full_position_update_timer;
Timer tribute_timer; Timer tribute_timer;
Timer proximity_timer; Timer proximity_timer;
@@ -2051,8 +2047,6 @@ private:
Timer afk_toggle_timer; Timer afk_toggle_timer;
Timer helm_toggle_timer; Timer helm_toggle_timer;
Timer aggro_meter_timer; Timer aggro_meter_timer;
Timer mob_close_scan_timer;
Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */
Timer consent_throttle_timer; Timer consent_throttle_timer;
Timer dynamiczone_removal_timer; Timer dynamiczone_removal_timer;
Timer task_request_timer; Timer task_request_timer;
@@ -2065,7 +2059,17 @@ private:
int m_lazy_load_sent_bank_slots = 0; int m_lazy_load_sent_bank_slots = 0;
glm::vec3 m_Proximity; glm::vec3 m_Proximity;
glm::vec4 last_position_before_bulk_update;
// client aggro
Timer m_client_npc_aggro_scan_timer;
void CheckClientToNpcAggroTimer();
void ClientToNpcAggroProcess();
// bulk position updates
glm::vec4 m_last_position_before_bulk_update;
Timer m_client_zone_wide_full_position_update_timer;
Timer m_position_update_timer;
void CheckSendBulkClientPositionUpdate();
void BulkSendInventoryItems(); void BulkSendInventoryItems();
+5 -295
View File
@@ -1009,65 +1009,7 @@ int Client::CalcHaste()
//in Mob::ResistSpell //in Mob::ResistSpell
int32 Client::CalcMR() int32 Client::CalcMR()
{ {
//racial bases MR = m_pp.magic_resist;
switch (GetBaseRace()) {
case HUMAN:
MR = 25;
break;
case BARBARIAN:
MR = 25;
break;
case ERUDITE:
MR = 30;
break;
case WOOD_ELF:
MR = 25;
break;
case HIGH_ELF:
MR = 25;
break;
case DARK_ELF:
MR = 25;
break;
case HALF_ELF:
MR = 25;
break;
case DWARF:
MR = 30;
break;
case TROLL:
MR = 25;
break;
case OGRE:
MR = 25;
break;
case HALFLING:
MR = 25;
break;
case GNOME:
MR = 25;
break;
case IKSAR:
MR = 25;
break;
case VAHSHIR:
MR = 25;
break;
case FROGLOK:
MR = 30;
break;
case DRAKKIN:
{
MR = 25;
if (GetDrakkinHeritage() == 2)
MR += 10;
else if (GetDrakkinHeritage() == 5)
MR += 2;
break;
}
default:
MR = 20;
}
MR += itembonuses.MR + spellbonuses.MR + aabonuses.MR; MR += itembonuses.MR + spellbonuses.MR + aabonuses.MR;
if (GetClass() == Class::Warrior || GetClass() == Class::Berserker) { if (GetClass() == Class::Warrior || GetClass() == Class::Berserker) {
MR += GetLevel() / 2; MR += GetLevel() / 2;
@@ -1083,65 +1025,7 @@ int32 Client::CalcMR()
int32 Client::CalcFR() int32 Client::CalcFR()
{ {
//racial bases FR = m_pp.fire_resist;
switch (GetBaseRace()) {
case HUMAN:
FR = 25;
break;
case BARBARIAN:
FR = 25;
break;
case ERUDITE:
FR = 25;
break;
case WOOD_ELF:
FR = 25;
break;
case HIGH_ELF:
FR = 25;
break;
case DARK_ELF:
FR = 25;
break;
case HALF_ELF:
FR = 25;
break;
case DWARF:
FR = 25;
break;
case TROLL:
FR = 5;
break;
case OGRE:
FR = 25;
break;
case HALFLING:
FR = 25;
break;
case GNOME:
FR = 25;
break;
case IKSAR:
FR = 30;
break;
case VAHSHIR:
FR = 25;
break;
case FROGLOK:
FR = 25;
break;
case DRAKKIN:
{
FR = 25;
if (GetDrakkinHeritage() == 0)
FR += 10;
else if (GetDrakkinHeritage() == 5)
FR += 2;
break;
}
default:
FR = 20;
}
int c = GetClass(); int c = GetClass();
if (c == Class::Ranger) { if (c == Class::Ranger) {
FR += 4; FR += 4;
@@ -1169,65 +1053,7 @@ int32 Client::CalcFR()
int32 Client::CalcDR() int32 Client::CalcDR()
{ {
//racial bases DR = m_pp.disease_resist;
switch (GetBaseRace()) {
case HUMAN:
DR = 15;
break;
case BARBARIAN:
DR = 15;
break;
case ERUDITE:
DR = 10;
break;
case WOOD_ELF:
DR = 15;
break;
case HIGH_ELF:
DR = 15;
break;
case DARK_ELF:
DR = 15;
break;
case HALF_ELF:
DR = 15;
break;
case DWARF:
DR = 15;
break;
case TROLL:
DR = 15;
break;
case OGRE:
DR = 15;
break;
case HALFLING:
DR = 20;
break;
case GNOME:
DR = 15;
break;
case IKSAR:
DR = 15;
break;
case VAHSHIR:
DR = 15;
break;
case FROGLOK:
DR = 15;
break;
case DRAKKIN:
{
DR = 15;
if (GetDrakkinHeritage() == 1)
DR += 10;
else if (GetDrakkinHeritage() == 5)
DR += 2;
break;
}
default:
DR = 15;
}
int c = GetClass(); int c = GetClass();
// the monk one is part of base resist // the monk one is part of base resist
if (c == Class::Monk) { if (c == Class::Monk) {
@@ -1261,65 +1087,7 @@ int32 Client::CalcDR()
int32 Client::CalcPR() int32 Client::CalcPR()
{ {
//racial bases PR = m_pp.poison_resist;
switch (GetBaseRace()) {
case HUMAN:
PR = 15;
break;
case BARBARIAN:
PR = 15;
break;
case ERUDITE:
PR = 15;
break;
case WOOD_ELF:
PR = 15;
break;
case HIGH_ELF:
PR = 15;
break;
case DARK_ELF:
PR = 15;
break;
case HALF_ELF:
PR = 15;
break;
case DWARF:
PR = 20;
break;
case TROLL:
PR = 15;
break;
case OGRE:
PR = 15;
break;
case HALFLING:
PR = 20;
break;
case GNOME:
PR = 15;
break;
case IKSAR:
PR = 15;
break;
case VAHSHIR:
PR = 15;
break;
case FROGLOK:
PR = 30;
break;
case DRAKKIN:
{
PR = 15;
if (GetDrakkinHeritage() == 3)
PR += 10;
else if (GetDrakkinHeritage() == 5)
PR += 2;
break;
}
default:
PR = 15;
}
int c = GetClass(); int c = GetClass();
// this monk bonus is part of the base // this monk bonus is part of the base
if (c == Class::Monk) { if (c == Class::Monk) {
@@ -1353,65 +1121,7 @@ int32 Client::CalcPR()
int32 Client::CalcCR() int32 Client::CalcCR()
{ {
//racial bases CR = m_pp.cold_resist;
switch (GetBaseRace()) {
case HUMAN:
CR = 25;
break;
case BARBARIAN:
CR = 35;
break;
case ERUDITE:
CR = 25;
break;
case WOOD_ELF:
CR = 25;
break;
case HIGH_ELF:
CR = 25;
break;
case DARK_ELF:
CR = 25;
break;
case HALF_ELF:
CR = 25;
break;
case DWARF:
CR = 25;
break;
case TROLL:
CR = 25;
break;
case OGRE:
CR = 25;
break;
case HALFLING:
CR = 25;
break;
case GNOME:
CR = 25;
break;
case IKSAR:
CR = 15;
break;
case VAHSHIR:
CR = 25;
break;
case FROGLOK:
CR = 25;
break;
case DRAKKIN:
{
CR = 25;
if (GetDrakkinHeritage() == 4)
CR += 10;
else if (GetDrakkinHeritage() == 5)
CR += 2;
break;
}
default:
CR = 25;
}
int c = GetClass(); int c = GetClass();
if (c == Class::Ranger || c == Class::Beastlord) { if (c == Class::Ranger || c == Class::Beastlord) {
CR += 4; CR += 4;
+13 -115
View File
@@ -794,7 +794,7 @@ void Client::CompleteConnect()
// sent to a succor point // sent to a succor point
SendMobPositions(); SendMobPositions();
SetLastPositionBeforeBulkUpdate(GetPosition()); m_last_position_before_bulk_update = GetPosition();
/* This sub event is for if a player logs in for the first time since entering world. */ /* This sub event is for if a player logs in for the first time since entering world. */
if (firstlogon == 1) { if (firstlogon == 1) {
@@ -940,7 +940,7 @@ void Client::CompleteConnect()
worldserver.RequestTellQueue(GetName()); worldserver.RequestTellQueue(GetName());
entity_list.ScanCloseMobs(close_mobs, this, true); entity_list.ScanCloseMobs(this);
if (GetGM() && IsDevToolsEnabled()) { if (GetGM() && IsDevToolsEnabled()) {
ShowDevToolsMenu(); ShowDevToolsMenu();
@@ -5012,103 +5012,9 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
SetMoving(!(cy == m_Position.y && cx == m_Position.x)); SetMoving(!(cy == m_Position.y && cx == m_Position.x));
/** CheckClientToNpcAggroTimer();
* Client aggro scanning CheckScanCloseMobsMovingTimer();
*/ CheckSendBulkClientPositionUpdate();
const uint16 client_scan_npc_aggro_timer_idle = RuleI(Aggro, ClientAggroCheckIdleInterval);
const uint16 client_scan_npc_aggro_timer_moving = RuleI(Aggro, ClientAggroCheckMovingInterval);
LogAggroDetail(
"ClientUpdate [{}] {}moving, scan timer [{}]",
GetCleanName(),
IsMoving() ? "" : "NOT ",
client_scan_npc_aggro_timer.GetRemainingTime()
);
if (IsMoving()) {
if (client_scan_npc_aggro_timer.GetRemainingTime() > client_scan_npc_aggro_timer_moving) {
LogAggroDetail("Client [{}] Restarting with moving timer", GetCleanName());
client_scan_npc_aggro_timer.Disable();
client_scan_npc_aggro_timer.Start(client_scan_npc_aggro_timer_moving);
client_scan_npc_aggro_timer.Trigger();
}
}
else if (client_scan_npc_aggro_timer.GetDuration() == client_scan_npc_aggro_timer_moving) {
LogAggroDetail("Client [{}] Restarting with idle timer", GetCleanName());
client_scan_npc_aggro_timer.Disable();
client_scan_npc_aggro_timer.Start(client_scan_npc_aggro_timer_idle);
}
/**
* Client mob close list cache scan timer
*/
const uint16 client_mob_close_scan_timer_moving = 6000;
const uint16 client_mob_close_scan_timer_idle = 60000;
LogAIScanCloseDetail(
"Client [{}] {}moving, scan timer [{}]",
GetCleanName(),
IsMoving() ? "" : "NOT ",
mob_close_scan_timer.GetRemainingTime()
);
if (IsMoving()) {
if (mob_close_scan_timer.GetRemainingTime() > client_mob_close_scan_timer_moving) {
LogAIScanCloseDetail("Client [{}] Restarting with moving timer", GetCleanName());
mob_close_scan_timer.Disable();
mob_close_scan_timer.Start(client_mob_close_scan_timer_moving);
mob_close_scan_timer.Trigger();
}
}
else if (mob_close_scan_timer.GetDuration() == client_mob_close_scan_timer_moving) {
LogAIScanCloseDetail("Client [{}] Restarting with idle timer", GetCleanName());
mob_close_scan_timer.Disable();
mob_close_scan_timer.Start(client_mob_close_scan_timer_idle);
}
/**
* On a normal basis we limit mob movement updates based on distance
* This ensures we send a periodic full zone update to a client that has started moving after 5 or so minutes
*
* For very large zones we will also force a full update based on distance
*
* We ignore a small distance around us so that we don't interrupt already pathing deltas as those npcs will appear
* to full stop when they are actually still pathing
*/
float distance_moved = DistanceNoZ(GetLastPositionBeforeBulkUpdate(), GetPosition());
bool moved_far_enough_before_bulk_update = distance_moved >= zone->GetNpcPositionUpdateDistance();
bool is_ready_to_update = (
client_zone_wide_full_position_update_timer.Check() || moved_far_enough_before_bulk_update
);
if (IsMoving() && is_ready_to_update) {
LogDebug("[[{}]] Client Zone Wide Position Update NPCs", GetCleanName());
auto &mob_movement_manager = MobMovementManager::Get();
auto &mob_list = entity_list.GetMobList();
for (auto &it : mob_list) {
Mob *entity = it.second;
if (!entity->IsNPC()) {
continue;
}
int animation_speed = 0;
if (entity->IsMoving()) {
if (entity->IsRunning()) {
animation_speed = (entity->IsFeared() ? entity->GetFearSpeed() : entity->GetRunspeed());
}
else {
animation_speed = entity->GetWalkspeed();
}
}
mob_movement_manager.SendCommandToClients(entity, 0.0, 0.0, 0.0, 0.0, animation_speed, ClientRangeAny, this);
}
SetLastPositionBeforeBulkUpdate(GetPosition());
}
int32 new_animation = ppu->animation; int32 new_animation = ppu->animation;
@@ -12078,15 +11984,7 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app)
auto t = GetTarget(); auto t = GetTarget();
if (t) { if (t) {
if (t->IsNPC()) { parse->EventBotMercNPC(EVENT_POPUP_RESPONSE, t, this, [&]() { return std::to_string(popup_response->popupid); });
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_POPUP_RESPONSE)) {
parse->EventNPC(EVENT_POPUP_RESPONSE, t->CastToNPC(), this, std::to_string(popup_response->popupid), 0);
}
} else if (t->IsBot()) {
if (parse->BotHasQuestSub(EVENT_POPUP_RESPONSE)) {
parse->EventBot(EVENT_POPUP_RESPONSE, t->CastToBot(), this, std::to_string(popup_response->popupid), 0);
}
}
} }
} }
@@ -13140,14 +13038,14 @@ void Client::Handle_OP_ReadBook(const EQApplicationPacket *app)
LogError("Wrong size: OP_ReadBook, size=[{}], expected [{}]", app->size, sizeof(BookRequest_Struct)); LogError("Wrong size: OP_ReadBook, size=[{}], expected [{}]", app->size, sizeof(BookRequest_Struct));
return; return;
} }
BookRequest_Struct* book = (BookRequest_Struct*)app->pBuffer;
ReadBook(book); auto b = (BookRequest_Struct*) app->pBuffer;
if (ClientVersion() >= EQ::versions::ClientVersion::SoF) ReadBook(b);
{
EQApplicationPacket EndOfBook(OP_FinishWindow, 0); if (ClientVersion() >= EQ::versions::ClientVersion::SoF) {
QueuePacket(&EndOfBook); EQApplicationPacket end_of_book(OP_FinishWindow, 0);
QueuePacket(&end_of_book);
} }
return;
} }
void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app) void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app)
+3 -32
View File
@@ -121,7 +121,7 @@ bool Client::Process() {
} }
/* I haven't naturally updated my position in 10 seconds, updating manually */ /* I haven't naturally updated my position in 10 seconds, updating manually */
if (!IsMoving() && position_update_timer.Check()) { if (!IsMoving() && m_position_update_timer.Check()) {
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0); SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
} }
@@ -281,13 +281,7 @@ bool Client::Process() {
} }
} }
/** ScanCloseMobProcess();
* Scan close range mobs
* Used in aggro checks
*/
if (mob_close_scan_timer.Check()) {
entity_list.ScanCloseMobs(close_mobs, this, IsMoving());
}
if (RuleB(Inventory, LazyLoadBank)) { if (RuleB(Inventory, LazyLoadBank)) {
// poll once a second to see if we are close to a banker and we haven't loaded the bank yet // poll once a second to see if we are close to a banker and we haven't loaded the bank yet
@@ -608,30 +602,7 @@ bool Client::Process() {
} }
} }
//At this point, we are still connected, everything important has taken ClientToNpcAggroProcess();
//place, now check to see if anybody wants to aggro us.
// only if client is not feigned
if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) {
int npc_scan_count = 0;
for (auto & close_mob : close_mobs) {
Mob *mob = close_mob.second;
if (!mob) {
continue;
}
if (mob->IsClient()) {
continue;
}
if (mob->CheckWillAggro(this) && !mob->CheckAggro(this)) {
mob->AddToHateList(this, 25);
}
npc_scan_count++;
}
LogAggro("Checking Reverse Aggro (client->npc) scanned_npcs ([{}])", npc_scan_count);
}
if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED))) if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED)))
{ {
+2 -2
View File
@@ -542,8 +542,8 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
if (EQ::ValueWithin(m_open_type, 57, 58) && HasDestinationZone()) { if (EQ::ValueWithin(m_open_type, 57, 58) && HasDestinationZone()) {
bool has_key_required = (required_key_item && required_key_item == player_key); bool has_key_required = (required_key_item && required_key_item == player_key);
if (sender->GetGM() && has_key_required) { if (sender->GetGM() && !has_key_required) {
has_key_required = false; has_key_required = true;
sender->Message(Chat::White, "Your GM flag allows you to open this door without a key."); sender->Message(Chat::White, "Your GM flag allows you to open this door without a key.");
} }
+12 -6
View File
@@ -1036,7 +1036,7 @@ void EntityList::AETaunt(Client* taunter, float range, int bonus_hate)
float range_squared = range * range; float range_squared = range * range;
for (auto& it: entity_list.GetCloseMobList(taunter, range)) { for (auto& it: taunter->GetCloseMobList(range)) {
Mob *them = it.second; Mob *them = it.second;
if (!them) { if (!them) {
continue; continue;
@@ -1096,7 +1096,7 @@ void EntityList::AESpell(
max_targets = nullptr; max_targets = nullptr;
} }
int max_targets_allowed = RuleI(Range, AOEMaxTargets); // unlimited int max_targets_allowed = RuleI(Spells, DefaultAOEMaxTargets);;
if (max_targets) { // rains pass this in since they need to preserve the count through waves if (max_targets) { // rains pass this in since they need to preserve the count through waves
max_targets_allowed = *max_targets; max_targets_allowed = *max_targets;
} else if (spells[spell_id].aoe_max_targets) { } else if (spells[spell_id].aoe_max_targets) {
@@ -1108,7 +1108,13 @@ void EntityList::AESpell(
!IsEffectInSpell(spell_id, SE_Lull) && !IsEffectInSpell(spell_id, SE_Lull) &&
!IsEffectInSpell(spell_id, SE_Mez) !IsEffectInSpell(spell_id, SE_Mez)
) { ) {
max_targets_allowed = 4; max_targets_allowed = RuleI(Spells, TargetedAOEMaxTargets);
} else if (
IsPBAENukeSpell(spell_id) &&
IsDetrimentalSpell &&
!is_npc
) {
max_targets_allowed = RuleI(Spells, PointBlankAOEMaxTargets);
} }
int target_hit_counter = 0; int target_hit_counter = 0;
@@ -1120,7 +1126,7 @@ void EntityList::AESpell(
distance distance
); );
for (auto& it: entity_list.GetCloseMobList(caster_mob, distance)) { for (auto& it: caster_mob->GetCloseMobList(distance)) {
current_mob = it.second; current_mob = it.second;
if (!current_mob) { if (!current_mob) {
continue; continue;
@@ -1256,7 +1262,7 @@ void EntityList::MassGroupBuff(
float distance_squared = distance * distance; float distance_squared = distance * distance;
bool is_detrimental_spell = IsDetrimentalSpell(spell_id); bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
for (auto& it: entity_list.GetCloseMobList(caster, distance)) { for (auto& it: caster->GetCloseMobList(distance)) {
current_mob = it.second; current_mob = it.second;
if (!current_mob) { if (!current_mob) {
continue; continue;
@@ -1306,7 +1312,7 @@ void EntityList::AEAttack(
float distance_squared = distance * distance; float distance_squared = distance * distance;
int current_hits = 0; int current_hits = 0;
for (auto& it: entity_list.GetCloseMobList(attacker, distance)) { for (auto& it: attacker->GetCloseMobList(distance)) {
current_mob = it.second; current_mob = it.second;
if (!current_mob) { if (!current_mob) {
continue; continue;
+226 -56
View File
@@ -57,6 +57,7 @@ void perl_register_expedition();
void perl_register_expedition_lock_messages(); void perl_register_expedition_lock_messages();
void perl_register_bot(); void perl_register_bot();
void perl_register_buff(); void perl_register_buff();
void perl_register_merc();
#endif // EMBPERL_XS_CLASSES #endif // EMBPERL_XS_CLASSES
#endif // EMBPERL_XS #endif // EMBPERL_XS
@@ -203,6 +204,7 @@ const char* QuestEventSubroutines[_LargestEventID] = {
"EVENT_ENTITY_VARIABLE_UPDATE", "EVENT_ENTITY_VARIABLE_UPDATE",
"EVENT_AA_LOSS", "EVENT_AA_LOSS",
"EVENT_SPELL_BLOCKED", "EVENT_SPELL_BLOCKED",
"EVENT_READ_ITEM",
// Add new events before these or Lua crashes // Add new events before these or Lua crashes
"EVENT_SPELL_EFFECT_BOT", "EVENT_SPELL_EFFECT_BOT",
@@ -216,6 +218,8 @@ PerlembParser::PerlembParser() : perl(nullptr)
global_player_quest_status_ = questUnloaded; global_player_quest_status_ = questUnloaded;
bot_quest_status_ = questUnloaded; bot_quest_status_ = questUnloaded;
global_bot_quest_status_ = questUnloaded; global_bot_quest_status_ = questUnloaded;
merc_quest_status_ = questUnloaded;
global_merc_quest_status_ = questUnloaded;
} }
PerlembParser::~PerlembParser() PerlembParser::~PerlembParser()
@@ -257,6 +261,8 @@ void PerlembParser::ReloadQuests()
global_player_quest_status_ = questUnloaded; global_player_quest_status_ = questUnloaded;
bot_quest_status_ = questUnloaded; bot_quest_status_ = questUnloaded;
global_bot_quest_status_ = questUnloaded; global_bot_quest_status_ = questUnloaded;
merc_quest_status_ = questUnloaded;
global_merc_quest_status_ = questUnloaded;
item_quest_status_.clear(); item_quest_status_.clear();
spell_quest_status_.clear(); spell_quest_status_.clear();
@@ -284,6 +290,8 @@ int PerlembParser::EventCommon(
bool is_global_npc_quest = false; bool is_global_npc_quest = false;
bool is_bot_quest = false; bool is_bot_quest = false;
bool is_global_bot_quest = false; bool is_global_bot_quest = false;
bool is_merc_quest = false;
bool is_global_merc_quest = false;
bool is_item_quest = false; bool is_item_quest = false;
bool is_spell_quest = false; bool is_spell_quest = false;
@@ -294,6 +302,8 @@ int PerlembParser::EventCommon(
is_global_player_quest, is_global_player_quest,
is_bot_quest, is_bot_quest,
is_global_bot_quest, is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest, is_global_npc_quest,
is_item_quest, is_item_quest,
is_spell_quest, is_spell_quest,
@@ -309,6 +319,8 @@ int PerlembParser::EventCommon(
is_global_player_quest, is_global_player_quest,
is_bot_quest, is_bot_quest,
is_global_bot_quest, is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest, is_global_npc_quest,
is_item_quest, is_item_quest,
is_spell_quest, is_spell_quest,
@@ -338,6 +350,8 @@ int PerlembParser::EventCommon(
is_global_player_quest, is_global_player_quest,
is_bot_quest, is_bot_quest,
is_global_bot_quest, is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest, is_global_npc_quest,
is_item_quest, is_item_quest,
is_spell_quest, is_spell_quest,
@@ -355,6 +369,8 @@ int PerlembParser::EventCommon(
is_global_player_quest, is_global_player_quest,
is_bot_quest, is_bot_quest,
is_global_bot_quest, is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest, is_global_npc_quest,
is_item_quest, is_item_quest,
is_spell_quest, is_spell_quest,
@@ -381,7 +397,7 @@ int PerlembParser::EventCommon(
if (is_player_quest || is_global_player_quest) { if (is_player_quest || is_global_player_quest) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, nullptr, nullptr); return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, nullptr, nullptr);
} else if (is_bot_quest || is_global_bot_quest) { } else if (is_bot_quest || is_global_bot_quest || is_merc_quest || is_global_merc_quest) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, npc_mob, mob, nullptr, nullptr); return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, npc_mob, mob, nullptr, nullptr);
} else if (is_item_quest) { } else if (is_item_quest) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, inst, nullptr); return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, inst, nullptr);
@@ -1008,41 +1024,22 @@ int PerlembParser::SendCommands(
#ifdef EMBPERL_XS_CLASSES #ifdef EMBPERL_XS_CLASSES
dTHX; dTHX;
{ {
std::string cl = fmt::format("${}::client", prefix); const std::vector<std::string>& suffixes = {
std::string np = fmt::format("${}::npc", prefix); "bot",
std::string qi = fmt::format("${}::questitem", prefix); "client",
std::string sp = fmt::format("${}::spell", prefix); "entity_list",
std::string enl = fmt::format("${}::entity_list", prefix); "merc",
std::string bot = fmt::format("${}::bot", prefix); "npc",
"questitem",
"spell"
};
if (clear_vars_.find(cl) != clear_vars_.end()) { for (const auto& suffix : suffixes) {
auto e = fmt::format("{} = undef;", cl); const std::string& key = fmt::format("${}::{}", prefix, suffix);
if (clear_vars_.find(suffix) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", key);
perl->eval(e.c_str()); perl->eval(e.c_str());
} }
if (clear_vars_.find(np) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", np);
perl->eval(e.c_str());
}
if (clear_vars_.find(qi) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", qi);
perl->eval(e.c_str());
}
if (clear_vars_.find(sp) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", sp);
perl->eval(e.c_str());
}
if (clear_vars_.find(enl) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", enl);
perl->eval(e.c_str());
}
if (clear_vars_.find(bot) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", bot);
perl->eval(e.c_str());
} }
} }
@@ -1059,19 +1056,21 @@ int PerlembParser::SendCommands(
sv_setsv(client, _empty_sv); sv_setsv(client, _empty_sv);
} }
//only export NPC if it's a npc quest if (other->IsBot()) {
if (!other->IsClient() && other->IsNPC()) {
NPC* n = quest_manager.GetNPC();
buf = fmt::format("{}::npc", prefix);
SV* npc = get_sv(buf.c_str(), true);
sv_setref_pv(npc, "NPC", n);
}
if (!other->IsClient() && other->IsBot()) {
Bot* b = quest_manager.GetBot(); Bot* b = quest_manager.GetBot();
buf = fmt::format("{}::bot", prefix); buf = fmt::format("{}::bot", prefix);
SV* bot = get_sv(buf.c_str(), true); SV* bot = get_sv(buf.c_str(), true);
sv_setref_pv(bot, "Bot", b); sv_setref_pv(bot, "Bot", b);
} else if (other->IsMerc()) {
Merc* m = quest_manager.GetMerc();
buf = fmt::format("{}::merc", prefix);
SV* merc = get_sv(buf.c_str(), true);
sv_setref_pv(merc, "Merc", m);
} else if (other->IsNPC()) {
NPC* n = quest_manager.GetNPC();
buf = fmt::format("{}::npc", prefix);
SV* npc = get_sv(buf.c_str(), true);
sv_setref_pv(npc, "NPC", n);
} }
//only export QuestItem if it's an inst quest //only export QuestItem if it's an inst quest
@@ -1097,23 +1096,25 @@ int PerlembParser::SendCommands(
#endif #endif
//now call the requested sub //now call the requested sub
ret_value = perl->dosub(std::string(prefix).append("::").append(event_id).c_str()); const std::string& sub_key = fmt::format("{}::{}", prefix, event_id);
ret_value = perl->dosub(sub_key.c_str());
#ifdef EMBPERL_XS_CLASSES #ifdef EMBPERL_XS_CLASSES
{ {
std::string cl = fmt::format("${}::client", prefix); const std::vector<std::string>& suffixes = {
std::string np = fmt::format("${}::npc", prefix); "bot",
std::string qi = fmt::format("${}::questitem", prefix); "client",
std::string sp = fmt::format("${}::spell", prefix); "entity_list",
std::string enl = fmt::format("${}::entity_list", prefix); "merc",
std::string bot = fmt::format("${}::bot", prefix); "npc",
"questitem",
"spell"
};
clear_vars_[cl] = 1; for (const auto& suffix : suffixes) {
clear_vars_[np] = 1; const std::string& key = fmt::format("${}::{}", prefix, suffix);
clear_vars_[qi] = 1; clear_vars_[key] = 1;
clear_vars_[sp] = 1; }
clear_vars_[enl] = 1;
clear_vars_[bot] = 1;
} }
#endif #endif
@@ -1183,6 +1184,7 @@ void PerlembParser::MapFunctions()
perl_register_expedition_lock_messages(); perl_register_expedition_lock_messages();
perl_register_bot(); perl_register_bot();
perl_register_buff(); perl_register_buff();
perl_register_merc();
#endif // EMBPERL_XS_CLASSES #endif // EMBPERL_XS_CLASSES
} }
@@ -1191,6 +1193,8 @@ void PerlembParser::GetQuestTypes(
bool& is_global_player_quest, bool& is_global_player_quest,
bool& is_bot_quest, bool& is_bot_quest,
bool& is_global_bot_quest, bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest, bool& is_global_npc_quest,
bool& is_item_quest, bool& is_item_quest,
bool& is_spell_quest, bool& is_spell_quest,
@@ -1218,10 +1222,14 @@ void PerlembParser::GetQuestTypes(
if (is_global) { if (is_global) {
if (npc_mob->IsBot()) { if (npc_mob->IsBot()) {
is_global_bot_quest = true; is_global_bot_quest = true;
} else if (npc_mob->IsMerc()) {
is_global_merc_quest = true;
} }
} else { } else {
if (npc_mob->IsBot()) { if (npc_mob->IsBot()) {
is_bot_quest = true; is_bot_quest = true;
} else if (npc_mob->IsMerc()) {
is_merc_quest = true;
} }
} }
} else { } else {
@@ -1250,6 +1258,8 @@ void PerlembParser::GetQuestPackageName(
bool& is_global_player_quest, bool& is_global_player_quest,
bool& is_bot_quest, bool& is_bot_quest,
bool& is_global_bot_quest, bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest, bool& is_global_npc_quest,
bool& is_item_quest, bool& is_item_quest,
bool& is_spell_quest, bool& is_spell_quest,
@@ -1267,6 +1277,8 @@ void PerlembParser::GetQuestPackageName(
!is_global_player_quest && !is_global_player_quest &&
!is_bot_quest && !is_bot_quest &&
!is_global_bot_quest && !is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest && !is_item_quest &&
!is_spell_quest !is_spell_quest
) { ) {
@@ -1290,6 +1302,10 @@ void PerlembParser::GetQuestPackageName(
package_name = "qst_bot"; package_name = "qst_bot";
} else if (is_global_bot_quest) { } else if (is_global_bot_quest) {
package_name = "qst_global_bot"; package_name = "qst_global_bot";
} else if (is_merc_quest) {
package_name = "qst_merc";
} else if (is_global_merc_quest) {
package_name = "qst_global_merc";
} else { } else {
package_name = fmt::format("qst_spell_{}", object_id); package_name = fmt::format("qst_spell_{}", object_id);
} }
@@ -1315,6 +1331,8 @@ void PerlembParser::ExportQGlobals(
bool is_global_player_quest, bool is_global_player_quest,
bool is_bot_quest, bool is_bot_quest,
bool is_global_bot_quest, bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest, bool is_global_npc_quest,
bool is_item_quest, bool is_item_quest,
bool is_spell_quest, bool is_spell_quest,
@@ -1330,6 +1348,8 @@ void PerlembParser::ExportQGlobals(
!is_global_player_quest && !is_global_player_quest &&
!is_bot_quest && !is_bot_quest &&
!is_global_bot_quest && !is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest && !is_item_quest &&
!is_spell_quest !is_spell_quest
) { ) {
@@ -1465,6 +1485,8 @@ void PerlembParser::ExportMobVariables(
bool is_global_player_quest, bool is_global_player_quest,
bool is_bot_quest, bool is_bot_quest,
bool is_global_bot_quest, bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest, bool is_global_npc_quest,
bool is_item_quest, bool is_item_quest,
bool is_spell_quest, bool is_spell_quest,
@@ -1490,6 +1512,8 @@ void PerlembParser::ExportMobVariables(
!is_global_player_quest && !is_global_player_quest &&
!is_bot_quest && !is_bot_quest &&
!is_global_bot_quest && !is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest !is_item_quest
) { ) {
if (mob && mob->IsClient() && npc_mob && npc_mob->IsNPC()) { if (mob && mob->IsClient() && npc_mob && npc_mob->IsNPC()) {
@@ -1520,6 +1544,8 @@ void PerlembParser::ExportMobVariables(
!is_global_player_quest && !is_global_player_quest &&
!is_bot_quest && !is_bot_quest &&
!is_global_bot_quest && !is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest && !is_item_quest &&
!is_spell_quest !is_spell_quest
) { ) {
@@ -2080,7 +2106,8 @@ void PerlembParser::ExportEventVariables(
"killed_bot_id", "killed_bot_id",
killed->IsBot() ? killed->CastToBot()->GetBotID() : 0 killed->IsBot() ? killed->CastToBot()->GetBotID() : 0
); );
ExportVar(package_name.c_str(), "killed_npc_id", killed->IsNPC() ? killed->GetNPCTypeID() : 0); ExportVar(package_name.c_str(), "killed_merc_id", killed->IsMerc() ? killed->CastToMerc()->GetMercenaryID() : 0);
ExportVar(package_name.c_str(), "killed_npc_id", !killed->IsMerc() && killed->IsNPC() ? killed->GetNPCTypeID() : 0);
} }
} }
break; break;
@@ -2356,6 +2383,7 @@ void PerlembParser::ExportEventVariables(
case EVENT_DESPAWN: { case EVENT_DESPAWN: {
ExportVar(package_name.c_str(), "despawned_entity_id", npc_mob->GetID()); ExportVar(package_name.c_str(), "despawned_entity_id", npc_mob->GetID());
ExportVar(package_name.c_str(), "despawned_bot_id", npc_mob->IsBot() ? npc_mob->CastToBot()->GetBotID() : 0); ExportVar(package_name.c_str(), "despawned_bot_id", npc_mob->IsBot() ? npc_mob->CastToBot()->GetBotID() : 0);
ExportVar(package_name.c_str(), "despawned_merc_id", npc_mob->IsMerc() ? npc_mob->CastToMerc()->GetMercenaryID() : 0);
ExportVar(package_name.c_str(), "despawned_npc_id", npc_mob->IsNPC() ? npc_mob->GetNPCTypeID() : 0); ExportVar(package_name.c_str(), "despawned_npc_id", npc_mob->IsNPC() ? npc_mob->GetNPCTypeID() : 0);
break; break;
} }
@@ -2488,6 +2516,28 @@ void PerlembParser::ExportEventVariables(
break; break;
} }
case EVENT_READ_ITEM: {;
ExportVar(package_name.c_str(), "item_id", extra_data);
ExportVar(package_name.c_str(), "text_file", data);
if (extra_pointers && extra_pointers->size() == 7) {
ExportVar(package_name.c_str(), "book_text", std::any_cast<std::string>(extra_pointers->at(0)).c_str());
ExportVar(package_name.c_str(), "can_cast", std::any_cast<int8>(extra_pointers->at(1)));
ExportVar(package_name.c_str(), "can_scribe", std::any_cast<int8>(extra_pointers->at(2)));
ExportVar(package_name.c_str(), "slot_id", std::any_cast<int16>(extra_pointers->at(3)));
ExportVar(package_name.c_str(), "target_id", std::any_cast<int>(extra_pointers->at(4)));
ExportVar(package_name.c_str(), "type", std::any_cast<uint8>(extra_pointers->at(5)));
ExportVar(
package_name.c_str(),
"item",
"QuestItem",
std::any_cast<EQ::ItemInstance*>(extra_pointers->at(6))
);
}
break;
}
default: { default: {
break; break;
} }
@@ -2614,4 +2664,124 @@ int PerlembParser::EventGlobalBot(
); );
} }
void PerlembParser::LoadMercScript(std::string filename)
{
if (!perl || merc_quest_status_ != questUnloaded) {
return;
}
try {
perl->eval_file("qst_merc", filename.c_str());
} catch (std::string e) {
AddError(
fmt::format(
"Error Compiling Merc Quest File [{}] Error [{}]",
filename,
e
)
);
merc_quest_status_ = questFailedToLoad;
return;
}
merc_quest_status_ = questLoaded;
}
void PerlembParser::LoadGlobalMercScript(std::string filename)
{
if (!perl || global_merc_quest_status_ != questUnloaded) {
return;
}
try {
perl->eval_file("qst_global_merc", filename.c_str());
} catch (std::string e) {
AddError(
fmt::format(
"Error Compiling Global Merc Quest File [{}] Error [{}]",
filename,
e
)
);
global_merc_quest_status_ = questFailedToLoad;
return;
}
global_merc_quest_status_ = questLoaded;
}
bool PerlembParser::MercHasQuestSub(QuestEventID event_id)
{
if (
!perl ||
merc_quest_status_ != questLoaded ||
event_id >= _LargestEventID
) {
return false;
}
return perl->SubExists("qst_merc", QuestEventSubroutines[event_id]);
}
bool PerlembParser::GlobalMercHasQuestSub(QuestEventID event_id)
{
if (
!perl ||
global_merc_quest_status_ != questLoaded ||
event_id >= _LargestEventID
) {
return false;
}
return (perl->SubExists("qst_global_merc", QuestEventSubroutines[event_id]));
}
int PerlembParser::EventMerc(
QuestEventID event_id,
Merc* merc,
Mob* mob,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return EventCommon(
event_id,
0,
data.c_str(),
merc,
nullptr,
nullptr,
mob,
extra_data,
false,
extra_pointers
);
}
int PerlembParser::EventGlobalMerc(
QuestEventID event_id,
Merc* merc,
Mob* mob,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return EventCommon(
event_id,
0,
data.c_str(),
merc,
nullptr,
nullptr,
mob,
extra_data,
true,
extra_pointers
);
}
#endif #endif
+32
View File
@@ -118,6 +118,24 @@ public:
std::vector<std::any>* extra_pointers std::vector<std::any>* extra_pointers
); );
virtual int EventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual int EventGlobalMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id); virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id);
virtual bool HasGlobalQuestSub(QuestEventID event_id); virtual bool HasGlobalQuestSub(QuestEventID event_id);
virtual bool PlayerHasQuestSub(QuestEventID event_id); virtual bool PlayerHasQuestSub(QuestEventID event_id);
@@ -126,6 +144,8 @@ public:
virtual bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id); virtual bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id);
virtual bool BotHasQuestSub(QuestEventID event_id); virtual bool BotHasQuestSub(QuestEventID event_id);
virtual bool GlobalBotHasQuestSub(QuestEventID event_id); virtual bool GlobalBotHasQuestSub(QuestEventID event_id);
virtual bool MercHasQuestSub(QuestEventID event_id);
virtual bool GlobalMercHasQuestSub(QuestEventID event_id);
virtual void LoadNPCScript(std::string filename, int npc_id); virtual void LoadNPCScript(std::string filename, int npc_id);
virtual void LoadGlobalNPCScript(std::string filename); virtual void LoadGlobalNPCScript(std::string filename);
@@ -135,6 +155,8 @@ public:
virtual void LoadSpellScript(std::string filename, uint32 spell_id); virtual void LoadSpellScript(std::string filename, uint32 spell_id);
virtual void LoadBotScript(std::string filename); virtual void LoadBotScript(std::string filename);
virtual void LoadGlobalBotScript(std::string filename); virtual void LoadGlobalBotScript(std::string filename);
virtual void LoadMercScript(std::string filename);
virtual void LoadGlobalMercScript(std::string filename);
virtual void AddVar(std::string name, std::string val); virtual void AddVar(std::string name, std::string val);
virtual std::string GetVar(std::string name); virtual std::string GetVar(std::string name);
@@ -182,6 +204,8 @@ private:
bool& is_global_player_quest, bool& is_global_player_quest,
bool& is_bot_quest, bool& is_bot_quest,
bool& is_global_bot_quest, bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest, bool& is_global_npc_quest,
bool& is_item_quest, bool& is_item_quest,
bool& is_spell_quest, bool& is_spell_quest,
@@ -197,6 +221,8 @@ private:
bool& is_global_player_quest, bool& is_global_player_quest,
bool& is_bot_quest, bool& is_bot_quest,
bool& is_global_bot_quest, bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest, bool& is_global_npc_quest,
bool& is_item_quest, bool& is_item_quest,
bool& is_spell_quest, bool& is_spell_quest,
@@ -216,6 +242,8 @@ private:
bool is_global_player_quest, bool is_global_player_quest,
bool is_bot_quest, bool is_bot_quest,
bool is_global_bot_quest, bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest, bool is_global_npc_quest,
bool is_item_quest, bool is_item_quest,
bool is_spell_quest, bool is_spell_quest,
@@ -230,6 +258,8 @@ private:
bool is_global_player_quest, bool is_global_player_quest,
bool is_bot_quest, bool is_bot_quest,
bool is_global_bot_quest, bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest, bool is_global_npc_quest,
bool is_item_quest, bool is_item_quest,
bool is_spell_quest, bool is_spell_quest,
@@ -263,6 +293,8 @@ private:
PerlQuestStatus global_player_quest_status_; PerlQuestStatus global_player_quest_status_;
PerlQuestStatus bot_quest_status_; PerlQuestStatus bot_quest_status_;
PerlQuestStatus global_bot_quest_status_; PerlQuestStatus global_bot_quest_status_;
PerlQuestStatus merc_quest_status_;
PerlQuestStatus global_merc_quest_status_;
SV* _empty_sv; SV* _empty_sv;
+83 -52
View File
@@ -696,7 +696,7 @@ void EntityList::AddNPC(NPC *npc, bool send_spawn_packet, bool dont_queue)
npc_list.emplace(std::pair<uint16, NPC *>(npc->GetID(), npc)); npc_list.emplace(std::pair<uint16, NPC *>(npc->GetID(), npc));
mob_list.emplace(std::pair<uint16, Mob *>(npc->GetID(), npc)); mob_list.emplace(std::pair<uint16, Mob *>(npc->GetID(), npc));
entity_list.ScanCloseMobs(npc->close_mobs, npc, true); entity_list.ScanCloseMobs(npc);
if (parse->HasQuestSub(npc->GetNPCTypeID(), EVENT_SPAWN)) { if (parse->HasQuestSub(npc->GetNPCTypeID(), EVENT_SPAWN)) {
parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0); parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0);
@@ -776,6 +776,10 @@ void EntityList::AddMerc(Merc *merc, bool SendSpawnPacket, bool dontqueue)
merc_list.emplace(std::pair<uint16, Merc *>(merc->GetID(), merc)); merc_list.emplace(std::pair<uint16, Merc *>(merc->GetID(), merc));
mob_list.emplace(std::pair<uint16, Mob *>(merc->GetID(), merc)); mob_list.emplace(std::pair<uint16, Mob *>(merc->GetID(), merc));
if (parse->MercHasQuestSub(EVENT_SPAWN)) {
parse->EventMerc(EVENT_SPAWN, merc, nullptr, "", 0);
}
} }
} }
@@ -1742,8 +1746,7 @@ void EntityList::QueueCloseClients(
} }
float distance_squared = distance * distance; float distance_squared = distance * distance;
for (auto &e : sender->GetCloseMobList(distance)) {
for (auto &e : GetCloseMobList(sender, distance)) {
Mob *mob = e.second; Mob *mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -2886,7 +2889,7 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob)
entity_id entity_id
); );
it->second->close_mobs.erase(entity_id); it->second->m_close_mobs.erase(entity_id);
++it; ++it;
} }
@@ -2911,49 +2914,40 @@ void EntityList::RemoveAuraFromMobs(Mob *aura)
} }
} }
/** // The purpose of this system is so that we cache relevant entities that are "close"
* The purpose of this system is so that we cache relevant entities that are "close" //
* // In general; it becomes incredibly expensive to run zone-wide checks against every single mob in the zone when in reality
* In general; it becomes incredibly expensive to run zone-wide checks against every single mob in the zone when in reality // we only care about entities closest to us
* we only care about entities closest to us //
* // A very simple example of where this is relevant is Aggro, the below example is skewed because the overall implementation
* A very simple example of where this is relevant is Aggro, the below example is skewed because the overall implementation // of Aggro was also tweaked in conjunction with close lists. We also scan more aggressively when entities are moving (1-6 seconds)
* of Aggro was also tweaked in conjunction with close lists. We also scan more aggressively when entities are moving (1-6 seconds) // versus 60 seconds when idle. We also have entities that are moving add themselves to those closest to them so that their close
* versus 60 seconds when idle. We also have entities that are moving add themselves to those closest to them so that their close // lists remain always up to date
* lists remain always up to date //
* // Before: Aggro checks for NPC to Client aggro | (40 clients in zone) x (525 npcs) x 2 (times a second) = 2,520,000 checks a minute
* Before: Aggro checks for NPC to Client aggro | (40 clients in zone) x (525 npcs) x 2 (times a second) = 2,520,000 checks a minute // After: Aggro checks for NPC to Client aggro | (40 clients in zone) x (20-30 npcs) x 2 (times a second) = 144,000 checks a minute (This is // tually far less today)
* After: Aggro checks for NPC to Client aggro | (40 clients in zone) x (20-30 npcs) x 2 (times a second) = 144,000 checks a minute (This is actually far less today) //
* // Places in the code where this logic makes a huge impact
* Places in the code where this logic makes a huge impact //
* // Aggro checks (zone wide -> close)
* Aggro checks (zone wide -> close) // Aura processing (zone wide -> close)
* Aura processing (zone wide -> close) // AE Taunt (zone wide -> close)
* AE Taunt (zone wide -> close) // AOE Spells (zone wide -> close)
* AOE Spells (zone wide -> close) // Bard Pulse AOE (zone wide -> close)
* Bard Pulse AOE (zone wide -> close) // Mass Group Buff (zone wide -> close)
* Mass Group Buff (zone wide -> close) // AE Attack (zone wide -> close)
* AE Attack (zone wide -> close) // Packet QueueCloseClients (zone wide -> close)
* Packet QueueCloseClients (zone wide -> close) // Check Close Beneficial Spells (Buffs; should I heal other npcs) (zone wide -> close)
* Check Close Beneficial Spells (Buffs; should I heal other npcs) (zone wide -> close) // AI Yell for Help (NPC Assist other NPCs) (zone wide -> close)
* AI Yell for Help (NPC Assist other NPCs) (zone wide -> close) //
* // All of the above makes a tremendous impact on the bottom line of cpu cycle performance because we run an order of magnitude
* All of the above makes a tremendous impact on the bottom line of cpu cycle performance because we run an order of magnitude // less checks by focusing our hot path logic down to a very small subset of relevant entities instead of looping an entire
* less checks by focusing our hot path logic down to a very small subset of relevant entities instead of looping an entire // entity list (zone wide)
* entity list (zone wide) void EntityList::ScanCloseMobs(Mob *scanning_mob)
*
* @param close_mobs
* @param scanning_mob
*/
void EntityList::ScanCloseMobs(
std::unordered_map<uint16, Mob *> &close_mobs,
Mob *scanning_mob,
bool add_self_to_other_lists
)
{ {
float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance);
close_mobs.clear(); scanning_mob->m_close_mobs.clear();
for (auto &e : mob_list) { for (auto &e : mob_list) {
auto mob = e.second; auto mob = e.second;
@@ -2963,12 +2957,13 @@ void EntityList::ScanCloseMobs(
float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition());
if (distance <= scan_range || mob->GetAggroRange() >= scan_range) { if (distance <= scan_range || mob->GetAggroRange() >= scan_range) {
close_mobs.emplace(std::pair<uint16, Mob *>(mob->GetID(), mob)); scanning_mob->m_close_mobs.emplace(std::pair<uint16, Mob *>(mob->GetID(), mob));
if (add_self_to_other_lists && scanning_mob->GetID() > 0) { // add self to other mobs close list
if (scanning_mob->GetID() > 0) {
bool has_mob = false; bool has_mob = false;
for (auto &cm: mob->close_mobs) { for (auto &cm: mob->m_close_mobs) {
if (scanning_mob->GetID() == cm.first) { if (scanning_mob->GetID() == cm.first) {
has_mob = true; has_mob = true;
break; break;
@@ -2976,7 +2971,7 @@ void EntityList::ScanCloseMobs(
} }
if (!has_mob) { if (!has_mob) {
mob->close_mobs.insert(std::pair<uint16, Mob *>(scanning_mob->GetID(), scanning_mob)); mob->m_close_mobs.insert(std::pair<uint16, Mob *>(scanning_mob->GetID(), scanning_mob));
} }
} }
} }
@@ -2985,7 +2980,7 @@ void EntityList::ScanCloseMobs(
LogAIScanCloseDetail( LogAIScanCloseDetail(
"[{}] Scanning Close List | list_size [{}] moving [{}]", "[{}] Scanning Close List | list_size [{}] moving [{}]",
scanning_mob->GetCleanName(), scanning_mob->GetCleanName(),
close_mobs.size(), scanning_mob->m_close_mobs.size(),
scanning_mob->IsMoving() ? "true" : "false" scanning_mob->IsMoving() ? "true" : "false"
); );
} }
@@ -4448,7 +4443,7 @@ void EntityList::QuestJournalledSayClose(
buf.WriteInt32(0); buf.WriteInt32(0);
if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) { if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) {
for (auto &e : GetCloseMobList(sender, (dist * dist))) { for (auto &e : sender->GetCloseMobList(dist)) {
Mob *mob = e.second; Mob *mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -5651,7 +5646,7 @@ std::vector<Mob*> EntityList::GetTargetsForVirusEffect(Mob *spreader, Mob *origi
std::vector<Mob *> spreader_list = {}; std::vector<Mob *> spreader_list = {};
bool is_detrimental_spell = IsDetrimentalSpell(spell_id); bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
for (auto &it : entity_list.GetCloseMobList(spreader, range)) { for (auto &it : spreader->GetCloseMobList(range)) {
Mob *mob = it.second; Mob *mob = it.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -5781,7 +5776,7 @@ void EntityList::ReloadMerchants() {
std::unordered_map<uint16, Mob *> &EntityList::GetCloseMobList(Mob *mob, float distance) std::unordered_map<uint16, Mob *> &EntityList::GetCloseMobList(Mob *mob, float distance)
{ {
if (distance <= RuleI(Range, MobCloseScanDistance)) { if (distance <= RuleI(Range, MobCloseScanDistance)) {
return mob->close_mobs; return mob->m_close_mobs;
} }
return mob_list; return mob_list;
@@ -5946,3 +5941,39 @@ void EntityList::DamageArea(
} }
} }
} }
std::vector<NPC*> EntityList::GetNPCsByIDs(std::vector<uint32> npc_ids)
{
std::vector<NPC*> v;
for (const auto& e : GetNPCList()) {
const auto& n = std::find(npc_ids.begin(), npc_ids.end(), e.second->GetNPCTypeID());
if (e.second) {
if (n != npc_ids.end()) {
continue;
}
v.emplace_back(e.second);
}
}
return v;
}
std::vector<NPC*> EntityList::GetExcludedNPCsByIDs(std::vector<uint32> npc_ids)
{
std::vector<NPC*> v;
for (const auto& e : GetNPCList()) {
const auto& n = std::find(npc_ids.begin(), npc_ids.end(), e.second->GetNPCTypeID());
if (e.second) {
if (n == npc_ids.end()) {
continue;
}
v.emplace_back(e.second);
}
}
return v;
}
+4 -5
View File
@@ -560,17 +560,16 @@ public:
std::unordered_map<uint16, Mob *> &GetCloseMobList(Mob *mob, float distance = 0.0f); std::unordered_map<uint16, Mob *> &GetCloseMobList(Mob *mob, float distance = 0.0f);
std::vector<NPC*> GetNPCsByIDs(std::vector<uint32> npc_ids);
std::vector<NPC*> GetExcludedNPCsByIDs(std::vector<uint32> npc_ids);
void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); void DepopAll(int NPCTypeID, bool StartSpawnTimer = true);
uint16 GetFreeID(); uint16 GetFreeID();
void RefreshAutoXTargets(Client *c); void RefreshAutoXTargets(Client *c);
void RefreshClientXTargets(Client *c); void RefreshClientXTargets(Client *c);
void SendAlternateAdvancementStats(); void SendAlternateAdvancementStats();
void ScanCloseMobs( void ScanCloseMobs(Mob *scanning_mob);
std::unordered_map<uint16, Mob *> &close_mobs,
Mob *scanning_mob,
bool add_self_to_other_lists = false
);
void GetTrapInfo(Client* c); void GetTrapInfo(Client* c);
bool IsTrapGroupSpawned(uint32 trap_id, uint8 group); bool IsTrapGroupSpawned(uint32 trap_id, uint8 group);
+1
View File
@@ -144,6 +144,7 @@ typedef enum {
EVENT_ENTITY_VARIABLE_UPDATE, EVENT_ENTITY_VARIABLE_UPDATE,
EVENT_AA_LOSS, EVENT_AA_LOSS,
EVENT_SPELL_BLOCKED, EVENT_SPELL_BLOCKED,
EVENT_READ_ITEM,
// Add new events before these or Lua crashes // Add new events before these or Lua crashes
EVENT_SPELL_EFFECT_BOT, EVENT_SPELL_EFFECT_BOT,
+5 -3
View File
@@ -8,14 +8,15 @@ void command_grantaa(Client *c, const Seperator *sep)
} }
const uint8 unlock_level = sep->IsNumber(1) ? static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[1])) : 0; const uint8 unlock_level = sep->IsNumber(1) ? static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[1])) : 0;
const bool skip_grant_only = sep->IsNumber(2) ? Strings::ToBool(sep->arg[2]) : false;
auto t = c->GetTarget()->CastToClient(); auto t = c->GetTarget()->CastToClient();
t->GrantAllAAPoints(unlock_level); t->GrantAllAAPoints(unlock_level, skip_grant_only);
c->Message( c->Message(
Chat::White, Chat::White,
fmt::format( fmt::format(
"Successfully granted all Alternate Advancements for {}{}.", "Successfully granted all Alternate Advancements for {}{}{}.",
c->GetTargetDescription(t), c->GetTargetDescription(t),
( (
unlock_level ? unlock_level ?
@@ -24,7 +25,8 @@ void command_grantaa(Client *c, const Seperator *sep)
unlock_level unlock_level
) : ) :
"" ""
) ),
skip_grant_only ? "except for grant only AAs" : ""
).c_str() ).c_str()
); );
} }
+1 -1
View File
@@ -17,7 +17,7 @@ void SetMOTD(Client *c, const Seperator *sep)
auto m = (ServerMotd_Struct *) pack->pBuffer; auto m = (ServerMotd_Struct *) pack->pBuffer;
strn0cpy(m->myname, c->GetName(), sizeof(m->myname)); strn0cpy(m->myname, c->GetName(), sizeof(m->myname));
strn0cpy(m->motd, message.c_str(), sizeof(m->motd)); strn0cpy(m->motd, !message.empty() ? message.c_str() : "", sizeof(m->motd));
worldserver.SendPacket(pack); worldserver.SendPacket(pack);
safe_delete(pack); safe_delete(pack);
+9 -4
View File
@@ -2117,14 +2117,19 @@ void Group::UnDelegateMarkNPC(const char *OldNPCMarkerName)
void Group::SaveGroupLeaderAA() void Group::SaveGroupLeaderAA()
{ {
const uint32 group_id = GetID();
if (!group_id) {
return;
}
// Stores the Group Leaders Leadership AA data from the Player Profile as a blob in the group_leaders table. // Stores the Group Leaders Leadership AA data from the Player Profile as a blob in the group_leaders table.
// This is done so that group members not in the same zone as the Leader still have access to this information. // This is done so that group members not in the same zone as the Leader still have access to this information.
std::string aa((char *) &LeaderAbilities, sizeof(GroupLeadershipAA_Struct)); std::string aa((char*) &LeaderAbilities, sizeof(GroupLeadershipAA_Struct));
auto results = GroupLeadersRepository::UpdateLeadershipAA(database, aa, GetID());
if (!results) { if (!GroupLeadersRepository::UpdateLeadershipAA(database, aa, group_id)) {
LogError("Unable to store GroupLeadershipAA for group_id: [{}]", GetID()); LogError("Unable to store GroupLeadershipAA for group_id: [{}]", group_id);
} }
} }
+7
View File
@@ -3216,6 +3216,12 @@ void Lua_Client::GrantAllAAPoints(uint8 unlock_level)
self->GrantAllAAPoints(unlock_level); self->GrantAllAAPoints(unlock_level);
} }
void Lua_Client::GrantAllAAPoints(uint8 unlock_level, bool skip_grant_only)
{
Lua_Safe_Call_Void();
self->GrantAllAAPoints(unlock_level, skip_grant_only);
}
void Lua_Client::AddEbonCrystals(uint32 amount) void Lua_Client::AddEbonCrystals(uint32 amount)
{ {
Lua_Safe_Call_Void(); Lua_Safe_Call_Void();
@@ -3699,6 +3705,7 @@ luabind::scope lua_register_client() {
.def("GoFish", (void(Lua_Client::*)(void))&Lua_Client::GoFish) .def("GoFish", (void(Lua_Client::*)(void))&Lua_Client::GoFish)
.def("GrantAllAAPoints", (void(Lua_Client::*)(void))&Lua_Client::GrantAllAAPoints) .def("GrantAllAAPoints", (void(Lua_Client::*)(void))&Lua_Client::GrantAllAAPoints)
.def("GrantAllAAPoints", (void(Lua_Client::*)(uint8))&Lua_Client::GrantAllAAPoints) .def("GrantAllAAPoints", (void(Lua_Client::*)(uint8))&Lua_Client::GrantAllAAPoints)
.def("GrantAllAAPoints", (void(Lua_Client::*)(uint8,bool))&Lua_Client::GrantAllAAPoints)
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int))&Lua_Client::GrantAlternateAdvancementAbility) .def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int))&Lua_Client::GrantAlternateAdvancementAbility)
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int, bool))&Lua_Client::GrantAlternateAdvancementAbility) .def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int, bool))&Lua_Client::GrantAlternateAdvancementAbility)
.def("GuildID", (uint32(Lua_Client::*)(void))&Lua_Client::GuildID) .def("GuildID", (uint32(Lua_Client::*)(void))&Lua_Client::GuildID)
+1
View File
@@ -487,6 +487,7 @@ public:
void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration); void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration);
void GrantAllAAPoints(); void GrantAllAAPoints();
void GrantAllAAPoints(uint8 unlock_level); void GrantAllAAPoints(uint8 unlock_level);
void GrantAllAAPoints(uint8 unlock_level, bool skip_grant_only);
void AddEbonCrystals(uint32 amount); void AddEbonCrystals(uint32 amount);
void AddRadiantCrystals(uint32 amount); void AddRadiantCrystals(uint32 amount);
void RemoveEbonCrystals(uint32 amount); void RemoveEbonCrystals(uint32 amount);
+8
View File
@@ -12,6 +12,7 @@
#include "lua_door.h" #include "lua_door.h"
#include "lua_bot.h" #include "lua_bot.h"
#include "lua_merc.h"
bool Lua_Entity::IsClient() { bool Lua_Entity::IsClient() {
Lua_Safe_Call_Bool(); Lua_Safe_Call_Bool();
@@ -140,6 +141,12 @@ Lua_Bot Lua_Entity::CastToBot() {
return Lua_Bot(b); return Lua_Bot(b);
} }
Lua_Merc Lua_Entity::CastToMerc() {
void *d = GetLuaPtrData();
Merc *m = reinterpret_cast<Merc*>(d);
return Lua_Merc(m);
}
luabind::scope lua_register_entity() { luabind::scope lua_register_entity() {
return luabind::class_<Lua_Entity>("Entity") return luabind::class_<Lua_Entity>("Entity")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
@@ -149,6 +156,7 @@ luabind::scope lua_register_entity() {
.def("CastToClient", &Lua_Entity::CastToClient) .def("CastToClient", &Lua_Entity::CastToClient)
.def("CastToCorpse", &Lua_Entity::CastToCorpse) .def("CastToCorpse", &Lua_Entity::CastToCorpse)
.def("CastToDoor", &Lua_Entity::CastToDoor) .def("CastToDoor", &Lua_Entity::CastToDoor)
.def("CastToMerc", &Lua_Entity::CastToMerc)
.def("CastToMob", &Lua_Entity::CastToMob) .def("CastToMob", &Lua_Entity::CastToMob)
.def("CastToNPC", &Lua_Entity::CastToNPC) .def("CastToNPC", &Lua_Entity::CastToNPC)
.def("CastToObject", &Lua_Entity::CastToObject) .def("CastToObject", &Lua_Entity::CastToObject)
+2
View File
@@ -7,6 +7,7 @@
class Entity; class Entity;
class Lua_Client; class Lua_Client;
class Lua_Bot; class Lua_Bot;
class Lua_Merc;
class Lua_NPC; class Lua_NPC;
class Lua_Mob; class Lua_Mob;
struct Lua_HateList; struct Lua_HateList;
@@ -59,6 +60,7 @@ public:
Lua_Object CastToObject(); Lua_Object CastToObject();
Lua_Door CastToDoor(); Lua_Door CastToDoor();
Lua_Bot CastToBot(); Lua_Bot CastToBot();
Lua_Merc CastToMerc();
}; };
#endif #endif
+63
View File
@@ -16,6 +16,7 @@
#include "lua_spawn.h" #include "lua_spawn.h"
#include "lua_bot.h" #include "lua_bot.h"
#include "lua_merc.h"
struct Lua_Mob_List { struct Lua_Mob_List {
std::vector<Lua_Mob> entries; std::vector<Lua_Mob> entries;
@@ -764,6 +765,66 @@ void Lua_EntityList::MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_
self->MassGroupBuff(caster, center, spell_id, affect_caster); self->MassGroupBuff(caster, center, spell_id, affect_caster);
} }
Lua_NPC_List Lua_EntityList::GetNPCsByExcludedIDs(luabind::adl::object table)
{
Lua_Safe_Call_Class(Lua_NPC_List);
Lua_NPC_List ret;
if (luabind::type(table) != LUA_TTABLE) {
return ret;
}
if (d_) {
auto self = reinterpret_cast<NativeType*>(d_);
std::vector<uint32> ids;
int index = 1;
while (luabind::type(table[index]) != LUA_TNIL) {
ids.emplace_back(luabind::object_cast<uint32>(table[index]));
index++;
}
const auto& l = self->GetExcludedNPCsByIDs(ids);
for (const auto& e : l) {
ret.entries.emplace_back(Lua_NPC(e));
}
}
return ret;
}
Lua_NPC_List Lua_EntityList::GetNPCsByIDs(luabind::adl::object table)
{
Lua_Safe_Call_Class(Lua_NPC_List);
Lua_NPC_List ret;
if (luabind::type(table) != LUA_TTABLE) {
return ret;
}
if (d_) {
auto self = reinterpret_cast<NativeType*>(d_);
std::vector<uint32> ids;
int index = 1;
while (luabind::type(table[index]) != LUA_TNIL) {
ids.emplace_back(luabind::object_cast<uint32>(table[index]));
index++;
}
const auto& l = self->GetNPCsByIDs(ids);
for (const auto& e : l) {
ret.entries.emplace_back(Lua_NPC(e));
}
}
return ret;
}
luabind::scope lua_register_entity_list() { luabind::scope lua_register_entity_list() {
return luabind::class_<Lua_EntityList>("EntityList") return luabind::class_<Lua_EntityList>("EntityList")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
@@ -829,6 +890,8 @@ luabind::scope lua_register_entity_list() {
.def("GetNPCByNPCTypeID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByNPCTypeID) .def("GetNPCByNPCTypeID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByNPCTypeID)
.def("GetNPCBySpawnID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCBySpawnID) .def("GetNPCBySpawnID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCBySpawnID)
.def("GetNPCList", (Lua_NPC_List(Lua_EntityList::*)(void))&Lua_EntityList::GetNPCList) .def("GetNPCList", (Lua_NPC_List(Lua_EntityList::*)(void))&Lua_EntityList::GetNPCList)
.def("GetNPCsByExcludedIDs", (Lua_NPC_List(Lua_EntityList::*)(luabind::adl::object))&Lua_EntityList::GetNPCsByExcludedIDs)
.def("GetNPCsByIDs", (Lua_NPC_List(Lua_EntityList::*)(luabind::adl::object))&Lua_EntityList::GetNPCsByIDs)
.def("GetObjectByDBID", (Lua_Object(Lua_EntityList::*)(uint32))&Lua_EntityList::GetObjectByDBID) .def("GetObjectByDBID", (Lua_Object(Lua_EntityList::*)(uint32))&Lua_EntityList::GetObjectByDBID)
.def("GetObjectByID", (Lua_Object(Lua_EntityList::*)(int))&Lua_EntityList::GetObjectByID) .def("GetObjectByID", (Lua_Object(Lua_EntityList::*)(int))&Lua_EntityList::GetObjectByID)
.def("GetObjectList", (Lua_Object_List(Lua_EntityList::*)(void))&Lua_EntityList::GetObjectList) .def("GetObjectList", (Lua_Object_List(Lua_EntityList::*)(void))&Lua_EntityList::GetObjectList)
+3 -1
View File
@@ -8,6 +8,7 @@ class EntityList;
class Lua_Mob; class Lua_Mob;
class Lua_Client; class Lua_Client;
class Lua_Bot; class Lua_Bot;
class Lua_Merc;
class Lua_NPC; class Lua_NPC;
class Lua_Door; class Lua_Door;
class Lua_Corpse; class Lua_Corpse;
@@ -156,7 +157,8 @@ public:
void AreaTaunt(Lua_Client caster, float range, int bonus_hate); void AreaTaunt(Lua_Client caster, float range, int bonus_hate);
void MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_id); void MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_id);
void MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster); void MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster);
Lua_NPC_List GetNPCsByIDs(luabind::adl::object npc_ids);
Lua_NPC_List GetNPCsByExcludedIDs(luabind::adl::object npc_ids);
}; };
#endif #endif
+2 -1
View File
@@ -6907,7 +6907,8 @@ luabind::scope lua_register_events() {
luabind::value("entity_variable_delete", static_cast<int>(EVENT_ENTITY_VARIABLE_DELETE)), luabind::value("entity_variable_delete", static_cast<int>(EVENT_ENTITY_VARIABLE_DELETE)),
luabind::value("entity_variable_set", static_cast<int>(EVENT_ENTITY_VARIABLE_SET)), luabind::value("entity_variable_set", static_cast<int>(EVENT_ENTITY_VARIABLE_SET)),
luabind::value("entity_variable_update", static_cast<int>(EVENT_ENTITY_VARIABLE_UPDATE)), luabind::value("entity_variable_update", static_cast<int>(EVENT_ENTITY_VARIABLE_UPDATE)),
luabind::value("aa_loss", static_cast<int>(EVENT_AA_LOSS)) luabind::value("aa_loss", static_cast<int>(EVENT_AA_LOSS)),
luabind::value("read", static_cast<int>(EVENT_READ_ITEM))
)]; )];
} }
+229
View File
@@ -0,0 +1,229 @@
#ifdef LUA_EQEMU
#include "lua.hpp"
#include <luabind/luabind.hpp>
#include "merc.h"
#include "lua_client.h"
#include "lua_merc.h"
#include "lua_group.h"
#include "lua_item.h"
#include "lua_iteminst.h"
#include "lua_mob.h"
uint32 Lua_Merc::GetCostFormula()
{
Lua_Safe_Call_Int();
return self->GetCostFormula();
}
Lua_Group Lua_Merc::GetGroup()
{
Lua_Safe_Call_Class(Lua_Group);
return self->GetGroup();
}
int Lua_Merc::GetHatedCount()
{
Lua_Safe_Call_Int();
return self->GetHatedCount();
}
float Lua_Merc::GetMaxMeleeRangeToTarget(Lua_Mob target)
{
Lua_Safe_Call_Real();
return self->GetMaxMeleeRangeToTarget(target);
}
uint32 Lua_Merc::GetMercenaryCharacterID()
{
Lua_Safe_Call_Int();
return self->GetMercenaryCharacterID();
}
uint32 Lua_Merc::GetMercenaryID()
{
Lua_Safe_Call_Int();
return self->GetMercenaryID();
}
uint32 Lua_Merc::GetMercenaryNameType()
{
Lua_Safe_Call_Int();
return self->GetMercNameType();
}
Lua_Client Lua_Merc::GetMercenaryOwner()
{
Lua_Safe_Call_Class(Lua_Client);
return Lua_Client(self->GetMercenaryOwner());
}
uint32 Lua_Merc::GetMercenarySubtype()
{
Lua_Safe_Call_Int();
return self->GetMercenarySubType();
}
uint32 Lua_Merc::GetMercenaryTemplateID()
{
Lua_Safe_Call_Int();
return self->GetMercenaryTemplateID();
}
uint32 Lua_Merc::GetMercenaryType()
{
Lua_Safe_Call_Int();
return self->GetMercenaryType();
}
Lua_Mob Lua_Merc::GetOwner()
{
Lua_Safe_Call_Class(Lua_Mob);
return Lua_Mob(self->GetOwner());
}
Lua_Mob Lua_Merc::GetOwnerOrSelf()
{
Lua_Safe_Call_Class(Lua_Mob);
return Lua_Mob(self->GetOwnerOrSelf());
}
uint8 Lua_Merc::GetProficiencyID()
{
Lua_Safe_Call_Int();
return self->GetProficiencyID();
}
uint8 Lua_Merc::GetStance()
{
Lua_Safe_Call_Int();
return self->GetStance();
}
uint8 Lua_Merc::GetTierID()
{
Lua_Safe_Call_Int();
return self->GetTierID();
}
bool Lua_Merc::HasOrMayGetAggro()
{
Lua_Safe_Call_Bool();
return self->HasOrMayGetAggro();
}
bool Lua_Merc::IsMercenaryCaster()
{
Lua_Safe_Call_Bool();
return self->IsMercCaster();
}
bool Lua_Merc::IsMercenaryCasterCombatRange(Lua_Mob target)
{
Lua_Safe_Call_Bool();
return self->IsMercCasterCombatRange(target);
}
bool Lua_Merc::IsSitting()
{
Lua_Safe_Call_Bool();
return self->IsSitting();
}
bool Lua_Merc::IsStanding()
{
Lua_Safe_Call_Bool();
return self->IsStanding();
}
void Lua_Merc::ScaleStats(int scale_percentage)
{
Lua_Safe_Call_Void();
self->ScaleStats(scale_percentage);
}
void Lua_Merc::ScaleStats(int scale_percentage, bool set_to_max)
{
Lua_Safe_Call_Void();
self->ScaleStats(scale_percentage, set_to_max);
}
void Lua_Merc::SendPayload(int payload_id, std::string payload_value)
{
Lua_Safe_Call_Void();
self->SendPayload(payload_id, payload_value);
}
void Lua_Merc::SetTarget(Lua_Mob target)
{
Lua_Safe_Call_Void();
self->SetTarget(target);
}
void Lua_Merc::Signal(int signal_id)
{
Lua_Safe_Call_Void();
self->Signal(signal_id);
}
void Lua_Merc::Sit()
{
Lua_Safe_Call_Void();
self->Sit();
}
void Lua_Merc::Stand()
{
Lua_Safe_Call_Void();
self->Stand();
}
bool Lua_Merc::Suspend()
{
Lua_Safe_Call_Bool();
return self->Suspend();
}
bool Lua_Merc::UseDiscipline(uint16 spell_id, uint16 target_id)
{
Lua_Safe_Call_Bool();
return self->UseDiscipline(spell_id, target_id);
}
luabind::scope lua_register_merc() {
return luabind::class_<Lua_Merc, Lua_Mob>("Merc")
.def(luabind::constructor<>())
.def("GetCostFormula", &Lua_Merc::GetCostFormula)
.def("GetGroup", &Lua_Merc::GetGroup)
.def("GetHatedCount", &Lua_Merc::GetHatedCount)
.def("GetMaxMeleeRangeToTarget", &Lua_Merc::GetMaxMeleeRangeToTarget)
.def("GetMercenaryCharacterID", &Lua_Merc::GetMercenaryCharacterID)
.def("GetMercenaryID", &Lua_Merc::GetMercenaryID)
.def("GetMercenaryNameType", &Lua_Merc::GetMercenaryNameType)
.def("GetMercenaryOwner", &Lua_Merc::GetMercenaryOwner)
.def("GetMercenarySubtype", &Lua_Merc::GetMercenarySubtype)
.def("GetMercenaryTemplateID", &Lua_Merc::GetMercenaryTemplateID)
.def("GetMercenaryType", &Lua_Merc::GetMercenaryType)
.def("GetOwner", &Lua_Merc::GetOwner)
.def("GetOwnerOrSelf", &Lua_Merc::GetOwnerOrSelf)
.def("GetProficiencyID", &Lua_Merc::GetProficiencyID)
.def("GetStance", &Lua_Merc::GetStance)
.def("GetTierID", &Lua_Merc::GetTierID)
.def("HasOrMayGetAggro", &Lua_Merc::HasOrMayGetAggro)
.def("IsMercenaryCaster", &Lua_Merc::IsMercenaryCaster)
.def("IsMercenaryCasterCombatRange", &Lua_Merc::IsMercenaryCasterCombatRange)
.def("IsSitting", &Lua_Merc::IsSitting)
.def("IsStanding", &Lua_Merc::IsStanding)
.def("ScaleStats", (void(Lua_Merc::*)(int))&Lua_Merc::ScaleStats)
.def("ScaleStats", (void(Lua_Merc::*)(int,bool))&Lua_Merc::ScaleStats)
.def("SendPayload", &Lua_Merc::SendPayload)
.def("SetTarget", &Lua_Merc::SetTarget)
.def("Signal", &Lua_Merc::Signal)
.def("Sit", &Lua_Merc::Sit)
.def("Stand", &Lua_Merc::Stand)
.def("Suspend", &Lua_Merc::Suspend)
.def("UseDiscipline", &Lua_Merc::UseDiscipline);
}
#endif
+63
View File
@@ -0,0 +1,63 @@
#ifndef EQEMU_LUA_MERC_H
#define EQEMU_LUA_MERC_H
#ifdef LUA_EQEMU
#include "lua_mob.h"
class Merc;
class Lua_Group;
class Lua_Merc;
class Lua_Mob;
namespace luabind {
struct scope;
}
luabind::scope lua_register_merc();
class Lua_Merc : public Lua_Mob
{
typedef Merc NativeType;
public:
Lua_Merc() { SetLuaPtrData(nullptr); }
Lua_Merc(Merc *d) { SetLuaPtrData(reinterpret_cast<Entity*>(d)); }
virtual ~Lua_Merc() { }
operator Merc*() {
return reinterpret_cast<Merc*>(GetLuaPtrData());
}
uint32 GetCostFormula();
Lua_Group GetGroup();
int GetHatedCount();
float GetMaxMeleeRangeToTarget(Lua_Mob target);
uint32 GetMercenaryCharacterID();
uint32 GetMercenaryID();
uint32 GetMercenaryNameType();
Lua_Client GetMercenaryOwner();
uint32 GetMercenarySubtype();
uint32 GetMercenaryTemplateID();
uint32 GetMercenaryType();
Lua_Mob GetOwner();
Lua_Mob GetOwnerOrSelf();
uint8 GetProficiencyID();
uint8 GetStance();
uint8 GetTierID();
bool HasOrMayGetAggro();
bool IsMercenaryCaster();
bool IsMercenaryCasterCombatRange(Lua_Mob target);
bool IsSitting();
bool IsStanding();
void ScaleStats(int scale_percentage);
void ScaleStats(int scale_percentage, bool set_to_max);
void SendPayload(int payload_id, std::string payload_value);
void SetTarget(Lua_Mob target);
void Signal(int signal_id);
void Sit();
void Stand();
bool Suspend();
bool UseDiscipline(uint16 spell_id, uint16 target_id);
};
#endif // LUA_EQEMU
#endif // EQEMU_LUA_MERC_H
+35
View File
@@ -3446,6 +3446,36 @@ void Lua_Mob::MassGroupBuff(Lua_Mob center, uint16 spell_id, bool affect_caster)
entity_list.MassGroupBuff(self, center, spell_id, affect_caster); entity_list.MassGroupBuff(self, center, spell_id, affect_caster);
} }
void Lua_Mob::BuffFadeBeneficial()
{
Lua_Safe_Call_Void();
self->BuffFadeBeneficial();
}
void Lua_Mob::BuffFadeDetrimental()
{
Lua_Safe_Call_Void();
self->BuffFadeDetrimental();
}
void Lua_Mob::BuffFadeDetrimentalByCaster(Lua_Mob caster)
{
Lua_Safe_Call_Void();
self->BuffFadeDetrimentalByCaster(caster);
}
void Lua_Mob::BuffFadeNonPersistDeath()
{
Lua_Safe_Call_Void();
self->BuffFadeNonPersistDeath();
}
void Lua_Mob::BuffFadeSongs()
{
Lua_Safe_Call_Void();
self->BuffFadeSongs();
}
luabind::scope lua_register_mob() { luabind::scope lua_register_mob() {
return luabind::class_<Lua_Mob, Lua_Entity>("Mob") return luabind::class_<Lua_Mob, Lua_Entity>("Mob")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
@@ -3483,11 +3513,16 @@ luabind::scope lua_register_mob() {
.def("BuffCount", (uint32(Lua_Mob::*)(bool))&Lua_Mob::BuffCount) .def("BuffCount", (uint32(Lua_Mob::*)(bool))&Lua_Mob::BuffCount)
.def("BuffCount", (uint32(Lua_Mob::*)(bool,bool))&Lua_Mob::BuffCount) .def("BuffCount", (uint32(Lua_Mob::*)(bool,bool))&Lua_Mob::BuffCount)
.def("BuffFadeAll", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeAll) .def("BuffFadeAll", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeAll)
.def("BuffFadeBeneficial", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeBeneficial)
.def("BuffFadeByEffect", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeByEffect) .def("BuffFadeByEffect", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeByEffect)
.def("BuffFadeByEffect", (void(Lua_Mob::*)(int,int))&Lua_Mob::BuffFadeByEffect) .def("BuffFadeByEffect", (void(Lua_Mob::*)(int,int))&Lua_Mob::BuffFadeByEffect)
.def("BuffFadeBySlot", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySlot) .def("BuffFadeBySlot", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySlot)
.def("BuffFadeBySlot", (void(Lua_Mob::*)(int,bool))&Lua_Mob::BuffFadeBySlot) .def("BuffFadeBySlot", (void(Lua_Mob::*)(int,bool))&Lua_Mob::BuffFadeBySlot)
.def("BuffFadeBySpellID", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySpellID) .def("BuffFadeBySpellID", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySpellID)
.def("BuffFadeDetrimental", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeDetrimental)
.def("BuffFadeDetrimentalByCaster", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::BuffFadeDetrimentalByCaster)
.def("BuffFadeNonPersistDeath", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeNonPersistDeath)
.def("BuffFadeSongs", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeSongs)
.def("CalculateDistance", (float(Lua_Mob::*)(double,double,double))&Lua_Mob::CalculateDistance) .def("CalculateDistance", (float(Lua_Mob::*)(double,double,double))&Lua_Mob::CalculateDistance)
.def("CalculateDistance", (float(Lua_Mob::*)(Lua_Mob))&Lua_Mob::CalculateDistance) .def("CalculateDistance", (float(Lua_Mob::*)(Lua_Mob))&Lua_Mob::CalculateDistance)
.def("CalculateHeadingToTarget", (double(Lua_Mob::*)(double,double))&Lua_Mob::CalculateHeadingToTarget) .def("CalculateHeadingToTarget", (double(Lua_Mob::*)(double,double))&Lua_Mob::CalculateHeadingToTarget)
+6
View File
@@ -10,6 +10,7 @@ class Lua_Item;
class Lua_ItemInst; class Lua_ItemInst;
class Lua_StatBonuses; class Lua_StatBonuses;
class Lua_Bot; class Lua_Bot;
class Lua_Merc;
class Lua_NPC; class Lua_NPC;
class Lua_Client; class Lua_Client;
struct Lua_Mob_List; struct Lua_Mob_List;
@@ -605,6 +606,11 @@ public:
void AreaSpell(Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int max_targets); void AreaSpell(Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int max_targets);
void MassGroupBuff(Lua_Mob center, uint16 spell_id); void MassGroupBuff(Lua_Mob center, uint16 spell_id);
void MassGroupBuff(Lua_Mob center, uint16 spell_id, bool affect_caster); void MassGroupBuff(Lua_Mob center, uint16 spell_id, bool affect_caster);
void BuffFadeBeneficial();
void BuffFadeDetrimental();
void BuffFadeDetrimentalByCaster(Lua_Mob caster);
void BuffFadeNonPersistDeath();
void BuffFadeSongs();
}; };
#endif #endif
+192 -1
View File
@@ -32,6 +32,7 @@
#include "lua_inventory.h" #include "lua_inventory.h"
#include "lua_item.h" #include "lua_item.h"
#include "lua_iteminst.h" #include "lua_iteminst.h"
#include "lua_merc.h"
#include "lua_mob.h" #include "lua_mob.h"
#include "lua_npc.h" #include "lua_npc.h"
#include "lua_object.h" #include "lua_object.h"
@@ -184,7 +185,8 @@ const char *LuaEvents[_LargestEventID] = {
"event_entity_variable_set", "event_entity_variable_set",
"event_entity_variable_update", "event_entity_variable_update",
"event_aa_loss", "event_aa_loss",
"event_spell_blocked" "event_spell_blocked",
"event_read_item"
}; };
extern Zone *zone; extern Zone *zone;
@@ -348,6 +350,7 @@ LuaParser::LuaParser() {
PlayerArgumentDispatch[EVENT_ENTITY_VARIABLE_UPDATE] = handle_player_entity_variable; PlayerArgumentDispatch[EVENT_ENTITY_VARIABLE_UPDATE] = handle_player_entity_variable;
PlayerArgumentDispatch[EVENT_AA_LOSS] = handle_player_aa_loss; PlayerArgumentDispatch[EVENT_AA_LOSS] = handle_player_aa_loss;
PlayerArgumentDispatch[EVENT_SPELL_BLOCKED] = handle_player_spell_blocked; PlayerArgumentDispatch[EVENT_SPELL_BLOCKED] = handle_player_spell_blocked;
PlayerArgumentDispatch[EVENT_READ_ITEM] = handle_player_read_item;
ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click; ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click;
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click; ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;
@@ -1277,6 +1280,7 @@ void LuaParser::MapFunctions(lua_State *L) {
lua_register_npc(), lua_register_npc(),
lua_register_client(), lua_register_client(),
lua_register_bot(), lua_register_bot(),
lua_register_merc(),
lua_register_inventory(), lua_register_inventory(),
lua_register_inventory_where(), lua_register_inventory_where(),
lua_register_iteminst(), lua_register_iteminst(),
@@ -1833,3 +1837,190 @@ void LuaParser::LoadBotScript(std::string filename) {
void LuaParser::LoadGlobalBotScript(std::string filename) { void LuaParser::LoadGlobalBotScript(std::string filename) {
LoadScript(filename, "global_bot"); LoadScript(filename, "global_bot");
} }
int LuaParser::EventMerc(
QuestEventID evt,
Merc *merc,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
if (!merc) {
return 0;
}
if (!MercHasQuestSub(evt)) {
return 0;
}
return _EventMerc("merc", evt, merc, init, data, extra_data, extra_pointers);
}
int LuaParser::EventGlobalMerc(
QuestEventID evt,
Merc *merc,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
if (!merc) {
return 0;
}
if (!GlobalMercHasQuestSub(evt)) {
return 0;
}
return _EventMerc("global_merc", evt, merc, init, data, extra_data, extra_pointers);
}
int LuaParser::_EventMerc(
std::string package_name,
QuestEventID evt,
Merc *merc,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers,
luabind::adl::object *l_func
) {
const char *sub_name = LuaEvents[evt];
int start = lua_gettop(L);
try {
int npop = 2;
PushErrorHandler(L);
if(l_func != nullptr) {
l_func->push(L);
} else {
lua_getfield(L, LUA_REGISTRYINDEX, package_name.c_str());
lua_getfield(L, -1, sub_name);
npop = 3;
}
lua_createtable(L, 0, 0);
//push self
Lua_Merc l_merc(merc);
luabind::adl::object l_merc_o = luabind::adl::object(L, l_merc);
l_merc_o.push(L);
lua_setfield(L, -2, "self");
auto arg_function = NPCArgumentDispatch[evt];
arg_function(this, L, merc, init, data, extra_data, extra_pointers);
auto* c = (init && init->IsClient()) ? init->CastToClient() : nullptr;
quest_manager.StartQuest(merc, c);
if(lua_pcall(L, 1, 1, start + 1)) {
std::string error = lua_tostring(L, -1);
AddError(error);
quest_manager.EndQuest();
lua_pop(L, npop);
return 0;
}
quest_manager.EndQuest();
if(lua_isnumber(L, -1)) {
int ret = static_cast<int>(lua_tointeger(L, -1));
lua_pop(L, npop);
return ret;
}
lua_pop(L, npop);
} catch(std::exception &ex) {
AddError(
fmt::format(
"Lua Exception | [{}] for Merc [{}] in [{}]: {}",
sub_name,
merc->GetID(),
package_name,
ex.what()
)
);
//Restore our stack to the best of our ability
int end = lua_gettop(L);
int n = end - start;
if(n > 0) {
lua_pop(L, n);
}
}
return 0;
}
int LuaParser::DispatchEventMerc(
QuestEventID evt,
Merc *merc,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
std::string package_name = "merc";
auto iter = lua_encounter_events_registered.find(package_name);
if (iter == lua_encounter_events_registered.end()) {
return 0;
}
int ret = 0;
auto riter = iter->second.begin();
while (riter != iter->second.end()) {
if (riter->event_id == evt) {
package_name = fmt::format("encounter_{}", riter->encounter_name);
int i = _EventMerc(package_name, evt, merc, init, data, extra_data, extra_pointers, &riter->lua_reference);
if (i != 0) {
ret = i;
}
}
++riter;
}
return ret;
}
bool LuaParser::MercHasQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "merc");
}
bool LuaParser::GlobalMercHasQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "global_merc");
}
void LuaParser::LoadMercScript(std::string filename) {
LoadScript(filename, "merc");
}
void LuaParser::LoadGlobalMercScript(std::string filename) {
LoadScript(filename, "global_merc");
}
+38
View File
@@ -109,6 +109,22 @@ public:
uint32 extra_data, uint32 extra_data,
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
); );
virtual int EventMerc(
QuestEventID evt,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual int EventGlobalMerc(
QuestEventID evt,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt); virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt);
virtual bool HasGlobalQuestSub(QuestEventID evt); virtual bool HasGlobalQuestSub(QuestEventID evt);
@@ -120,6 +136,8 @@ public:
virtual bool HasEncounterSub(const std::string& package_name, QuestEventID evt); virtual bool HasEncounterSub(const std::string& package_name, QuestEventID evt);
virtual bool BotHasQuestSub(QuestEventID evt); virtual bool BotHasQuestSub(QuestEventID evt);
virtual bool GlobalBotHasQuestSub(QuestEventID evt); virtual bool GlobalBotHasQuestSub(QuestEventID evt);
virtual bool MercHasQuestSub(QuestEventID evt);
virtual bool GlobalMercHasQuestSub(QuestEventID evt);
virtual void LoadNPCScript(std::string filename, int npc_id); virtual void LoadNPCScript(std::string filename, int npc_id);
virtual void LoadGlobalNPCScript(std::string filename); virtual void LoadGlobalNPCScript(std::string filename);
@@ -130,6 +148,8 @@ public:
virtual void LoadEncounterScript(std::string filename, std::string encounter_name); virtual void LoadEncounterScript(std::string filename, std::string encounter_name);
virtual void LoadBotScript(std::string filename); virtual void LoadBotScript(std::string filename);
virtual void LoadGlobalBotScript(std::string filename); virtual void LoadGlobalBotScript(std::string filename);
virtual void LoadMercScript(std::string filename);
virtual void LoadGlobalMercScript(std::string filename);
virtual void AddVar(std::string name, std::string val); virtual void AddVar(std::string name, std::string val);
virtual std::string GetVar(std::string name); virtual std::string GetVar(std::string name);
@@ -179,6 +199,14 @@ public:
uint32 extra_data, uint32 extra_data,
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
); );
virtual int DispatchEventMerc(
QuestEventID evt,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
static LuaParser* Instance() { static LuaParser* Instance() {
static LuaParser inst; static LuaParser inst;
@@ -269,6 +297,16 @@ private:
std::vector<std::any> *extra_pointers, std::vector<std::any> *extra_pointers,
luabind::adl::object *l_func = nullptr luabind::adl::object *l_func = nullptr
); );
int _EventMerc(
std::string package_name,
QuestEventID evt,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers,
luabind::adl::object* l_func = nullptr
);
void LoadScript(std::string filename, std::string package_name); void LoadScript(std::string filename, std::string package_name);
void MapFunctions(lua_State *L); void MapFunctions(lua_State *L);
+55
View File
@@ -734,6 +734,18 @@ void handle_player_death(
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[4])); lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[4]));
lua_setfield(L, -2, "killed_entity_id"); lua_setfield(L, -2, "killed_entity_id");
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[5]));
lua_setfield(L, -2, "combat_start_time");
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[6]));
lua_setfield(L, -2, "combat_end_time");
lua_pushinteger(L, Strings::ToBigInt(sep.arg[7]));
lua_setfield(L, -2, "damage_received");
lua_pushinteger(L, Strings::ToBigInt(sep.arg[8]));
lua_setfield(L, -2, "healing_received");
} }
void handle_player_timer( void handle_player_timer(
@@ -1745,6 +1757,49 @@ void handle_player_spell_blocked(
lua_setfield(L, -2, "cast_spell"); lua_setfield(L, -2, "cast_spell");
} }
void handle_player_read_item(
QuestInterface *parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
)
{
lua_pushstring(L, data.c_str());
lua_setfield(L, -2, "text_file");
lua_pushinteger(L, extra_data);
lua_setfield(L, -2, "item_id");
if (extra_pointers) {
if (extra_pointers->size() == 7) {
lua_pushstring(L, std::any_cast<std::string>(extra_pointers->at(0)).c_str());
lua_setfield(L, -2, "book_text");
lua_pushboolean(L, std::any_cast<int8>(extra_pointers->at(1)));
lua_setfield(L, -2, "can_cast");
lua_pushboolean(L, std::any_cast<int8>(extra_pointers->at(2)));
lua_setfield(L, -2, "can_scribe");
lua_pushinteger(L, std::any_cast<int16>(extra_pointers->at(3)));
lua_setfield(L, -2, "slot_id");
lua_pushinteger(L, std::any_cast<int>(extra_pointers->at(4)));
lua_setfield(L, -2, "target_id");
lua_pushinteger(L, std::any_cast<uint8>(extra_pointers->at(5)));
lua_setfield(L, -2, "type");
Lua_ItemInst l_item(std::any_cast<EQ::ItemInstance*>(extra_pointers->at(6)));
luabind::adl::object l_item_o = luabind::adl::object(L, l_item);
l_item_o.push(L);
lua_setfield(L, -2, "item");
}
}
}
// Item // Item
void handle_item_click( void handle_item_click(
QuestInterface *parse, QuestInterface *parse,
+10
View File
@@ -8,6 +8,7 @@ typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, EQ::Ite
typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, Mob*, Client*, uint32, std::string, uint32, std::vector<std::any>*); typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, Mob*, Client*, uint32, std::string, uint32, std::vector<std::any>*);
typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector<std::any>*); typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector<std::any>*);
typedef void(*BotArgumentHandler)(QuestInterface*, lua_State*, Bot*, Mob*, std::string, uint32, std::vector<std::any>*); typedef void(*BotArgumentHandler)(QuestInterface*, lua_State*, Bot*, Mob*, std::string, uint32, std::vector<std::any>*);
typedef void(*MercArgumentHandler)(QuestInterface*, lua_State*, Merc*, Mob*, std::string, uint32, std::vector<std::any>*);
// NPC // NPC
void handle_npc_event_say( void handle_npc_event_say(
@@ -855,6 +856,15 @@ void handle_player_spell_blocked(
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
); );
void handle_player_read_item(
QuestInterface *parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
// Item // Item
void handle_item_click( void handle_item_click(
QuestInterface *parse, QuestInterface *parse,
+17 -10
View File
@@ -5,6 +5,7 @@
#include "entity.h" #include "entity.h"
#include "groups.h" #include "groups.h"
#include "mob.h" #include "mob.h"
#include "quest_parser_collection.h"
#include "zone.h" #include "zone.h"
#include "string_ids.h" #include "string_ids.h"
@@ -4078,12 +4079,6 @@ bool Merc::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillT
Save(); Save();
//no corpse, no exp if we're a merc.
//We'll suspend instead, since that's what live does.
//Not actually sure live supports 'depopping' merc corpses.
//if(entity_list.GetCorpseByID(GetID()))
// entity_list.GetCorpseByID(GetID())->Depop();
// If client is in zone, suspend merc, else depop it. // If client is in zone, suspend merc, else depop it.
if (!Suspend()) { if (!Suspend()) {
Depop(); Depop();
@@ -4671,7 +4666,6 @@ bool Merc::Spawn(Client *owner) {
//UpdateMercAppearance(); //UpdateMercAppearance();
return true; return true;
} }
@@ -5189,9 +5183,6 @@ void Client::SpawnMerc(Merc* merc, bool setMaxStats) {
merc->SetStance(GetMercInfo().Stance); merc->SetStance(GetMercInfo().Stance);
Log(Logs::General, Logs::Mercenaries, "SpawnMerc Success for %s.", GetName()); Log(Logs::General, Logs::Mercenaries, "SpawnMerc Success for %s.", GetName());
return;
} }
bool Merc::Suspend() { bool Merc::Suspend() {
@@ -5914,3 +5905,19 @@ uint32 Merc::CalcUpkeepCost(uint32 templateID , uint8 level, uint8 currency_type
return cost; return cost;
} }
void Merc::Signal(int signal_id)
{
if (parse->MercHasQuestSub(EVENT_SIGNAL)) {
parse->EventMerc(EVENT_SIGNAL, this, nullptr, std::to_string(signal_id), 0);
}
}
void Merc::SendPayload(int payload_id, std::string payload_value)
{
if (parse->MercHasQuestSub(EVENT_PAYLOAD)) {
const auto& export_string = fmt::format("{} {}", payload_id, payload_value);
parse->EventMerc(EVENT_PAYLOAD, this, nullptr, export_string, 0);
}
}
+3
View File
@@ -146,6 +146,9 @@ public:
bool IsMedding() { return _medding; }; bool IsMedding() { return _medding; };
bool IsSuspended() { return _suspended; }; bool IsSuspended() { return _suspended; };
void Signal(int signal_id);
void SendPayload(int payload_id, std::string payload_value);
static uint32 CalcPurchaseCost( uint32 templateID , uint8 level, uint8 currency_type = 0); static uint32 CalcPurchaseCost( uint32 templateID , uint8 level, uint8 currency_type = 0);
static uint32 CalcUpkeepCost( uint32 templateID , uint8 level, uint8 currency_type = 0); static uint32 CalcUpkeepCost( uint32 templateID , uint8 level, uint8 currency_type = 0);
+89 -87
View File
@@ -128,8 +128,9 @@ Mob::Mob(
attack_anim_timer(500), attack_anim_timer(500),
position_update_melee_push_timer(500), position_update_melee_push_timer(500),
hate_list_cleanup_timer(6000), hate_list_cleanup_timer(6000),
mob_close_scan_timer(6000), m_scan_close_mobs_timer(6000),
mob_check_moving_timer(1000) m_mob_check_moving_timer(1000),
bot_attack_flag_timer(10000)
{ {
mMovementManager = &MobMovementManager::Get(); mMovementManager = &MobMovementManager::Get();
mMovementManager->AddMob(this); mMovementManager->AddMob(this);
@@ -400,6 +401,10 @@ Mob::Mob(
pet_owner_npc = false; pet_owner_npc = false;
pet_targetlock_id = 0; pet_targetlock_id = 0;
//bot attack flag
bot_attack_flags.clear();
bot_attack_flag_timer.Disable();
attacked_count = 0; attacked_count = 0;
mezzed = false; mezzed = false;
stunned = false; stunned = false;
@@ -512,7 +517,7 @@ Mob::Mob(
m_manual_follow = false; m_manual_follow = false;
mob_close_scan_timer.Trigger(); m_scan_close_mobs_timer.Trigger();
SetCanOpenDoors(true); SetCanOpenDoors(true);
@@ -565,7 +570,7 @@ Mob::~Mob()
entity_list.RemoveMobFromCloseLists(this); entity_list.RemoveMobFromCloseLists(this);
entity_list.RemoveAuraFromMobs(this); entity_list.RemoveAuraFromMobs(this);
close_mobs.clear(); m_close_mobs.clear();
LeaveHealRotationTargetPool(); LeaveHealRotationTargetPool();
} }
@@ -4063,7 +4068,8 @@ uint8 Mob::GetDefaultGender(uint16 in_race, uint8 in_gender) {
in_race == Race::Human2 || in_race == Race::Human2 ||
in_race == Race::ElvenGhost || in_race == Race::ElvenGhost ||
in_race == Race::HumanGhost || in_race == Race::HumanGhost ||
in_race == Race::Coldain2 in_race == Race::Coldain2 ||
in_race == Race::Akheva
) { ) {
if (in_gender >= Gender::Neuter) { // Male default for PC Races if (in_gender >= Gender::Neuter) { // Male default for PC Races
return Gender::Male; return Gender::Male;
@@ -5012,7 +5018,7 @@ void Mob::Say(const char *format, ...)
int16 distance = 200; int16 distance = 200;
if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) { if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) {
for (auto &e : entity_list.GetCloseMobList(talker, (distance * distance))) { for (auto &e : talker->GetCloseMobList(distance)) {
Mob *mob = e.second; Mob *mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -5508,37 +5514,13 @@ void Mob::SetTarget(Mob *mob)
target = mob; target = mob;
entity_list.UpdateHoTT(this); entity_list.UpdateHoTT(this);
const auto has_target_change_event = (
parse->HasQuestSub(GetNPCTypeID(), EVENT_TARGET_CHANGE) ||
parse->PlayerHasQuestSub(EVENT_TARGET_CHANGE) ||
parse->BotHasQuestSub(EVENT_TARGET_CHANGE)
);
if (IsClient() && CastToClient()->admin > AccountStatus::GMMgmt) { if (IsClient() && CastToClient()->admin > AccountStatus::GMMgmt) {
DisplayInfo(mob); DisplayInfo(mob);
} }
if (has_target_change_event) { std::vector<std::any> args = { mob };
std::vector<std::any> args;
args.emplace_back(mob); parse->EventMob(EVENT_TARGET_CHANGE, this, mob, [&]() { return ""; }, 0, &args);
if (IsNPC()) {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_TARGET_CHANGE)) {
parse->EventNPC(EVENT_TARGET_CHANGE, CastToNPC(), mob, "", 0, &args);
}
} else if (IsClient()) {
if (parse->PlayerHasQuestSub(EVENT_TARGET_CHANGE)) {
parse->EventPlayer(EVENT_TARGET_CHANGE, CastToClient(), "", 0, &args);
}
CastToClient()->SetBotPrecombat(false); // Any change in target will nullify this flag (target == mob checked above)
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_TARGET_CHANGE)) {
parse->EventBot(EVENT_TARGET_CHANGE, CastToBot(), mob, "", 0, &args);
}
}
}
if (IsPet() && GetOwner() && GetOwner()->IsClient()) { if (IsPet() && GetOwner() && GetOwner()->IsClient()) {
GetOwner()->CastToClient()->UpdateXTargetType(MyPetTarget, mob); GetOwner()->CastToClient()->UpdateXTargetType(MyPetTarget, mob);
@@ -5711,22 +5693,10 @@ bool Mob::ClearEntityVariables()
return false; return false;
} }
if (
(IsBot() && parse->BotHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) ||
(IsClient() && parse->PlayerHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) ||
(IsNPC() && parse->HasQuestSub(GetNPCTypeID(), EVENT_ENTITY_VARIABLE_DELETE))
) {
for (const auto& e : m_EntityVariables) { for (const auto& e : m_EntityVariables) {
std::vector<std::any> args = { e.first, e.second }; std::vector<std::any> args = { e.first, e.second };
if (IsBot()) { parse->EventMob(EVENT_ENTITY_VARIABLE_DELETE, this, nullptr, [&]() { return ""; }, 0, &args);
parse->EventBot(EVENT_ENTITY_VARIABLE_DELETE, CastToBot(), nullptr, "", 0, &args);
} else if (IsClient()) {
parse->EventPlayer(EVENT_ENTITY_VARIABLE_DELETE, CastToClient(), "", 0, &args);
} else if (IsNPC()) {
parse->EventNPC(EVENT_ENTITY_VARIABLE_DELETE, CastToNPC(), nullptr, "", 0, &args);
}
}
} }
m_EntityVariables.clear(); m_EntityVariables.clear();
@@ -5744,23 +5714,10 @@ bool Mob::DeleteEntityVariable(std::string variable_name)
return false; return false;
} }
m_EntityVariables.erase(v);
if (
(IsBot() && parse->BotHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) ||
(IsClient() && parse->PlayerHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) ||
(IsNPC() && parse->HasQuestSub(GetNPCTypeID(), EVENT_ENTITY_VARIABLE_DELETE))
) {
std::vector<std::any> args = { v->first, v->second }; std::vector<std::any> args = { v->first, v->second };
parse->EventMob(EVENT_ENTITY_VARIABLE_DELETE, this, nullptr, [&]() { return ""; }, 0, &args);
if (IsBot()) { m_EntityVariables.erase(v);
parse->EventBot(EVENT_ENTITY_VARIABLE_DELETE, CastToBot(), nullptr, "", 0, &args);
} else if (IsClient()) {
parse->EventPlayer(EVENT_ENTITY_VARIABLE_DELETE, CastToClient(), "", 0, &args);
} else if (IsNPC()) {
parse->EventNPC(EVENT_ENTITY_VARIABLE_DELETE, CastToNPC(), nullptr, "", 0, &args);
}
}
return true; return true;
} }
@@ -5808,32 +5765,16 @@ void Mob::SetEntityVariable(std::string variable_name, std::string variable_valu
return; return;
} }
const QuestEventID event_id = (
!EntityVariableExists(variable_name) ?
EVENT_ENTITY_VARIABLE_SET :
EVENT_ENTITY_VARIABLE_UPDATE
);
if (
(IsBot() && parse->BotHasQuestSub(event_id)) ||
(IsClient() && parse->PlayerHasQuestSub(event_id)) ||
(IsNPC() && parse->HasQuestSub(GetNPCTypeID(), event_id))
) {
std::vector<std::any> args; std::vector<std::any> args;
if (event_id != EVENT_ENTITY_VARIABLE_UPDATE) { if (!EntityVariableExists(variable_name)) {
args = { variable_name, variable_value }; args = { variable_name, variable_value };
parse->EventMob(EVENT_ENTITY_VARIABLE_SET, this, nullptr, [&]() { return ""; }, 0, &args);
} else { } else {
args = { variable_name, GetEntityVariable(variable_name), variable_value }; args = { variable_name, GetEntityVariable(variable_name), variable_value };
}
if (IsBot()) { parse->EventMob(EVENT_ENTITY_VARIABLE_UPDATE, this, nullptr, [&]() { return ""; }, 0, &args);
parse->EventBot(event_id, CastToBot(), nullptr, "", 0, &args);
} else if (IsClient()) {
parse->EventPlayer(event_id, CastToClient(), "", 0, &args);
} else if (IsNPC()) {
parse->EventNPC(event_id, CastToNPC(), nullptr, "", 0, &args);
}
} }
m_EntityVariables[variable_name] = variable_value; m_EntityVariables[variable_name] = variable_value;
@@ -6171,9 +6112,7 @@ int32 Mob::GetPositionalDmgTakenAmt(Mob *attacker)
void Mob::SetBottomRampageList() void Mob::SetBottomRampageList()
{ {
auto &mob_list = entity_list.GetCloseMobList(this); for (auto &e : GetCloseMobList()) {
for (auto &e : mob_list) {
auto mob = e.second; auto mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -6198,9 +6137,7 @@ void Mob::SetBottomRampageList()
void Mob::SetTopRampageList() void Mob::SetTopRampageList()
{ {
auto &mob_list = entity_list.GetCloseMobList(this); for (auto &e : GetCloseMobList()) {
for (auto &e : mob_list) {
auto mob = e.second; auto mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -8614,7 +8551,7 @@ void Mob::SetExtraHaste(int haste, bool need_to_save)
bool Mob::IsCloseToBanker() bool Mob::IsCloseToBanker()
{ {
for (auto &e: entity_list.GetCloseMobList(this)) { for (auto &e: GetCloseMobList()) {
auto mob = e.second; auto mob = e.second;
if (mob && mob->IsNPC() && mob->GetClass() == Class::Banker) { if (mob && mob->IsNPC() && mob->GetClass() == Class::Banker) {
return true; return true;
@@ -8623,3 +8560,68 @@ bool Mob::IsCloseToBanker()
return false; return false;
} }
bool Mob::HasBotAttackFlag(Mob* tar) {
if (!tar) {
return false;
}
std::vector<uint32> l = tar->GetBotAttackFlags();
for (uint32 e : l) {
if (IsBot() && e == CastToBot()->GetBotOwnerCharacterID()) {
return true;
}
if (IsClient() && e == CastToClient()->CharacterID()) {
return true;
}
}
return false;
}
const uint16 scan_close_mobs_timer_moving = 6000; // 6 seconds
const uint16 scan_close_mobs_timer_idle = 60000; // 60 seconds
void Mob::CheckScanCloseMobsMovingTimer()
{
LogAIScanCloseDetail(
"Mob [{}] {}moving, scan timer [{}]",
GetCleanName(),
IsMoving() ? "" : "NOT ",
m_scan_close_mobs_timer.GetRemainingTime()
);
// If the moving timer triggers, lets see if we are moving or idle to restart the appropriate
// dynamic timer
if (m_mob_check_moving_timer.Check()) {
// If the mob is still moving, restart the moving timer
if (moving) {
if (m_scan_close_mobs_timer.GetRemainingTime() > scan_close_mobs_timer_moving) {
LogAIScanCloseDetail("Mob [{}] Restarting with moving timer", GetCleanName());
m_scan_close_mobs_timer.Disable();
m_scan_close_mobs_timer.Start(scan_close_mobs_timer_moving);
m_scan_close_mobs_timer.Trigger();
}
}
// If the mob is not moving, restart the idle timer
else if (m_scan_close_mobs_timer.GetDuration() == scan_close_mobs_timer_moving) {
LogAIScanCloseDetail("Mob [{}] Restarting with idle timer", GetCleanName());
m_scan_close_mobs_timer.Disable();
m_scan_close_mobs_timer.Start(scan_close_mobs_timer_idle);
}
}
}
void Mob::ScanCloseMobProcess()
{
if (m_scan_close_mobs_timer.Check()) {
entity_list.ScanCloseMobs(this);
}
}
std::unordered_map<uint16, Mob *> &Mob::GetCloseMobList(float distance)
{
return entity_list.GetCloseMobList(this, distance);
}
+19 -3
View File
@@ -201,9 +201,12 @@ public:
void DisplayInfo(Mob *mob); void DisplayInfo(Mob *mob);
std::unordered_map<uint16, Mob *> close_mobs; std::unordered_map<uint16, Mob *> m_close_mobs;
Timer mob_close_scan_timer; Timer m_scan_close_mobs_timer;
Timer mob_check_moving_timer; Timer m_mob_check_moving_timer;
// Bot attack flag
Timer bot_attack_flag_timer;
//Somewhat sorted: needs documenting! //Somewhat sorted: needs documenting!
@@ -444,6 +447,7 @@ public:
void BuffFadeBySlot(int slot, bool iRecalcBonuses = true); void BuffFadeBySlot(int slot, bool iRecalcBonuses = true);
void BuffFadeDetrimentalByCaster(Mob *caster); void BuffFadeDetrimentalByCaster(Mob *caster);
void BuffFadeBySitModifier(); void BuffFadeBySitModifier();
void BuffFadeSongs();
void BuffDetachCaster(Mob *caster); void BuffDetachCaster(Mob *caster);
bool IsAffectedByBuffByGlobalGroup(GlobalGroup group); bool IsAffectedByBuffByGlobalGroup(GlobalGroup group);
void BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration); void BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration);
@@ -1102,6 +1106,11 @@ public:
bool invulnerable; bool invulnerable;
bool qglobal; bool qglobal;
inline std::vector<uint32> GetBotAttackFlags() { return bot_attack_flags; }
inline void SetBotAttackFlag(uint32 value) { bot_attack_flags.push_back(value); }
inline void ClearBotAttackFlags() { bot_attack_flags.clear(); }
bool HasBotAttackFlag(Mob* tar);
virtual void SetAttackTimer(); virtual void SetAttackTimer();
inline void SetInvul(bool invul) { invulnerable=invul; } inline void SetInvul(bool invul) { invulnerable=invul; }
inline bool GetInvul(void) { return invulnerable; } inline bool GetInvul(void) { return invulnerable; }
@@ -1479,6 +1488,10 @@ public:
bool IsCloseToBanker(); bool IsCloseToBanker();
void ScanCloseMobProcess();
std::unordered_map<uint16, Mob *> &GetCloseMobList(float distance = 0.0f);
void CheckScanCloseMobsMovingTimer();
protected: protected:
void CommonDamage(Mob* other, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None); void CommonDamage(Mob* other, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None);
static uint16 GetProcID(uint16 spell_id, uint8 effect_index); static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
@@ -1863,6 +1876,9 @@ protected:
bool pet_owner_npc; // Flags pets as belonging to an NPC bool pet_owner_npc; // Flags pets as belonging to an NPC
uint32 pet_targetlock_id; uint32 pet_targetlock_id;
//bot attack flags
std::vector<uint32> bot_attack_flags;
glm::vec3 m_TargetRing; glm::vec3 m_TargetRing;
GravityBehavior flymode; GravityBehavior flymode;
+6 -39
View File
@@ -1391,29 +1391,8 @@ void Mob::AI_Process() {
StopNavigation(); StopNavigation();
} }
} }
else if (zone->CanDoCombat() && CastToNPC()->GetNPCAggro() && AI_scan_area_timer->Check()) { else if (zone->CanDoCombat() && IsNPC() && CastToNPC()->GetNPCAggro() && AI_scan_area_timer->Check()) {
CastToNPC()->DoNpcToNpcAggroScan();
/**
* NPC to NPC aggro (npc_aggro flag set)
*/
for (auto &close_mob : close_mobs) {
Mob *mob = close_mob.second;
if (mob->IsClient()) {
continue;
}
if (CheckWillAggro(mob)) {
AddToHateList(mob);
}
}
AI_scan_area_timer->Disable();
AI_scan_area_timer->Start(
RandomTimer(RuleI(NPC, NPCToNPCAggroTimerMin), RuleI(NPC, NPCToNPCAggroTimerMax)),
false
);
} }
else if (AI_movement_timer->Check() && !IsRooted()) { else if (AI_movement_timer->Check() && !IsRooted()) {
if (IsPet()) { if (IsPet()) {
@@ -1776,6 +1755,8 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
SetAppearance(eaStanding); SetAppearance(eaStanding);
parse->EventBotMerc(EVENT_COMBAT, this, attacker, [&] { return "1"; });
if (IsNPC()) { if (IsNPC()) {
CastToNPC()->AIautocastspell_timer->Start(300, false); CastToNPC()->AIautocastspell_timer->Start(300, false);
@@ -1814,12 +1795,6 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
} }
} }
} }
if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_COMBAT)) {
parse->EventBot(EVENT_COMBAT, CastToBot(), attacker, "1", 0);
}
}
} }
// Note: Hate list may not be actually clear until after this function call completes // Note: Hate list may not be actually clear until after this function call completes
@@ -1840,15 +1815,12 @@ void Mob::AI_Event_NoLongerEngaged() {
StopNavigation(); StopNavigation();
ClearRampage(); ClearRampage();
parse->EventBotMercNPC(EVENT_COMBAT, this, nullptr, [&]() { return "0"; });
if (IsNPC()) { if (IsNPC()) {
SetPrimaryAggro(false); SetPrimaryAggro(false);
SetAssistAggro(false); SetAssistAggro(false);
if (CastToNPC()->GetCombatEvent() && GetHP() > 0) { if (CastToNPC()->GetCombatEvent() && GetHP() > 0) {
if (entity_list.GetNPCByID(GetID())) {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_COMBAT)) {
parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0);
}
const uint32 emote_id = CastToNPC()->GetEmoteID(); const uint32 emote_id = CastToNPC()->GetEmoteID();
if (emote_id) { if (emote_id) {
CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emote_id); CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emote_id);
@@ -1858,11 +1830,6 @@ void Mob::AI_Event_NoLongerEngaged() {
CastToNPC()->SetCombatEvent(false); CastToNPC()->SetCombatEvent(false);
} }
} }
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_COMBAT)) {
parse->EventBot(EVENT_COMBAT, CastToBot(), nullptr, "0", 0);
}
}
} }
//this gets called from InterruptSpell() for failure or SpellFinished() for success //this gets called from InterruptSpell() for failure or SpellFinished() for success
-136
View File
@@ -127,11 +127,6 @@ public:
} }
/**
* @param mob_movement_manager
* @param mob
* @return
*/
virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob) virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob)
{ {
if (!mob->IsAIControlled()) { if (!mob->IsAIControlled()) {
@@ -286,11 +281,6 @@ public:
} }
/**
* @param mob_movement_manager
* @param mob
* @return
*/
virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob) virtual bool Process(MobMovementManager *mob_movement_manager, Mob *mob)
{ {
if (!mob->IsAIControlled()) { if (!mob->IsAIControlled()) {
@@ -707,33 +697,21 @@ void MobMovementManager::Process()
} }
} }
/**
* @param mob
*/
void MobMovementManager::AddMob(Mob *mob) void MobMovementManager::AddMob(Mob *mob)
{ {
_impl->Entries.insert(std::make_pair(mob, MobMovementEntry())); _impl->Entries.insert(std::make_pair(mob, MobMovementEntry()));
} }
/**
* @param mob
*/
void MobMovementManager::RemoveMob(Mob *mob) void MobMovementManager::RemoveMob(Mob *mob)
{ {
_impl->Entries.erase(mob); _impl->Entries.erase(mob);
} }
/**
* @param client
*/
void MobMovementManager::AddClient(Client *client) void MobMovementManager::AddClient(Client *client)
{ {
_impl->Clients.push_back(client); _impl->Clients.push_back(client);
} }
/**
* @param client
*/
void MobMovementManager::RemoveClient(Client *client) void MobMovementManager::RemoveClient(Client *client)
{ {
auto iter = _impl->Clients.begin(); auto iter = _impl->Clients.begin();
@@ -747,11 +725,6 @@ void MobMovementManager::RemoveClient(Client *client)
} }
} }
/**
* @param who
* @param to
* @param mob_movement_mode
*/
void MobMovementManager::RotateTo(Mob *who, float to, MobMovementMode mob_movement_mode) void MobMovementManager::RotateTo(Mob *who, float to, MobMovementMode mob_movement_mode)
{ {
auto iter = _impl->Entries.find(who); auto iter = _impl->Entries.find(who);
@@ -764,13 +737,6 @@ void MobMovementManager::RotateTo(Mob *who, float to, MobMovementMode mob_moveme
PushRotateTo(ent.second, who, to, mob_movement_mode); PushRotateTo(ent.second, who, to, mob_movement_mode);
} }
/**
* @param who
* @param x
* @param y
* @param z
* @param heading
*/
void MobMovementManager::Teleport(Mob *who, float x, float y, float z, float heading) void MobMovementManager::Teleport(Mob *who, float x, float y, float z, float heading)
{ {
auto iter = _impl->Entries.find(who); auto iter = _impl->Entries.find(who);
@@ -781,13 +747,6 @@ void MobMovementManager::Teleport(Mob *who, float x, float y, float z, float hea
PushTeleportTo(ent.second, x, y, z, heading); PushTeleportTo(ent.second, x, y, z, heading);
} }
/**
* @param who
* @param x
* @param y
* @param z
* @param mode
*/
void MobMovementManager::NavigateTo(Mob *who, float x, float y, float z, MobMovementMode mode) void MobMovementManager::NavigateTo(Mob *who, float x, float y, float z, MobMovementMode mode)
{ {
if (IsPositionEqualWithinCertainZ(glm::vec3(x, y, z), glm::vec3(who->GetX(), who->GetY(), who->GetZ()), 6.0f)) { if (IsPositionEqualWithinCertainZ(glm::vec3(x, y, z), glm::vec3(who->GetX(), who->GetY(), who->GetZ()), 6.0f)) {
@@ -824,9 +783,6 @@ void MobMovementManager::NavigateTo(Mob *who, float x, float y, float z, MobMove
} }
} }
/**
* @param who
*/
void MobMovementManager::StopNavigation(Mob *who) void MobMovementManager::StopNavigation(Mob *who)
{ {
auto iter = _impl->Entries.find(who); auto iter = _impl->Entries.find(who);
@@ -852,16 +808,6 @@ void MobMovementManager::StopNavigation(Mob *who)
PushStopMoving(ent.second); PushStopMoving(ent.second);
} }
/**
* @param mob
* @param delta_x
* @param delta_y
* @param delta_z
* @param delta_heading
* @param anim
* @param range
* @param single_client
*/
void MobMovementManager::SendCommandToClients( void MobMovementManager::SendCommandToClients(
Mob *mob, Mob *mob,
float delta_x, float delta_x,
@@ -961,10 +907,6 @@ void MobMovementManager::SendCommandToClients(
} }
} }
/**
* @param in
* @return
*/
float MobMovementManager::FixHeading(float in) float MobMovementManager::FixHeading(float in)
{ {
auto h = in; auto h = in;
@@ -979,9 +921,6 @@ float MobMovementManager::FixHeading(float in)
return h; return h;
} }
/**
* @param client
*/
void MobMovementManager::DumpStats(Client *client) void MobMovementManager::DumpStats(Client *client)
{ {
auto current_time = static_cast<double>(Timer::GetCurrentTime()) / 1000.0; auto current_time = static_cast<double>(Timer::GetCurrentTime()) / 1000.0;
@@ -1062,13 +1001,6 @@ void MobMovementManager::FillCommandStruct(
} }
} }
/**
* @param who
* @param x
* @param y
* @param z
* @param mob_movement_mode
*/
void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode) void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode)
{ {
Mob *target=who->GetTarget(); Mob *target=who->GetTarget();
@@ -1114,13 +1046,6 @@ void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMove
} }
} }
/**
* @param who
* @param x
* @param y
* @param z
* @param mode
*/
void MobMovementManager::UpdatePathGround(Mob *who, float x, float y, float z, MobMovementMode mode) void MobMovementManager::UpdatePathGround(Mob *who, float x, float y, float z, MobMovementMode mode)
{ {
PathfinderOptions opts; PathfinderOptions opts;
@@ -1253,13 +1178,6 @@ void MobMovementManager::UpdatePathGround(Mob *who, float x, float y, float z, M
} }
} }
/**
* @param who
* @param x
* @param y
* @param z
* @param movement_mode
*/
void MobMovementManager::UpdatePathUnderwater(Mob *who, float x, float y, float z, MobMovementMode movement_mode) void MobMovementManager::UpdatePathUnderwater(Mob *who, float x, float y, float z, MobMovementMode movement_mode)
{ {
auto eiter = _impl->Entries.find(who); auto eiter = _impl->Entries.find(who);
@@ -1368,13 +1286,6 @@ void MobMovementManager::UpdatePathUnderwater(Mob *who, float x, float y, float
} }
} }
/**
* @param who
* @param x
* @param y
* @param z
* @param mode
*/
void MobMovementManager::UpdatePathBoat(Mob *who, float x, float y, float z, MobMovementMode mode) void MobMovementManager::UpdatePathBoat(Mob *who, float x, float y, float z, MobMovementMode mode)
{ {
auto eiter = _impl->Entries.find(who); auto eiter = _impl->Entries.find(who);
@@ -1386,48 +1297,21 @@ void MobMovementManager::UpdatePathBoat(Mob *who, float x, float y, float z, Mob
PushStopMoving(ent.second); PushStopMoving(ent.second);
} }
/**
* @param ent
* @param x
* @param y
* @param z
* @param heading
*/
void MobMovementManager::PushTeleportTo(MobMovementEntry &ent, float x, float y, float z, float heading) void MobMovementManager::PushTeleportTo(MobMovementEntry &ent, float x, float y, float z, float heading)
{ {
ent.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new TeleportToCommand(x, y, z, heading))); ent.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new TeleportToCommand(x, y, z, heading)));
} }
/**
* @param ent
* @param x
* @param y
* @param z
* @param mob_movement_mode
*/
void MobMovementManager::PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode) void MobMovementManager::PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode)
{ {
ent.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new MoveToCommand(x, y, z, mob_movement_mode))); ent.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new MoveToCommand(x, y, z, mob_movement_mode)));
} }
/**
* @param ent
* @param x
* @param y
* @param z
* @param mob_movement_mode
*/
void MobMovementManager::PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode) void MobMovementManager::PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode)
{ {
ent.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new SwimToCommand(x, y, z, mob_movement_mode))); ent.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new SwimToCommand(x, y, z, mob_movement_mode)));
} }
/**
* @param ent
* @param who
* @param to
* @param mob_movement_mode
*/
void MobMovementManager::PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mob_movement_mode) void MobMovementManager::PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mob_movement_mode)
{ {
auto from = FixHeading(who->GetHeading()); auto from = FixHeading(who->GetHeading());
@@ -1450,41 +1334,21 @@ void MobMovementManager::PushRotateTo(MobMovementEntry &ent, Mob *who, float to,
ent.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new RotateToCommand(to, diff > 0 ? 1.0 : -1.0, mob_movement_mode))); ent.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new RotateToCommand(to, diff > 0 ? 1.0 : -1.0, mob_movement_mode)));
} }
/**
* @param ent
* @param x
* @param y
* @param z
* @param mob_movement_mode
*/
void MobMovementManager::PushFlyTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode) void MobMovementManager::PushFlyTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mob_movement_mode)
{ {
ent.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new FlyToCommand(x, y, z, mob_movement_mode))); ent.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new FlyToCommand(x, y, z, mob_movement_mode)));
} }
/**
* @param mob_movement_entry
*/
void MobMovementManager::PushStopMoving(MobMovementEntry &mob_movement_entry) void MobMovementManager::PushStopMoving(MobMovementEntry &mob_movement_entry)
{ {
mob_movement_entry.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new StopMovingCommand())); mob_movement_entry.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new StopMovingCommand()));
} }
/**
* @param mob_movement_entry
*/
void MobMovementManager::PushEvadeCombat(MobMovementEntry &mob_movement_entry) void MobMovementManager::PushEvadeCombat(MobMovementEntry &mob_movement_entry)
{ {
mob_movement_entry.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new EvadeCombatCommand())); mob_movement_entry.Commands.emplace_back(std::unique_ptr<IMovementCommand>(new EvadeCombatCommand()));
} }
/**
* @param who
* @param x
* @param y
* @param z
* @param mob_movement_mode
*/
void MobMovementManager::HandleStuckBehavior(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode) void MobMovementManager::HandleStuckBehavior(Mob *who, float x, float y, float z, MobMovementMode mob_movement_mode)
{ {
LogDebug("Handle stuck behavior for {0} at ({1}, {2}, {3}) with movement_mode {4}", who->GetName(), x, y, z, static_cast<int>(mob_movement_mode)); LogDebug("Handle stuck behavior for {0} at ({1}, {2}, {3}) with movement_mode {4}", who->GetName(), x, y, z, static_cast<int>(mob_movement_mode));
+278 -60
View File
@@ -601,28 +601,8 @@ bool NPC::Process()
DepopSwarmPets(); DepopSwarmPets();
} }
if (mob_close_scan_timer.Check()) { ScanCloseMobProcess();
entity_list.ScanCloseMobs(close_mobs, this, IsMoving()); CheckScanCloseMobsMovingTimer();
}
const uint16 npc_mob_close_scan_timer_moving = 6000;
const uint16 npc_mob_close_scan_timer_idle = 60000;
if (mob_check_moving_timer.Check()) {
if (moving) {
if (mob_close_scan_timer.GetRemainingTime() > npc_mob_close_scan_timer_moving) {
LogAIScanCloseDetail("NPC [{}] Restarting with moving timer", GetCleanName());
mob_close_scan_timer.Disable();
mob_close_scan_timer.Start(npc_mob_close_scan_timer_moving);
mob_close_scan_timer.Trigger();
}
}
else if (mob_close_scan_timer.GetDuration() == npc_mob_close_scan_timer_moving) {
LogAIScanCloseDetail("NPC [{}] Restarting with idle timer", GetCleanName());
mob_close_scan_timer.Disable();
mob_close_scan_timer.Start(npc_mob_close_scan_timer_idle);
}
}
if (hp_regen_per_second > 0 && hp_regen_per_second_timer.Check()) { if (hp_regen_per_second > 0 && hp_regen_per_second_timer.Check()) {
if (GetHP() < GetMaxHP()) { if (GetHP() < GetMaxHP()) {
@@ -799,6 +779,11 @@ bool NPC::Process()
} }
} }
if (bot_attack_flag_timer.Check()) {
bot_attack_flag_timer.Disable();
ClearBotAttackFlags();
}
AI_Process(); AI_Process();
return true; return true;
@@ -848,23 +833,11 @@ void NPC::Depop(bool start_spawn_timer) {
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDespawn, emoteid); DoNPCEmote(EQ::constants::EmoteEventTypes::OnDespawn, emoteid);
} }
if (IsNPC()) { parse->EventBotMercNPC(EVENT_DESPAWN, this, nullptr);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DESPAWN)) {
parse->EventNPC(EVENT_DESPAWN, this, nullptr, "", 0);
}
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DESPAWN_ZONE)) { if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DESPAWN_ZONE)) {
DispatchZoneControllerEvent(EVENT_DESPAWN_ZONE, this, "", 0, nullptr); DispatchZoneControllerEvent(EVENT_DESPAWN_ZONE, this, "", 0, nullptr);
} }
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_DESPAWN)) {
parse->EventBot(EVENT_DESPAWN, CastToBot(), nullptr, "", 0);
}
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DESPAWN_ZONE)) {
DispatchZoneControllerEvent(EVENT_DESPAWN_ZONE, this, "", 0, nullptr);
}
}
p_depop = true; p_depop = true;
if (respawn2) { if (respawn2) {
@@ -2932,31 +2905,252 @@ void NPC::DoNPCEmote(uint8 event_, uint32 emote_id, Mob* t)
bool NPC::CanTalk() bool NPC::CanTalk()
{ {
//Races that should be able to talk. (Races up to Titanium) switch (GetRace()) {
case Race::Human:
uint16 TalkRace[473] = case Race::Barbarian:
{1,2,3,4,5,6,7,8,9,10,11,12,0,0,15,16,0,18,19,20,0,0,23,0,25,0,0,0,0,0,0, case Race::Erudite:
32,0,0,0,0,0,0,39,40,0,0,0,44,0,0,0,0,49,0,51,0,53,54,55,56,57,58,0,0,0, case Race::WoodElf:
62,0,64,65,66,67,0,0,70,71,0,0,0,0,0,77,78,79,0,81,82,0,0,0,86,0,0,0,90, case Race::HighElf:
0,92,93,94,95,0,0,98,99,0,101,0,103,0,0,0,0,0,0,110,111,112,0,0,0,0,0,0, case Race::DarkElf:
0,0,0,0,123,0,0,126,0,128,0,130,131,0,0,0,0,136,137,0,139,140,0,0,0,144, case Race::HalfElf:
0,0,0,0,0,150,151,152,153,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, case Race::Dwarf:
0,0,0,0,0,0,183,184,0,0,187,188,189,0,0,0,0,0,195,196,0,198,0,0,0,202,0, case Race::Troll:
0,205,0,0,208,0,0,0,0,0,0,0,0,217,0,219,0,0,0,0,0,0,226,0,0,229,230,0,0, case Race::Ogre:
0,0,235,236,0,238,239,240,241,242,243,244,0,246,247,0,0,0,251,0,0,254,255, case Race::Halfling:
256,257,0,0,0,0,0,0,0,0,266,267,0,0,270,271,0,0,0,0,0,277,278,0,0,0,0,283, case Race::Gnome:
284,0,286,0,288,289,290,0,0,0,0,295,296,297,298,299,300,0,0,0,304,0,0,0,0, case Race::Werewolf:
0,0,0,0,0,0,0,0,0,0,0,320,0,322,323,324,325,0,0,0,0,330,331,332,333,334,335, case Race::Brownie:
336,337,338,339,340,341,342,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,359,360,361,362, case Race::Centaur:
0,364,365,366,0,368,369,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,385,386,0,0,0,0,0,392, case Race::Giant:
393,394,395,396,397,398,0,400,402,0,0,0,0,406,0,408,0,0,411,0,413,0,0,0,417, case Race::Trakanon:
0,0,420,0,0,0,0,425,0,0,0,0,0,0,0,433,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, case Race::VenrilSathir:
0,0,0,0,0,458,0,0,0,0,0,0,0,0,467,0,0,470,0,0,473}; case Race::Kerran:
case Race::Fairy:
if (TalkRace[GetRace() - 1] > 0) case Race::Ghost:
case Race::Gnoll:
case Race::Goblin:
case Race::FreeportGuard:
case Race::LavaDragon:
case Race::LizardMan:
case Race::Minotaur:
case Race::Orc:
case Race::HumanBeggar:
case Race::Pixie:
case Race::Drachnid:
case Race::SolusekRo:
case Race::Tunare:
case Race::Treant:
case Race::Vampire:
case Race::StatueOfRallosZek:
case Race::HighpassCitizen:
case Race::Zombie:
case Race::QeynosCitizen:
case Race::NeriakCitizen:
case Race::EruditeCitizen:
case Race::Bixie:
case Race::RivervaleCitizen:
case Race::Scarecrow:
case Race::Sphinx:
case Race::HalasCitizen:
case Race::GrobbCitizen:
case Race::OggokCitizen:
case Race::KaladimCitizen:
case Race::CazicThule:
case Race::ElfVampire:
case Race::Denizen:
case Race::Efreeti:
case Race::PhinigelAutropos:
case Race::Mermaid:
case Race::Harpy:
case Race::Fayguard:
case Race::Innoruuk:
case Race::Djinn:
case Race::InvisibleMan:
case Race::Iksar:
case Race::VahShir:
case Race::Sarnak:
case Race::Xalgoz:
case Race::Yeti:
case Race::IksarCitizen:
case Race::ForestGiant:
case Race::Burynai:
case Race::Erollisi:
case Race::Tribunal:
case Race::Bertoxxulous:
case Race::Bristlebane:
case Race::Ratman:
case Race::Coldain:
case Race::VeliousDragon:
case Race::Siren:
case Race::FrostGiant:
case Race::StormGiant:
case Race::BlackAndWhiteDragon:
case Race::GhostDragon:
case Race::PrismaticDragon:
case Race::Grimling:
case Race::KhatiSha:
case Race::Vampire2:
case Race::Shissar:
case Race::VampireVolatalis:
case Race::Shadel:
case Race::Netherbian:
case Race::Akhevan:
case Race::Wretch:
case Race::LordInquisitorSeru:
case Race::VahShirKing:
case Race::VahShirGuard:
case Race::TeleportMan:
case Race::Werewolf2:
case Race::Nymph:
case Race::Dryad:
case Race::Treant2:
case Race::TarewMarr:
case Race::SolusekRo2:
case Race::GuardOfJustice:
case Race::SolusekRoGuard:
case Race::BertoxxulousNew:
case Race::TribunalNew:
case Race::TerrisThule:
case Race::KnightOfPestilence:
case Race::Lepertoloth:
case Race::Pusling:
case Race::WaterMephit:
case Race::NightmareGoblin:
case Race::Karana:
case Race::Saryrn:
case Race::FenninRo:
case Race::SoulDevourer:
case Race::NewRallosZek:
case Race::VallonZek:
case Race::TallonZek:
case Race::AirMephit:
case Race::EarthMephit:
case Race::FireMephit:
case Race::NightmareMephit:
case Race::Zebuxoruk:
case Race::MithanielMarr:
case Race::UndeadKnight:
case Race::Rathe:
case Race::Xegony:
case Race::Fiend:
case Race::Quarm:
case Race::Efreeti2:
case Race::Valorian2:
case Race::AnimatedArmor:
case Race::UndeadFootman:
case Race::RallosOgre:
case Race::Froglok2:
case Race::TrollCrewMember:
case Race::PirateDeckhand:
case Race::BrokenSkullPirate:
case Race::PirateGhost:
case Race::OneArmedPirate:
case Race::SpiritmasterNadox:
case Race::BrokenSkullTaskmaster:
case Race::GnomePirate:
case Race::DarkElfPirate:
case Race::OgrePirate:
case Race::HumanPirate:
case Race::EruditePirate:
case Race::UndeadVampire:
case Race::Vampire3:
case Race::RujarkianOrc:
case Race::BoneGolem:
case Race::SandElf:
case Race::MasterVampire:
case Race::MasterOrc:
case Race::Mummy:
case Race::NewGoblin:
case Race::Nihil:
case Race::Trusik:
case Race::Ukun:
case Race::Ixt:
case Race::Ikaav:
case Race::Aneuk:
case Race::Kyv:
case Race::Noc:
case Race::Ratuk:
case Race::Huvul:
case Race::Mastruq:
case Race::MataMuram:
case Race::Succubus:
case Race::Pyrilen:
case Race::Dragorn:
case Race::Gelidran:
case Race::Minotaur2:
case Race::CrystalShard:
case Race::Goblin2:
case Race::Giant2:
case Race::Orc2:
case Race::Werewolf3:
case Race::Shiliskin:
case Race::Minotaur3:
case Race::Fairy2:
case Race::Bolvirk:
case Race::Elddar:
case Race::ForestGiant2:
case Race::BoneGolem2:
case Race::Scrykin:
case Race::Treant3:
case Race::Vampire4:
case Race::AyonaeRo:
case Race::SullonZek:
case Race::Bixie2:
case Race::Centaur2:
case Race::Drakkin:
case Race::Giant3:
case Race::Gnoll2:
case Race::GiantShade:
case Race::Harpy2:
case Race::Satyr:
case Race::Dynleth:
case Race::Kedge:
case Race::Kerran2:
case Race::Shissar2:
case Race::Siren2:
case Race::Sphinx2:
case Race::Human2:
case Race::Brownie2:
case Race::Exoskeleton:
case Race::Minotaur4:
case Race::Scarecrow2:
case Race::Wereorc:
case Race::ElvenGhost:
case Race::HumanGhost:
case Race::Burynai2:
case Race::Dracolich:
case Race::IksarGhost:
case Race::Mephit:
case Race::Sarnak2:
case Race::Gnoll3:
case Race::GodOfDiscord:
case Race::Ogre2:
case Race::Giant4:
case Race::Apexus:
case Race::Bellikos:
case Race::BrellsFirstCreation:
case Race::Brell:
case Race::Coldain2:
case Race::Coldain3:
case Race::Telmira:
case Race::MorellThule:
case Race::Amygdalan:
case Race::Sandman:
case Race::RoyalGuard:
case Race::CazicThule2:
case Race::Erudite2:
case Race::Alaran:
case Race::AlaranGhost:
case Race::Ratman2:
case Race::Akheva:
case Race::Luclin:
case Race::Luclin2:
case Race::Luclin3:
case Race::Luclin4:
return true; return true;
default:
return false; return false;
}
} }
//this is called with 'this' as the mob being looked at, and //this is called with 'this' as the mob being looked at, and
@@ -3322,7 +3516,7 @@ bool NPC::AICheckCloseBeneficialSpells(
/** /**
* Check through close range mobs * Check through close range mobs
*/ */
for (auto & close_mob : entity_list.GetCloseMobList(caster, cast_range)) { for (auto & close_mob : caster->GetCloseMobList(cast_range)) {
Mob *mob = close_mob.second; Mob *mob = close_mob.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -3401,7 +3595,7 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker)
GetID() GetID()
); );
for (auto &close_mob : entity_list.GetCloseMobList(sender)) { for (auto &close_mob: sender->GetCloseMobList()) {
Mob *mob = close_mob.second; Mob *mob = close_mob.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -4014,3 +4208,27 @@ void NPC::DescribeSpecialAbilities(Client* c)
c->Message(Chat::White, e.c_str()); c->Message(Chat::White, e.c_str());
} }
} }
void NPC::DoNpcToNpcAggroScan()
{
for (auto &close_mob : GetCloseMobList(GetAggroRange())) {
Mob *mob = close_mob.second;
if (!mob) {
continue;
}
if (!mob->IsNPC()) {
continue;
}
if (CheckWillAggro(mob)) {
AddToHateList(mob);
}
}
AI_scan_area_timer->Disable();
AI_scan_area_timer->Start(
RandomTimer(RuleI(NPC, NPCToNPCAggroTimerMin), RuleI(NPC, NPCToNPCAggroTimerMax)),
false
);
}
+1
View File
@@ -557,6 +557,7 @@ public:
bool CanPathTo(float x, float y, float z); bool CanPathTo(float x, float y, float z);
void DoNpcToNpcAggroScan();
protected: protected:
void HandleRoambox(); void HandleRoambox();
+12
View File
@@ -3031,6 +3031,11 @@ void Perl_Client_GrantAllAAPoints(Client* self, uint8 unlock_level)
self->GrantAllAAPoints(unlock_level); self->GrantAllAAPoints(unlock_level);
} }
void Perl_Client_GrantAllAAPoints(Client* self, uint8 unlock_level, bool skip_grant_only)
{
self->GrantAllAAPoints(unlock_level, skip_grant_only);
}
void Perl_Client_AddEbonCrystals(Client* self, uint32 amount) void Perl_Client_AddEbonCrystals(Client* self, uint32 amount)
{ {
self->AddEbonCrystals(amount); self->AddEbonCrystals(amount);
@@ -3202,6 +3207,11 @@ void Perl_Client_AreaTaunt(Client* self, float range, int bonus_hate)
entity_list.AETaunt(self, range, bonus_hate); entity_list.AETaunt(self, range, bonus_hate);
} }
Merc* Perl_Client_GetMerc(Client* self)
{
return self->GetMerc();
}
void perl_register_client() void perl_register_client()
{ {
perl::interpreter perl(PERL_GET_THX); perl::interpreter perl(PERL_GET_THX);
@@ -3433,6 +3443,7 @@ void perl_register_client()
package.add("GetLearnedDisciplines", &Perl_Client_GetLearnedDisciplines); package.add("GetLearnedDisciplines", &Perl_Client_GetLearnedDisciplines);
package.add("GetLockoutExpeditionUUID", &Perl_Client_GetLockoutExpeditionUUID); package.add("GetLockoutExpeditionUUID", &Perl_Client_GetLockoutExpeditionUUID);
package.add("GetMaxEndurance", &Perl_Client_GetMaxEndurance); package.add("GetMaxEndurance", &Perl_Client_GetMaxEndurance);
package.add("GetMerc", &Perl_Client_GetMerc);
package.add("GetMemmedSpells", &Perl_Client_GetMemmedSpells); package.add("GetMemmedSpells", &Perl_Client_GetMemmedSpells);
package.add("GetModCharacterFactionLevel", &Perl_Client_GetModCharacterFactionLevel); package.add("GetModCharacterFactionLevel", &Perl_Client_GetModCharacterFactionLevel);
package.add("GetMoney", &Perl_Client_GetMoney); package.add("GetMoney", &Perl_Client_GetMoney);
@@ -3471,6 +3482,7 @@ void perl_register_client()
package.add("GoFish", &Perl_Client_GoFish); package.add("GoFish", &Perl_Client_GoFish);
package.add("GrantAllAAPoints", (void(*)(Client*))&Perl_Client_GrantAllAAPoints); package.add("GrantAllAAPoints", (void(*)(Client*))&Perl_Client_GrantAllAAPoints);
package.add("GrantAllAAPoints", (void(*)(Client*, uint8))&Perl_Client_GrantAllAAPoints); package.add("GrantAllAAPoints", (void(*)(Client*, uint8))&Perl_Client_GrantAllAAPoints);
package.add("GrantAllAAPoints", (void(*)(Client*, uint8, bool))&Perl_Client_GrantAllAAPoints);
package.add("GrantAlternateAdvancementAbility", (bool(*)(Client*, int, int))&Perl_Client_GrantAlternateAdvancementAbility); package.add("GrantAlternateAdvancementAbility", (bool(*)(Client*, int, int))&Perl_Client_GrantAlternateAdvancementAbility);
package.add("GrantAlternateAdvancementAbility", (bool(*)(Client*, int, int, bool))&Perl_Client_GrantAlternateAdvancementAbility); package.add("GrantAlternateAdvancementAbility", (bool(*)(Client*, int, int, bool))&Perl_Client_GrantAlternateAdvancementAbility);
package.add("GuildID", &Perl_Client_GuildID); package.add("GuildID", &Perl_Client_GuildID);
+40
View File
@@ -739,6 +739,44 @@ void Perl_EntityList_MassGroupBuff(EntityList* self, Mob* caster, Mob* center, u
self->MassGroupBuff(caster, center, spell_id, affect_caster); self->MassGroupBuff(caster, center, spell_id, affect_caster);
} }
perl::array Perl_EntityList_GetNPCsByExcludedIDs(EntityList* self, perl::array npc_ids)
{
std::vector<uint32> ids;
for (int i = 0; i < npc_ids.size(); i++) {
ids.emplace_back(npc_ids[i]);
}
const auto& l = self->GetExcludedNPCsByIDs(ids);
perl::array npcs;
for (const auto& e : l) {
npcs.push_back(e);
}
return npcs;
}
perl::array Perl_EntityList_GetNPCsByIDs(EntityList* self, perl::array npc_ids)
{
std::vector<uint32> ids;
for (int i = 0; i < npc_ids.size(); i++) {
ids.emplace_back(npc_ids[i]);
}
const auto& l = self->GetNPCsByIDs(ids);
perl::array npcs;
for (const auto& e : l) {
npcs.push_back(e);
}
return npcs;
}
void perl_register_entitylist() void perl_register_entitylist()
{ {
perl::interpreter perl(PERL_GET_THX); perl::interpreter perl(PERL_GET_THX);
@@ -804,6 +842,8 @@ void perl_register_entitylist()
package.add("GetNPCByNPCTypeID", &Perl_EntityList_GetNPCByNPCTypeID); package.add("GetNPCByNPCTypeID", &Perl_EntityList_GetNPCByNPCTypeID);
package.add("GetNPCBySpawnID", &Perl_EntityList_GetNPCBySpawnID); package.add("GetNPCBySpawnID", &Perl_EntityList_GetNPCBySpawnID);
package.add("GetNPCList", &Perl_EntityList_GetNPCList); package.add("GetNPCList", &Perl_EntityList_GetNPCList);
package.add("GetNPCsByExcludedIDs", &Perl_EntityList_GetNPCsByExcludedIDs);
package.add("GetNPCsByIDs", &Perl_EntityList_GetNPCsByIDs);
package.add("GetObjectByDBID", &Perl_EntityList_GetObjectByDBID); package.add("GetObjectByDBID", &Perl_EntityList_GetObjectByDBID);
package.add("GetObjectByID", &Perl_EntityList_GetObjectByID); package.add("GetObjectByID", &Perl_EntityList_GetObjectByID);
package.add("GetObjectList", &Perl_EntityList_GetObjectList); package.add("GetObjectList", &Perl_EntityList_GetObjectList);
+195
View File
@@ -0,0 +1,195 @@
#include "../common/features.h"
#ifdef EMBPERL_XS_CLASSES
#include "../common/global_define.h"
#include "embperl.h"
#include "merc.h"
uint32 Perl_Merc_GetCostFormula(Merc* self)
{
return self->GetCostFormula();
}
Group* Perl_Merc_GetGroup(Merc* self)
{
return self->GetGroup();
}
int Perl_Merc_GetHatedCount(Merc* self)
{
return self->GetHatedCount();
}
float Perl_Merc_GetMaxMeleeRangeToTarget(Merc* self, Mob* target)
{
return self->GetMaxMeleeRangeToTarget(target);
}
uint32 Perl_Merc_GetMercenaryCharacterID(Merc* self)
{
return self->GetMercenaryCharacterID();
}
uint32 Perl_Merc_GetMercenaryID(Merc* self)
{
return self->GetMercenaryID();
}
uint32 Perl_Merc_GetMercenaryNameType(Merc* self)
{
return self->GetMercNameType();
}
Client* Perl_Merc_GetMercenaryOwner(Merc* self)
{
return self->GetMercenaryOwner();
}
uint32 Perl_Merc_GetMercenarySubtype(Merc* self)
{
return self->GetMercenarySubType();
}
uint32 Perl_Merc_GetMercenaryTemplateID(Merc* self)
{
return self->GetMercenaryTemplateID();
}
uint32 Perl_Merc_GetMercenaryType(Merc* self)
{
return self->GetMercenaryType();
}
Mob* Perl_Merc_GetOwner(Merc* self)
{
return self->GetOwner();
}
Mob* Perl_Merc_GetOwnerOrSelf(Merc* self)
{
return self->GetOwnerOrSelf();
}
uint8 Perl_Merc_GetProficiencyID(Merc* self)
{
return self->GetProficiencyID();
}
uint8 Perl_Merc_GetStance(Merc* self)
{
return self->GetStance();
}
uint8 Perl_Merc_GetTierID(Merc* self)
{
return self->GetTierID();
}
bool Perl_Merc_HasOrMayGetAggro(Merc* self)
{
return self->HasOrMayGetAggro();
}
bool Perl_Merc_IsMercenaryCaster(Merc* self)
{
return self->IsMercCaster();
}
bool Perl_Merc_IsMercenaryCasterCombatRange(Merc* self, Mob* target)
{
return self->IsMercCasterCombatRange(target);
}
bool Perl_Merc_IsSitting(Merc* self)
{
return self->IsSitting();
}
bool Perl_Merc_IsStanding(Merc* self)
{
return self->IsStanding();
}
void Perl_Merc_ScaleStats(Merc* self, int scale_percentage)
{
self->ScaleStats(scale_percentage);
}
void Perl_Merc_ScaleStats(Merc* self, int scale_percentage, bool set_to_max)
{
self->ScaleStats(scale_percentage, set_to_max);
}
void Perl_Merc_SendPayload(Merc* self, int payload_id, std::string payload_value)
{
self->SendPayload(payload_id, payload_value);
}
void Perl_Merc_SetTarget(Merc* self, Mob* target)
{
self->SetTarget(target);
}
void Perl_Merc_Signal(Merc* self, int signal_id)
{
self->Signal(signal_id);
}
void Perl_Merc_Sit(Merc* self)
{
self->Sit();
}
void Perl_Merc_Stand(Merc* self)
{
self->Stand();
}
bool Perl_Merc_Suspend(Merc* self)
{
return self->Suspend();
}
bool Perl_Merc_UseDiscipline(Merc* self, uint16 spell_id, uint16 target_id)
{
return self->UseDiscipline(spell_id, target_id);
}
void perl_register_merc()
{
perl::interpreter state(PERL_GET_THX);
auto package = state.new_class<Merc>("Merc");
package.add_base_class("NPC");
package.add("GetCostFormula", &Perl_Merc_GetCostFormula);
package.add("GetGroup", &Perl_Merc_GetGroup);
package.add("GetHatedCount", &Perl_Merc_GetHatedCount);
package.add("GetMaxMeleeRangeToTarget", &Perl_Merc_GetMaxMeleeRangeToTarget);
package.add("GetMercenaryCharacterID", &Perl_Merc_GetMercenaryCharacterID);
package.add("GetMercenaryID", &Perl_Merc_GetMercenaryID);
package.add("GetMercenaryNameType", &Perl_Merc_GetMercenaryNameType);
package.add("GetMercenaryOwner", &Perl_Merc_GetMercenaryOwner);
package.add("GetMercenarySubtype", &Perl_Merc_GetMercenarySubtype);
package.add("GetMercenaryTemplateID", &Perl_Merc_GetMercenaryTemplateID);
package.add("GetMercenaryType", &Perl_Merc_GetMercenaryType);
package.add("GetOwner", &Perl_Merc_GetOwner);
package.add("GetOwnerOrSelf", &Perl_Merc_GetOwnerOrSelf);
package.add("GetProficiencyID", &Perl_Merc_GetProficiencyID);
package.add("GetStance", &Perl_Merc_GetStance);
package.add("GetTierID", &Perl_Merc_GetTierID);
package.add("HasOrMayGetAggro", &Perl_Merc_HasOrMayGetAggro);
package.add("IsMercenaryCaster", &Perl_Merc_IsMercenaryCaster);
package.add("IsMercenaryCasterCombatRange", &Perl_Merc_IsMercenaryCasterCombatRange);
package.add("IsSitting", &Perl_Merc_IsSitting);
package.add("IsStanding", &Perl_Merc_IsStanding);
package.add("ScaleStats", (void(*)(Merc*, int))&Perl_Merc_ScaleStats);
package.add("ScaleStats", (void(*)(Merc*, int, bool))&Perl_Merc_ScaleStats);
package.add("SendPayload", &Perl_Merc_SendPayload);
package.add("SetTarget", &Perl_Merc_SetTarget);
package.add("Signal", &Perl_Merc_Signal);
package.add("Sit", &Perl_Merc_Sit);
package.add("Stand", &Perl_Merc_Stand);
package.add("Suspend", &Perl_Merc_Suspend);
package.add("UseDiscipline", &Perl_Merc_UseDiscipline);
}
#endif //EMBPERL_XS_CLASSES
+30
View File
@@ -3543,6 +3543,31 @@ void Perl_Mob_MassGroupBuff(Mob* self, Mob* center, uint16 spell_id, bool affect
entity_list.MassGroupBuff(self, center, spell_id, affect_caster); entity_list.MassGroupBuff(self, center, spell_id, affect_caster);
} }
void Perl_Mob_BuffFadeBeneficial(Mob* self)
{
self->BuffFadeBeneficial();
}
void Perl_Mob_BuffFadeDetrimental(Mob* self)
{
self->BuffFadeDetrimental();
}
void Perl_Mob_BuffFadeDetrimentalByCaster(Mob* self, Mob* caster)
{
self->BuffFadeDetrimentalByCaster(caster);
}
void Perl_Mob_BuffFadeNonPersistDeath(Mob* self)
{
self->BuffFadeNonPersistDeath();
}
void Perl_Mob_BuffFadeSongs(Mob* self)
{
self->BuffFadeSongs();
}
void perl_register_mob() void perl_register_mob()
{ {
perl::interpreter perl(PERL_GET_THX); perl::interpreter perl(PERL_GET_THX);
@@ -3578,11 +3603,16 @@ void perl_register_mob()
package.add("BuffCount", (uint32(*)(Mob*, bool))&Perl_Mob_BuffCount); package.add("BuffCount", (uint32(*)(Mob*, bool))&Perl_Mob_BuffCount);
package.add("BuffCount", (uint32(*)(Mob*, bool, bool))&Perl_Mob_BuffCount); package.add("BuffCount", (uint32(*)(Mob*, bool, bool))&Perl_Mob_BuffCount);
package.add("BuffFadeAll", &Perl_Mob_BuffFadeAll); package.add("BuffFadeAll", &Perl_Mob_BuffFadeAll);
package.add("BuffFadeBeneficial", &Perl_Mob_BuffFadeBeneficial);
package.add("BuffFadeByEffect", (void(*)(Mob*, int))&Perl_Mob_BuffFadeByEffect); package.add("BuffFadeByEffect", (void(*)(Mob*, int))&Perl_Mob_BuffFadeByEffect);
package.add("BuffFadeByEffect", (void(*)(Mob*, int, int))&Perl_Mob_BuffFadeByEffect); package.add("BuffFadeByEffect", (void(*)(Mob*, int, int))&Perl_Mob_BuffFadeByEffect);
package.add("BuffFadeBySlot", (void(*)(Mob*, int))&Perl_Mob_BuffFadeBySlot); package.add("BuffFadeBySlot", (void(*)(Mob*, int))&Perl_Mob_BuffFadeBySlot);
package.add("BuffFadeBySlot", (void(*)(Mob*, int, bool))&Perl_Mob_BuffFadeBySlot); package.add("BuffFadeBySlot", (void(*)(Mob*, int, bool))&Perl_Mob_BuffFadeBySlot);
package.add("BuffFadeBySpellID", &Perl_Mob_BuffFadeBySpellID); package.add("BuffFadeBySpellID", &Perl_Mob_BuffFadeBySpellID);
package.add("BuffFadeDetrimental", &Perl_Mob_BuffFadeDetrimental);
package.add("BuffFadeDetrimentalByCaster", &Perl_Mob_BuffFadeDetrimentalByCaster);
package.add("BuffFadeNonPersistDeath", &Perl_Mob_BuffFadeNonPersistDeath);
package.add("BuffFadeSongs", &Perl_Mob_BuffFadeSongs);
package.add("CalculateDistance", (float(*)(Mob*, float, float, float))&Perl_Mob_CalculateDistance); package.add("CalculateDistance", (float(*)(Mob*, float, float, float))&Perl_Mob_CalculateDistance);
package.add("CalculateDistance", (float(*)(Mob*, Mob*))&Perl_Mob_CalculateDistance); package.add("CalculateDistance", (float(*)(Mob*, Mob*))&Perl_Mob_CalculateDistance);
package.add("CalculateHeadingToTarget", &Perl_Mob_CalculateHeadingToTarget); package.add("CalculateHeadingToTarget", &Perl_Mob_CalculateHeadingToTarget);
+48
View File
@@ -139,6 +139,30 @@ public:
return 0; return 0;
} }
virtual int EventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return 0;
}
virtual int EventGlobalMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return 0;
}
virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id) virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id)
{ {
return false; return false;
@@ -189,6 +213,16 @@ public:
return false; return false;
} }
virtual bool MercHasQuestSub(QuestEventID event_id)
{
return false;
}
virtual bool GlobalMercHasQuestSub(QuestEventID event_id)
{
return false;
}
virtual void LoadNPCScript(std::string filename, int npc_id) { } virtual void LoadNPCScript(std::string filename, int npc_id) { }
virtual void LoadGlobalNPCScript(std::string filename) { } virtual void LoadGlobalNPCScript(std::string filename) { }
virtual void LoadPlayerScript(std::string filename) { } virtual void LoadPlayerScript(std::string filename) { }
@@ -198,6 +232,8 @@ public:
virtual void LoadEncounterScript(std::string filename, std::string encounter_name) { } virtual void LoadEncounterScript(std::string filename, std::string encounter_name) { }
virtual void LoadBotScript(std::string filename) { } virtual void LoadBotScript(std::string filename) { }
virtual void LoadGlobalBotScript(std::string filename) { } virtual void LoadGlobalBotScript(std::string filename) { }
virtual void LoadMercScript(std::string filename) { }
virtual void LoadGlobalMercScript(std::string filename) { }
virtual int DispatchEventNPC( virtual int DispatchEventNPC(
QuestEventID event_id, QuestEventID event_id,
@@ -260,6 +296,18 @@ public:
return 0; return 0;
} }
virtual int DispatchEventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return 0;
}
virtual void AddVar(std::string name, std::string val) { } virtual void AddVar(std::string name, std::string val) { }
virtual std::string GetVar(std::string name) virtual std::string GetVar(std::string name)
{ {
+301
View File
@@ -47,6 +47,8 @@ QuestParserCollection::QuestParserCollection()
_global_npc_quest_status = QuestUnloaded; _global_npc_quest_status = QuestUnloaded;
_bot_quest_status = QuestUnloaded; _bot_quest_status = QuestUnloaded;
_global_bot_quest_status = QuestUnloaded; _global_bot_quest_status = QuestUnloaded;
_merc_quest_status = QuestUnloaded;
_global_merc_quest_status = QuestUnloaded;
} }
QuestParserCollection::~QuestParserCollection() { } QuestParserCollection::~QuestParserCollection() { }
@@ -94,6 +96,8 @@ void QuestParserCollection::ReloadQuests(bool reset_timers)
_global_npc_quest_status = QuestUnloaded; _global_npc_quest_status = QuestUnloaded;
_bot_quest_status = QuestUnloaded; _bot_quest_status = QuestUnloaded;
_global_bot_quest_status = QuestUnloaded; _global_bot_quest_status = QuestUnloaded;
_merc_quest_status = QuestUnloaded;
_global_merc_quest_status = QuestUnloaded;
_spell_quest_status.clear(); _spell_quest_status.clear();
_item_quest_status.clear(); _item_quest_status.clear();
@@ -379,6 +383,49 @@ bool QuestParserCollection::BotHasQuestSub(QuestEventID event_id)
return BotHasQuestSubLocal(event_id) || BotHasQuestSubGlobal(event_id); return BotHasQuestSubLocal(event_id) || BotHasQuestSubGlobal(event_id);
} }
bool QuestParserCollection::MercHasQuestSubLocal(QuestEventID event_id)
{
if (_merc_quest_status == QuestUnloaded) {
std::string filename;
auto qi = GetQIByMercQuest(filename);
if (qi) {
_merc_quest_status = qi->GetIdentifier();
qi->LoadMercScript(filename);
return qi->MercHasQuestSub(event_id);
}
} else if (_merc_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_merc_quest_status);
return iter->second->MercHasQuestSub(event_id);
}
return false;
}
bool QuestParserCollection::MercHasQuestSubGlobal(QuestEventID event_id)
{
if (_global_merc_quest_status == QuestUnloaded) {
std::string filename;
auto qi = GetQIByGlobalMercQuest(filename);
if (qi) {
_global_merc_quest_status = qi->GetIdentifier();
qi->LoadGlobalMercScript(filename);
return qi->GlobalMercHasQuestSub(event_id);
}
} else if (_global_merc_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_global_merc_quest_status);
return iter->second->GlobalMercHasQuestSub(event_id);
}
return false;
}
bool QuestParserCollection::MercHasQuestSub(QuestEventID event_id)
{
return MercHasQuestSubLocal(event_id) || MercHasQuestSubGlobal(event_id);
}
int QuestParserCollection::EventNPC( int QuestParserCollection::EventNPC(
QuestEventID event_id, QuestEventID event_id,
NPC* npc, NPC* npc,
@@ -793,6 +840,86 @@ int QuestParserCollection::EventBotGlobal(
return 0; return 0;
} }
int QuestParserCollection::EventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
const int local_return = EventMercLocal(event_id, merc, init, data, extra_data, extra_pointers);
const int global_return = EventMercGlobal(event_id, merc, init, data, extra_data, extra_pointers);
const int default_return = DispatchEventMerc(event_id, merc, init, data, extra_data, extra_pointers);
if (local_return != 0) {
return local_return;
} else if (global_return != 0) {
return global_return;
} else if (default_return != 0) {
return default_return;
}
return 0;
}
int QuestParserCollection::EventMercLocal(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (_merc_quest_status == QuestUnloaded) {
std::string filename;
auto qi = GetQIByMercQuest(filename);
if (qi) {
_merc_quest_status = qi->GetIdentifier();
qi->LoadMercScript(filename);
return qi->EventMerc(event_id, merc, init, data, extra_data, extra_pointers);
}
} else {
if (_merc_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_merc_quest_status);
return iter->second->EventMerc(event_id, merc, init, data, extra_data, extra_pointers);
}
}
return 0;
}
int QuestParserCollection::EventMercGlobal(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (_global_merc_quest_status == QuestUnloaded) {
std::string filename;
auto qi = GetQIByGlobalMercQuest(filename);
if (qi) {
_global_merc_quest_status = qi->GetIdentifier();
qi->LoadGlobalMercScript(filename);
return qi->EventGlobalMerc(event_id, merc, init, data, extra_data, extra_pointers);
}
} else {
if (_global_merc_quest_status != QuestFailedToLoad) {
auto iter = _interfaces.find(_global_merc_quest_status);
return iter->second->EventGlobalMerc(event_id, merc, init, data, extra_data, extra_pointers);
}
}
return 0;
}
QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::string& filename) QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::string& filename)
{ {
if (!zone) { if (!zone) {
@@ -1188,6 +1315,81 @@ QuestInterface* QuestParserCollection::GetQIByGlobalBotQuest(std::string& filena
return nullptr; return nullptr;
} }
QuestInterface* QuestParserCollection::GetQIByMercQuest(std::string& filename)
{
if (!zone || !zone->IsLoaded()) {
return nullptr;
}
const std::string& global_path = fmt::format(
"{}/{}",
path.GetQuestsPath(),
QUEST_GLOBAL_DIRECTORY
);
const std::string& zone_path = fmt::format(
"{}/{}",
path.GetQuestsPath(),
zone->GetShortName()
);
const std::string& zone_versioned_path = fmt::format(
"{}/{}/v{}",
path.GetQuestsPath(),
zone->GetShortName(),
zone->GetInstanceVersion()
);
std::vector<std::string> file_names = {
fmt::format("{}/merc", zone_versioned_path), // Local versioned by Instance Version ./quests/zone/v0/merc.ext
fmt::format("{}/merc_v{}", zone_path, zone->GetInstanceVersion()), // Local by Instance Version
fmt::format("{}/merc", zone_path), // Local
fmt::format("{}/merc", global_path) // Global
};
std::string file_name;
for (auto & file : file_names) {
for (auto* e: _load_precedence) {
file_name = fmt::format(
"{}.{}",
file,
_extensions.find(e->GetIdentifier())->second
);
if (File::Exists(file_name)) {
filename = file_name;
return e;
}
}
}
return nullptr;
}
QuestInterface* QuestParserCollection::GetQIByGlobalMercQuest(std::string& filename)
{
if (!zone) {
return nullptr;
}
std::string file_name;
for (auto* e: _load_precedence) {
file_name = fmt::format(
"{}/{}/global_merc.{}",
path.GetQuestsPath(),
QUEST_GLOBAL_DIRECTORY,
_extensions.find(e->GetIdentifier())->second
);
if (File::Exists(file_name)) {
filename = file_name;
return e;
}
}
return nullptr;
}
void QuestParserCollection::GetErrors(std::list<std::string>& quest_errors) void QuestParserCollection::GetErrors(std::list<std::string>& quest_errors)
{ {
quest_errors.clear(); quest_errors.clear();
@@ -1303,6 +1505,27 @@ int QuestParserCollection::DispatchEventBot(
return ret; return ret;
} }
int QuestParserCollection::DispatchEventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
int ret = 0;
for (const auto& e: _load_precedence) {
int i = e->DispatchEventMerc(event_id, merc, init, data, extra_data, extra_pointers);
if (i != 0) {
ret = i;
}
}
return ret;
}
void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings* s) void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings* s)
{ {
for (int i = 0; i < _LargestEventID; i++) { for (int i = 0; i < _LargestEventID; i++) {
@@ -1325,3 +1548,81 @@ void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings*
LogInfo("Loaded [{}] Perl Event Export Settings", l.size()); LogInfo("Loaded [{}] Perl Event Export Settings", l.size());
} }
int QuestParserCollection::EventBotMerc(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (e->IsBot() && BotHasQuestSub(event_id)) {
return EventBot(event_id, e->CastToBot(), init, lazy_data(), extra_data, extra_pointers);
} else if (e->IsMerc() && MercHasQuestSub(event_id)) {
return EventMerc(event_id, e->CastToMerc(), init, lazy_data(), extra_data, extra_pointers);
}
return false; // No quest subscription found
}
int QuestParserCollection::EventMercNPC(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (e->IsMerc() && MercHasQuestSub(event_id)) {
return EventMerc(event_id, e->CastToMerc(), init, lazy_data(), extra_data, extra_pointers);
} else if (e->IsNPC() && HasQuestSub(e->GetNPCTypeID(), event_id)) {
return EventNPC(event_id, e->CastToNPC(), init, lazy_data(), extra_data, extra_pointers);
}
return false; // No quest subscription found
}
int QuestParserCollection::EventBotMercNPC(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (e->IsBot() && BotHasQuestSub(event_id)) {
return EventBot(event_id, e->CastToBot(), init, lazy_data(), extra_data, extra_pointers);
} else if (e->IsMerc() && MercHasQuestSub(event_id)) {
return EventMerc(event_id, e->CastToMerc(), init, lazy_data(), extra_data, extra_pointers);
} else if (e->IsNPC() && HasQuestSub(e->GetNPCTypeID(), event_id)) {
return EventNPC(event_id, e->CastToNPC(), init, lazy_data(), extra_data, extra_pointers);
}
return false; // No quest subscription found
}
int QuestParserCollection::EventMob(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
if (e->IsClient() && PlayerHasQuestSub(event_id)) {
return EventPlayer(event_id, e->CastToClient(), lazy_data(), extra_data, extra_pointers);
} else if (e->IsBot() && BotHasQuestSub(event_id)) {
return EventBot(event_id, e->CastToBot(), init, lazy_data(), extra_data, extra_pointers);
} else if (e->IsMerc() && MercHasQuestSub(event_id)) {
return EventMerc(event_id, e->CastToMerc(), init, lazy_data(), extra_data, extra_pointers);
} else if (e->IsNPC() && HasQuestSub(e->GetNPCTypeID(), event_id)) {
return EventNPC(event_id, e->CastToNPC(), init, lazy_data(), extra_data, extra_pointers);
}
return false; // No quest subscription found
}
+79
View File
@@ -71,6 +71,7 @@ public:
bool SpellHasQuestSub(uint32 spell_id, QuestEventID event_id); bool SpellHasQuestSub(uint32 spell_id, QuestEventID event_id);
bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id); bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id);
bool BotHasQuestSub(QuestEventID event_id); bool BotHasQuestSub(QuestEventID event_id);
bool MercHasQuestSub(QuestEventID event_id);
int EventNPC( int EventNPC(
QuestEventID event_id, QuestEventID event_id,
@@ -126,6 +127,51 @@ public:
std::vector<std::any> *extra_pointers = nullptr std::vector<std::any> *extra_pointers = nullptr
); );
int EventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers = nullptr
);
int EventBotMerc(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data = []() { return ""; },
uint32 extra_data = 0,
std::vector<std::any>* extra_pointers = nullptr
);
int EventMercNPC(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data = []() { return ""; },
uint32 extra_data = 0,
std::vector<std::any>* extra_pointers = nullptr
);
int EventBotMercNPC(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data = []() { return ""; },
uint32 extra_data = 0,
std::vector<std::any>* extra_pointers = nullptr
);
int EventMob(
QuestEventID event_id,
Mob* e,
Mob* init,
std::function<std::string()> lazy_data = []() { return ""; },
uint32 extra_data = 0,
std::vector<std::any>* extra_pointers = nullptr
);
void GetErrors(std::list<std::string> &quest_errors); void GetErrors(std::list<std::string> &quest_errors);
/* /*
@@ -161,6 +207,8 @@ private:
bool HasEncounterSub(QuestEventID event_id, const std::string& package_name); bool HasEncounterSub(QuestEventID event_id, const std::string& package_name);
bool BotHasQuestSubLocal(QuestEventID event_id); bool BotHasQuestSubLocal(QuestEventID event_id);
bool BotHasQuestSubGlobal(QuestEventID event_id); bool BotHasQuestSubGlobal(QuestEventID event_id);
bool MercHasQuestSubLocal(QuestEventID event_id);
bool MercHasQuestSubGlobal(QuestEventID event_id);
int EventNPCLocal( int EventNPCLocal(
QuestEventID event_id, QuestEventID event_id,
@@ -214,6 +262,24 @@ private:
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
); );
int EventMercLocal(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
int EventMercGlobal(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
QuestInterface* GetQIByNPCQuest(uint32 npc_id, std::string& filename); QuestInterface* GetQIByNPCQuest(uint32 npc_id, std::string& filename);
QuestInterface* GetQIByGlobalNPCQuest(std::string& filename); QuestInterface* GetQIByGlobalNPCQuest(std::string& filename);
QuestInterface* GetQIByPlayerQuest(std::string& filename); QuestInterface* GetQIByPlayerQuest(std::string& filename);
@@ -223,6 +289,8 @@ private:
QuestInterface* GetQIByEncounterQuest(std::string encounter_name, std::string& filename); QuestInterface* GetQIByEncounterQuest(std::string encounter_name, std::string& filename);
QuestInterface* GetQIByBotQuest(std::string& filename); QuestInterface* GetQIByBotQuest(std::string& filename);
QuestInterface* GetQIByGlobalBotQuest(std::string& filename); QuestInterface* GetQIByGlobalBotQuest(std::string& filename);
QuestInterface* GetQIByMercQuest(std::string& filename);
QuestInterface* GetQIByGlobalMercQuest(std::string& filename);
int DispatchEventNPC( int DispatchEventNPC(
QuestEventID event_id, QuestEventID event_id,
@@ -270,6 +338,15 @@ private:
std::vector<std::any>* extra_pointers std::vector<std::any>* extra_pointers
); );
int DispatchEventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
std::map<uint32, QuestInterface*> _interfaces; std::map<uint32, QuestInterface*> _interfaces;
std::map<uint32, std::string> _extensions; std::map<uint32, std::string> _extensions;
std::list<QuestInterface*> _load_precedence; std::list<QuestInterface*> _load_precedence;
@@ -280,6 +357,8 @@ private:
uint32 _global_player_quest_status; uint32 _global_player_quest_status;
uint32 _bot_quest_status; uint32 _bot_quest_status;
uint32 _global_bot_quest_status; uint32 _global_bot_quest_status;
uint32 _merc_quest_status;
uint32 _global_merc_quest_status;
std::map<uint32, uint32> _spell_quest_status; std::map<uint32, uint32> _spell_quest_status;
std::map<uint32, uint32> _item_quest_status; std::map<uint32, uint32> _item_quest_status;
std::map<std::string, uint32> _encounter_quest_status; std::map<std::string, uint32> _encounter_quest_status;
+79 -224
View File
@@ -90,12 +90,7 @@ void QuestManager::Process() {
while (cur != end) { while (cur != end) {
if (cur->Timer_.Enabled() && cur->Timer_.Check()) { if (cur->Timer_.Enabled() && cur->Timer_.Check()) {
if (cur->mob) { if (cur->mob) {
if (cur->mob->IsNPC()) { if (cur->mob->IsEncounter()) {
if (parse->HasQuestSub(cur->mob->GetNPCTypeID(), EVENT_TIMER)) {
parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0);
}
}
else if (cur->mob->IsEncounter()) {
parse->EventEncounter( parse->EventEncounter(
EVENT_TIMER, EVENT_TIMER,
cur->mob->CastToEncounter()->GetEncounterName(), cur->mob->CastToEncounter()->GetEncounterName(),
@@ -103,17 +98,8 @@ void QuestManager::Process() {
0, 0,
nullptr nullptr
); );
} } else {
else if (cur->mob->IsClient()) { parse->EventMob(EVENT_TIMER, cur->mob, nullptr, [&]() { return cur->name; }, 0);
if (parse->PlayerHasQuestSub(EVENT_TIMER)) {
//this is inheriently unsafe if we ever make it so more than npc/client start timers
parse->EventPlayer(EVENT_TIMER, cur->mob->CastToClient(), cur->name, 0);
}
}
else if (cur->mob->IsBot()) {
if (parse->BotHasQuestSub(EVENT_TIMER)) {
parse->EventBot(EVENT_TIMER, cur->mob->CastToBot(), nullptr, cur->name, 0);
}
} }
//we MUST reset our iterator since the quest could have removed/added any //we MUST reset our iterator since the quest could have removed/added any
@@ -539,32 +525,20 @@ void QuestManager::settimer(const std::string& timer_name, uint32 seconds, Mob*
return; return;
} }
const bool has_start_event = ( std::function<std::string()> f = [&]() {
(mob->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_START)) || return fmt::format(
(mob->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_START)) || "{} {}",
(mob->IsNPC() && parse->HasQuestSub(mob->GetNPCTypeID(), EVENT_TIMER_START)) timer_name,
seconds * 1000
); );
};
if (!QTimerList.empty()) { if (!QTimerList.empty()) {
for (auto& e : QTimerList) { for (auto& e : QTimerList) {
if (e.mob && e.mob == mob && e.name == timer_name) { if (e.mob && e.mob == mob && e.name == timer_name) {
e.Timer_.Start(seconds * 1000, false); e.Timer_.Start(seconds * 1000, false);
if (has_start_event) { parse->EventMob(EVENT_TIMER_START, mob, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
seconds * 1000
);
if (mob->IsClient()) {
parse->EventPlayer(EVENT_TIMER_START, mob->CastToClient(), export_string, 0);
} else if (mob->IsBot()) {
parse->EventBot(EVENT_TIMER_START, mob->CastToBot(), nullptr, export_string, 0);
} else if (mob->IsNPC()) {
parse->EventNPC(EVENT_TIMER_START, mob->CastToNPC(), nullptr, export_string, 0);
}
}
return; return;
} }
@@ -573,21 +547,7 @@ void QuestManager::settimer(const std::string& timer_name, uint32 seconds, Mob*
QTimerList.emplace_back(QuestTimer(seconds * 1000, mob, timer_name)); QTimerList.emplace_back(QuestTimer(seconds * 1000, mob, timer_name));
if (has_start_event) { parse->EventMob(EVENT_TIMER_START, mob, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
seconds * 1000
);
if (mob->IsClient()) {
parse->EventPlayer(EVENT_TIMER_START, mob->CastToClient(), export_string, 0);
} else if (mob->IsBot()) {
parse->EventBot(EVENT_TIMER_START, mob->CastToBot(), nullptr, export_string, 0);
} else if (mob->IsNPC()) {
parse->EventNPC(EVENT_TIMER_START, mob->CastToNPC(), nullptr, export_string, 0);
}
}
} }
void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds) void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds)
@@ -598,11 +558,13 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds
return; return;
} }
const bool has_start_event = ( std::function<std::string()> f = [&]() {
(owner->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_START)) || return fmt::format(
(owner->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_START)) || "{} {}",
(owner->IsNPC() && parse->HasQuestSub(owner->GetNPCTypeID(), EVENT_TIMER_START)) timer_name,
milliseconds
); );
};
if (questitem) { if (questitem) {
questitem->SetTimer(timer_name, milliseconds); questitem->SetTimer(timer_name, milliseconds);
@@ -625,21 +587,7 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds
if (e.mob && e.mob == owner && e.name == timer_name) { if (e.mob && e.mob == owner && e.name == timer_name) {
e.Timer_.Start(milliseconds, false); e.Timer_.Start(milliseconds, false);
if (has_start_event) { parse->EventMob(EVENT_TIMER_START, owner, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
e.name,
milliseconds
);
if (owner->IsClient()) {
parse->EventPlayer(EVENT_TIMER_START, owner->CastToClient(), export_string, 0);
} else if (owner->IsBot()) {
parse->EventBot(EVENT_TIMER_START, owner->CastToBot(), nullptr, export_string, 0);
} else if (owner->IsNPC()) {
parse->EventNPC(EVENT_TIMER_START, owner->CastToNPC(), nullptr, export_string, 0);
}
}
return; return;
} }
@@ -648,21 +596,7 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds
QTimerList.emplace_back(QuestTimer(milliseconds, owner, timer_name)); QTimerList.emplace_back(QuestTimer(milliseconds, owner, timer_name));
if (has_start_event) { parse->EventMob(EVENT_TIMER_START, owner, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
milliseconds
);
if (owner->IsClient()) {
parse->EventPlayer(EVENT_TIMER_START, owner->CastToClient(), export_string, 0);
} else if (owner->IsBot()) {
parse->EventBot(EVENT_TIMER_START, owner->CastToBot(), nullptr, export_string, 0);
} else if (owner->IsNPC()) {
parse->EventNPC(EVENT_TIMER_START, owner->CastToNPC(), nullptr, export_string, 0);
}
}
} }
void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds, EQ::ItemInstance* inst) void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds, EQ::ItemInstance* inst)
@@ -678,32 +612,20 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds
return; return;
} }
const bool has_start_event = ( std::function<std::string()> f = [&]() {
(m->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_START)) || return fmt::format(
(m->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_START)) || "{} {}",
(m->IsNPC() && parse->HasQuestSub(m->GetNPCTypeID(), EVENT_TIMER_START)) timer_name,
milliseconds
); );
};
if (!QTimerList.empty()) { if (!QTimerList.empty()) {
for (auto& e : QTimerList) { for (auto& e : QTimerList) {
if (e.mob && e.mob == m && e.name == timer_name) { if (e.mob && e.mob == m && e.name == timer_name) {
e.Timer_.Start(milliseconds, false); e.Timer_.Start(milliseconds, false);
if (has_start_event) { parse->EventMob(EVENT_TIMER_START, m, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
milliseconds
);
if (m->IsClient()) {
parse->EventPlayer(EVENT_TIMER_START, m->CastToClient(), export_string, 0);
} else if (m->IsBot()) {
parse->EventBot(EVENT_TIMER_START, m->CastToBot(), nullptr, export_string, 0);
} else if (m->IsNPC()) {
parse->EventNPC(EVENT_TIMER_START, m->CastToNPC(), nullptr, export_string, 0);
}
}
return; return;
} }
@@ -712,21 +634,7 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds
QTimerList.emplace_back(QuestTimer(milliseconds, m, timer_name)); QTimerList.emplace_back(QuestTimer(milliseconds, m, timer_name));
if (has_start_event) { parse->EventMob(EVENT_TIMER_START, m, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
milliseconds
);
if (m->IsClient()) {
parse->EventPlayer(EVENT_TIMER_START, m->CastToClient(), export_string, 0);
} else if (m->IsBot()) {
parse->EventBot(EVENT_TIMER_START, m->CastToBot(), nullptr, export_string, 0);
} else if (m->IsNPC()) {
parse->EventNPC(EVENT_TIMER_START, m->CastToNPC(), nullptr, export_string, 0);
}
}
} }
void QuestManager::stoptimer(const std::string& timer_name) void QuestManager::stoptimer(const std::string& timer_name)
@@ -751,23 +659,16 @@ void QuestManager::stoptimer(const std::string& timer_name)
return; return;
} }
const bool has_stop_event = (
(owner->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_STOP)) ||
(owner->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_STOP)) ||
(owner->IsNPC() && parse->HasQuestSub(owner->GetNPCTypeID(), EVENT_TIMER_STOP))
);
for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) { for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) {
if (e->mob && e->mob == owner && e->name == timer_name) { if (e->mob && e->mob == owner && e->name == timer_name) {
if (has_stop_event) { parse->EventMob(
if (owner->IsClient()) { EVENT_TIMER_STOP,
parse->EventPlayer(EVENT_TIMER_STOP, owner->CastToClient(), timer_name, 0); owner,
} else if (owner->IsBot()) { nullptr,
parse->EventBot(EVENT_TIMER_STOP, owner->CastToBot(), nullptr, timer_name, 0); [&]() {
} else if (owner->IsNPC()) { return timer_name;
parse->EventNPC(EVENT_TIMER_STOP, owner->CastToNPC(), nullptr, timer_name, 0);
}
} }
);
QTimerList.erase(e); QTimerList.erase(e);
break; break;
@@ -792,23 +693,16 @@ void QuestManager::stoptimer(const std::string& timer_name, Mob* m)
return; return;
} }
const bool has_stop_event = (
(m->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_STOP)) ||
(m->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_STOP)) ||
(m->IsNPC() && parse->HasQuestSub(m->GetNPCTypeID(), EVENT_TIMER_STOP))
);
for (auto e = QTimerList.begin(); e != QTimerList.end();) { for (auto e = QTimerList.begin(); e != QTimerList.end();) {
if (e->mob && e->mob == m) { if (e->mob && e->mob == m) {
if (has_stop_event) { parse->EventMob(
if (m->IsClient()) { EVENT_TIMER_STOP,
parse->EventPlayer(EVENT_TIMER_STOP, m->CastToClient(), e->name, 0); m,
} else if (m->IsBot()) { nullptr,
parse->EventBot(EVENT_TIMER_STOP, m->CastToBot(), nullptr, e->name, 0); [&]() {
} else if (m->IsNPC()) { return timer_name;
parse->EventNPC(EVENT_TIMER_STOP, m->CastToNPC(), nullptr, e->name, 0);
}
} }
);
QTimerList.erase(e); QTimerList.erase(e);
break; break;
@@ -847,23 +741,16 @@ void QuestManager::stopalltimers()
return; return;
} }
const bool has_stop_event = (
(owner->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_STOP)) ||
(owner->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_STOP)) ||
(owner->IsNPC() && parse->HasQuestSub(owner->GetNPCTypeID(), EVENT_TIMER_STOP))
);
for (auto e = QTimerList.begin(); e != QTimerList.end();) { for (auto e = QTimerList.begin(); e != QTimerList.end();) {
if (e->mob && e->mob == owner) { if (e->mob && e->mob == owner) {
if (has_stop_event) { parse->EventMob(
if (owner->IsClient()) { EVENT_TIMER_STOP,
parse->EventPlayer(EVENT_TIMER_STOP, owner->CastToClient(), e->name, 0); owner,
} else if (owner->IsBot()) { nullptr,
parse->EventBot(EVENT_TIMER_STOP, owner->CastToBot(), nullptr, e->name, 0); [&]() {
} else if (owner->IsNPC()) { return e->name;
parse->EventNPC(EVENT_TIMER_STOP, owner->CastToNPC(), nullptr, e->name, 0);
}
} }
);
e = QTimerList.erase(e); e = QTimerList.erase(e);
} else { } else {
@@ -903,23 +790,16 @@ void QuestManager::stopalltimers(Mob* m)
return; return;
} }
const bool has_stop_event = (
(m->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_STOP)) ||
(m->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_STOP)) ||
(m->IsNPC() && parse->HasQuestSub(m->GetNPCTypeID(), EVENT_TIMER_STOP))
);
for (auto e = QTimerList.begin(); e != QTimerList.end();) { for (auto e = QTimerList.begin(); e != QTimerList.end();) {
if (e->mob && e->mob == m) { if (e->mob && e->mob == m) {
if (has_stop_event) { parse->EventMob(
if (m->IsClient()) { EVENT_TIMER_STOP,
parse->EventPlayer(EVENT_TIMER_STOP, m->CastToClient(), e->name, 0); m,
} else if (m->IsBot()) { nullptr,
parse->EventBot(EVENT_TIMER_STOP, m->CastToBot(), nullptr, e->name, 0); [&]() {
} else if (m->IsNPC()) { return e->name;
parse->EventNPC(EVENT_TIMER_STOP, m->CastToNPC(), nullptr, e->name, 0);
}
} }
);
e = QTimerList.erase(e); e = QTimerList.erase(e);
} else { } else {
@@ -955,12 +835,6 @@ void QuestManager::pausetimer(const std::string& timer_name, Mob* m)
uint32 milliseconds = 0; uint32 milliseconds = 0;
const bool has_pause_event = (
(mob->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_PAUSE)) ||
(mob->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_PAUSE)) ||
(mob->IsNPC() && parse->HasQuestSub(mob->GetNPCTypeID(), EVENT_TIMER_PAUSE))
);
if (!QTimerList.empty()) { if (!QTimerList.empty()) {
for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) { for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) {
if (e->mob && e->mob == mob && e->name == timer_name) { if (e->mob && e->mob == mob && e->name == timer_name) {
@@ -979,21 +853,18 @@ void QuestManager::pausetimer(const std::string& timer_name, Mob* m)
} }
); );
if (has_pause_event) { parse->EventMob(
const std::string& export_string = fmt::format( EVENT_TIMER_PAUSE,
mob,
nullptr,
[&]() {
return fmt::format(
"{} {}", "{} {}",
timer_name, timer_name,
milliseconds milliseconds
); );
if (mob->IsClient()) {
parse->EventPlayer(EVENT_TIMER_PAUSE, mob->CastToClient(), export_string, 0);
} else if (mob->IsBot()) {
parse->EventBot(EVENT_TIMER_PAUSE, mob->CastToBot(), nullptr, export_string, 0);
} else if (mob->IsNPC()) {
parse->EventNPC(EVENT_TIMER_PAUSE, mob->CastToNPC(), nullptr, export_string, 0);
}
} }
);
LogQuests("Pausing timer [{}] for [{}] with [{}] ms remaining", timer_name, owner->GetName(), milliseconds); LogQuests("Pausing timer [{}] for [{}] with [{}] ms remaining", timer_name, owner->GetName(), milliseconds);
} }
@@ -1031,11 +902,13 @@ void QuestManager::resumetimer(const std::string& timer_name, Mob* m)
return; return;
} }
const bool has_resume_event = ( std::function<std::string()> f = [&]() {
(mob->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_RESUME)) || return fmt::format(
(mob->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_RESUME)) || "{} {}",
(mob->IsNPC() && parse->HasQuestSub(mob->GetNPCTypeID(), EVENT_TIMER_RESUME)) timer_name,
milliseconds
); );
};
if (!QTimerList.empty()) { if (!QTimerList.empty()) {
for (auto e : QTimerList) { for (auto e : QTimerList) {
@@ -1049,21 +922,8 @@ void QuestManager::resumetimer(const std::string& timer_name, Mob* m)
milliseconds milliseconds
); );
if (has_resume_event) { parse->EventMob(EVENT_TIMER_RESUME, mob, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
milliseconds
);
if (mob->IsClient()) {
parse->EventPlayer(EVENT_TIMER_RESUME, mob->CastToClient(), export_string, 0);
} else if (mob->IsBot()) {
parse->EventBot(EVENT_TIMER_RESUME, mob->CastToBot(), nullptr, export_string, 0);
} else if (mob->IsNPC()) {
parse->EventNPC(EVENT_TIMER_RESUME, mob->CastToNPC(), nullptr, export_string, 0);
}
}
return; return;
} }
} }
@@ -1071,21 +931,7 @@ void QuestManager::resumetimer(const std::string& timer_name, Mob* m)
QTimerList.emplace_back(QuestTimer(milliseconds, m, timer_name)); QTimerList.emplace_back(QuestTimer(milliseconds, m, timer_name));
if (has_resume_event) { parse->EventMob(EVENT_TIMER_RESUME, mob, nullptr, f);
const std::string& export_string = fmt::format(
"{} {}",
timer_name,
milliseconds
);
if (mob->IsClient()) {
parse->EventPlayer(EVENT_TIMER_RESUME, mob->CastToClient(), export_string, 0);
} else if (mob->IsBot()) {
parse->EventBot(EVENT_TIMER_RESUME, mob->CastToBot(), nullptr, export_string, 0);
} else if (mob->IsNPC()) {
parse->EventNPC(EVENT_TIMER_RESUME, mob->CastToNPC(), nullptr, export_string, 0);
}
}
LogQuests( LogQuests(
"Creating a new timer and resuming [{}] for [{}] with [{}] ms remaining", "Creating a new timer and resuming [{}] for [{}] with [{}] ms remaining",
@@ -4213,6 +4059,15 @@ Bot *QuestManager::GetBot() const {
return nullptr; return nullptr;
} }
Merc *QuestManager::GetMerc() const {
if (!quests_running_.empty()) {
running_quest e = quests_running_.top();
return (e.owner && e.owner->IsMerc()) ? e.owner->CastToMerc() : nullptr;
}
return nullptr;
}
Mob *QuestManager::GetOwner() const { Mob *QuestManager::GetOwner() const {
if(!quests_running_.empty()) { if(!quests_running_.empty()) {
running_quest e = quests_running_.top(); running_quest e = quests_running_.top();
+1
View File
@@ -360,6 +360,7 @@ public:
Bot *GetBot() const; Bot *GetBot() const;
Client *GetInitiator() const; Client *GetInitiator() const;
Merc* GetMerc() const;
NPC *GetNPC() const; NPC *GetNPC() const;
Mob *GetOwner() const; Mob *GetOwner() const;
EQ::InventoryProfile* GetInventory() const; EQ::InventoryProfile* GetInventory() const;
+69 -139
View File
@@ -254,54 +254,34 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
} }
} }
if (IsClient()) {
if (parse->PlayerHasQuestSub(EVENT_CAST_BEGIN)) {
Mob* spell_target = entity_list.GetMobID(target_id); Mob* spell_target = entity_list.GetMobID(target_id);
std::vector<std::any> args = { spell_target }; std::vector<std::any> args = { spell_target };
const auto& export_string = fmt::format( int return_value = parse->EventMob(
EVENT_CAST_BEGIN,
this,
nullptr,
[&]() {
return fmt::format(
"{} {} {} {}", "{} {} {} {}",
spell_id, spell_id,
GetID(), GetID(),
GetCasterLevel(spell_id), GetCasterLevel(spell_id),
target_id target_id
); );
if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), export_string, 0, &args) != 0) { },
0,
&args
);
if (IsClient() && return_value != 0) {
if (IsDiscipline(spell_id)) { if (IsDiscipline(spell_id)) {
CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, 0); CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, 0);
} } else {
else {
CastToClient()->SendSpellBarEnable(spell_id); CastToClient()->SendSpellBarEnable(spell_id);
} }
return false; return false;
} }
}
} else if (IsNPC()) {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_CAST_BEGIN)) {
Mob* spell_target = entity_list.GetMobID(target_id);
std::vector<std::any> args = { spell_target };
const auto& export_string = fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
GetCasterLevel(spell_id),
target_id
);
parse->EventNPC(EVENT_CAST_BEGIN, CastToNPC(), nullptr, export_string, 0, &args);
}
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_CAST_BEGIN)) {
Mob* spell_target = entity_list.GetMobID(target_id);
std::vector<std::any> args = { spell_target };
const auto& export_string = fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
GetCasterLevel(spell_id),
target_id
);
parse->EventBot(EVENT_CAST_BEGIN, CastToBot(), nullptr, export_string, 0, &args);
}
}
//To prevent NPC ghosting when spells are cast from scripts //To prevent NPC ghosting when spells are cast from scripts
if (IsNPC() && IsMoving() && cast_time > 0) { if (IsNPC() && IsMoving() && cast_time > 0) {
@@ -821,14 +801,12 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp
} }
if (!spell_target) { if (!spell_target) {
if (IsGroupSpell(spell_id)){ if (IsGroupSpell(spell_id)) {
return true; return true;
} } else if (spells[spell_id].target_type == ST_Self) {
else if (spells[spell_id].target_type == ST_Self) {
spell_target = this; spell_target = this;
} }
} } else {
else {
if (IsGroupSpell(spell_id) && spell_target != this) { if (IsGroupSpell(spell_id) && spell_target != this) {
ignore_on_casting = true; ignore_on_casting = true;
} }
@@ -942,7 +920,13 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp
/* /*
Requires target to be in same group or same raid in order to apply invisible. Requires target to be in same group or same raid in order to apply invisible.
*/ */
if (check_on_casting && RuleB(Spells, InvisRequiresGroup) && IsInvisibleSpell(spell_id)) { if (
check_on_casting &&
spells[spell_id].target_type != ST_Self &&
GetTarget() != this &&
RuleB(Spells, InvisRequiresGroup) &&
IsInvisibleSpell(spell_id)
) {
if (IsClient() && spell_target && spell_target->IsClient()) { if (IsClient() && spell_target && spell_target->IsClient()) {
if (spell_target && spell_target->GetID() != GetID()) { if (spell_target && spell_target->GetID() != GetID()) {
bool cast_failed = true; bool cast_failed = true;
@@ -954,8 +938,7 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp
(target_group->GetID() == my_group->GetID())) { (target_group->GetID() == my_group->GetID())) {
cast_failed = false; cast_failed = false;
} }
} } else if (spell_target->IsRaidGrouped()) {
else if (spell_target->IsRaidGrouped()) {
Raid *target_raid = spell_target->GetRaid(); Raid *target_raid = spell_target->GetRaid();
Raid *my_raid = GetRaid(); Raid *my_raid = GetRaid();
if (target_raid && if (target_raid &&
@@ -1819,47 +1802,24 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
} }
} }
// std::vector<std::any> args = { spell_target };
// at this point the spell has successfully been cast
//
if (IsClient()) { parse->EventMob(
if (parse->PlayerHasQuestSub(EVENT_CAST)) { EVENT_CAST,
std::vector<std::any> args = { spell_target }; this,
const auto& export_string = fmt::format( nullptr,
[&]() {
return fmt::format(
"{} {} {} {}", "{} {} {} {}",
spell_id, spell_id,
GetID(), GetID(),
GetCasterLevel(spell_id), GetCasterLevel(spell_id),
target_id target_id
); );
parse->EventPlayer(EVENT_CAST, CastToClient(), export_string, 0, &args); },
} 0,
} else if (IsNPC()) { &args
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_CAST)) {
std::vector<std::any> args = { spell_target };
const auto& export_string = fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
GetCasterLevel(spell_id),
target_id
); );
parse->EventNPC(EVENT_CAST, CastToNPC(), nullptr, export_string, 0, &args);
}
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_CAST)) {
std::vector<std::any> args = { spell_target };
const auto& export_string = fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
GetCasterLevel(spell_id),
target_id
);
parse->EventBot(EVENT_CAST, CastToBot(), nullptr, export_string, 0, &args);
}
}
if(bard_song_mode) if(bard_song_mode)
{ {
@@ -2473,8 +2433,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in
//Guard Assist Code //Guard Assist Code
if (RuleB(Character, PVPEnableGuardFactionAssist) && spell_target && IsDetrimentalSpell(spell_id) && spell_target != this) { if (RuleB(Character, PVPEnableGuardFactionAssist) && spell_target && IsDetrimentalSpell(spell_id) && spell_target != this) {
if (IsClient() && spell_target->IsClient()|| (HasOwner() && GetOwner()->IsClient() && spell_target->IsClient())) { if (IsClient() && spell_target->IsClient()|| (HasOwner() && GetOwner()->IsClient() && spell_target->IsClient())) {
auto& mob_list = entity_list.GetCloseMobList(spell_target); for (auto& e : spell_target->GetCloseMobList()) {
for (auto& e : mob_list) {
auto mob = e.second; auto mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -3005,7 +2964,6 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste
int res = CalcBuffDuration_formula(castlevel, formula, duration); int res = CalcBuffDuration_formula(castlevel, formula, duration);
if ( if (
caster == target &&
( (
target->aabonuses.IllusionPersistence || target->aabonuses.IllusionPersistence ||
target->spellbonuses.IllusionPersistence || target->spellbonuses.IllusionPersistence ||
@@ -3620,44 +3578,18 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
); );
} }
const bool caster_has_block_event = ( std::function<std::string()> f = [&]() {
(caster->IsBot() && parse->BotHasQuestSub(EVENT_SPELL_BLOCKED)) || return fmt::format(
(caster->IsClient() && parse->PlayerHasQuestSub(EVENT_SPELL_BLOCKED)) ||
(caster->IsNPC() && parse->HasQuestSub(caster->GetNPCTypeID(), EVENT_SPELL_BLOCKED))
);
const bool cast_on_has_block_event = (
(IsBot() && parse->BotHasQuestSub(EVENT_SPELL_BLOCKED)) ||
(IsClient() && parse->PlayerHasQuestSub(EVENT_SPELL_BLOCKED)) ||
(IsNPC() && parse->HasQuestSub(GetNPCTypeID(), EVENT_SPELL_BLOCKED))
);
if (caster_has_block_event || cast_on_has_block_event) {
const std::string& export_string = fmt::format(
"{} {}", "{} {}",
curbuf.spellid, curbuf.spellid,
spell_id spell_id
); );
};
if (caster_has_block_event) { parse->EventMob(EVENT_SPELL_BLOCKED, caster, this, f);
if (caster->IsBot()) {
parse->EventBot(EVENT_SPELL_BLOCKED, caster->CastToBot(), this, export_string, 0);
} else if (caster->IsClient()) {
parse->EventPlayer(EVENT_SPELL_BLOCKED, caster->CastToClient(), export_string, 0);
} else if (caster->IsNPC()) {
parse->EventNPC(EVENT_SPELL_BLOCKED, caster->CastToNPC(), this, export_string, 0);
}
}
if (cast_on_has_block_event && caster != this) { if (caster != this) {
if (IsBot()) { parse->EventMob(EVENT_SPELL_BLOCKED, this, caster, f);
parse->EventBot(EVENT_SPELL_BLOCKED, CastToBot(), caster, export_string, 0);
} else if (IsClient()) {
parse->EventPlayer(EVENT_SPELL_BLOCKED, CastToClient(), export_string, 0);
} else if (IsNPC()) {
parse->EventNPC(EVENT_SPELL_BLOCKED, CastToNPC(), caster, export_string, 0);
}
}
} }
} }
@@ -4031,43 +3963,24 @@ bool Mob::SpellOnTarget(
(spellOwner->IsClient() ? FilterPCSpells : FilterNPCSpells) /* EQ Filter Type: (8 or 9) */ (spellOwner->IsClient() ? FilterPCSpells : FilterNPCSpells) /* EQ Filter Type: (8 or 9) */
); );
if (spelltar->IsNPC()) {
if (parse->HasQuestSub(spelltar->GetNPCTypeID(), EVENT_CAST_ON)) {
std::vector<std::any> args = { spelltar }; std::vector<std::any> args = { spelltar };
const auto& export_string = fmt::format(
parse->EventMob(
EVENT_CAST_ON,
spelltar,
this,
[&]() {
return fmt::format(
"{} {} {} {}", "{} {} {} {}",
spell_id, spell_id,
GetID(), GetID(),
caster_level, caster_level,
target_id target_id
); );
parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, export_string, 0, &args); },
} 0,
} else if (spelltar->IsClient()) { &args
if (parse->PlayerHasQuestSub(EVENT_CAST_ON)) {
std::vector<std::any> args = { spelltar };
const auto& export_string = fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
caster_level,
target_id
); );
parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(), export_string, 0, &args);
}
} else if (spelltar->IsBot()) {
if (parse->BotHasQuestSub(EVENT_CAST_ON)) {
std::vector<std::any> args = { spelltar };
const auto& export_string = fmt::format(
"{} {} {} {}",
spell_id,
GetID(),
caster_level,
target_id
);
parse->EventBot(EVENT_CAST_ON, spelltar->CastToBot(), this, export_string, 0, &args);
}
}
if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) { if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) {
safe_delete(action_packet); safe_delete(action_packet);
@@ -4985,6 +4898,23 @@ void Mob::BuffFadeByEffect(int effect_id, int slot_to_skip)
} }
} }
void Mob::BuffFadeSongs() {
bool recalc_bonus = false;
int buff_count = GetMaxTotalSlots();
for (int buff_slot = 0; buff_slot < buff_count; buff_slot++) {
const uint16 current_spell_id = buffs[buff_slot].spellid;
if (IsBardSong(current_spell_id)) {
BuffFadeBySlot(buff_slot, false);
recalc_bonus = true;
}
}
if (recalc_bonus) {
CalcBonuses();
}
}
bool Mob::IsAffectedByBuffByGlobalGroup(GlobalGroup group) bool Mob::IsAffectedByBuffByGlobalGroup(GlobalGroup group)
{ {
int buff_count = GetMaxTotalSlots(); int buff_count = GetMaxTotalSlots();
+1 -1
View File
@@ -3495,7 +3495,7 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati
ps.item_slot = parcel_out.slot_id; ps.item_slot = parcel_out.slot_id;
strn0cpy(ps.send_to, GetCleanName(), sizeof(ps.send_to)); strn0cpy(ps.send_to, GetCleanName(), sizeof(ps.send_to));
if (trader_item.item_charges == tbs->quantity) { if (trader_item.item_charges <= static_cast<int32>(tbs->quantity)) {
TraderRepository::DeleteOne(database, trader_item.id); TraderRepository::DeleteOne(database, trader_item.id);
} else { } else {
TraderRepository::UpdateQuantity( TraderRepository::UpdateQuantity(
+12
View File
@@ -561,6 +561,12 @@ bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct*
m_epp->expended_aa = e.e_expended_aa_spent; m_epp->expended_aa = e.e_expended_aa_spent;
m_epp->last_invsnapshot_time = e.e_last_invsnapshot; m_epp->last_invsnapshot_time = e.e_last_invsnapshot;
m_epp->next_invsnapshot_time = m_epp->last_invsnapshot_time + (RuleI(Character, InvSnapshotMinIntervalM) * 60); m_epp->next_invsnapshot_time = m_epp->last_invsnapshot_time + (RuleI(Character, InvSnapshotMinIntervalM) * 60);
pp->cold_resist = e.cold_resist;
pp->fire_resist = e.fire_resist;
pp->magic_resist = e.magic_resist;
pp->disease_resist = e.disease_resist;
pp->poison_resist = e.poison_resist;
pp->corruption_resist = e.corruption_resist;
return true; return true;
} }
@@ -1158,6 +1164,12 @@ bool ZoneDatabase::SaveCharacterData(
e.e_expended_aa_spent = m_epp->expended_aa; e.e_expended_aa_spent = m_epp->expended_aa;
e.e_last_invsnapshot = m_epp->last_invsnapshot_time; e.e_last_invsnapshot = m_epp->last_invsnapshot_time;
e.mailkey = c->GetMailKeyFull(); e.mailkey = c->GetMailKeyFull();
e.cold_resist = pp->cold_resist;
e.fire_resist = pp->fire_resist;
e.magic_resist = pp->magic_resist;
e.disease_resist = pp->disease_resist;
e.poison_resist = pp->poison_resist;
e.corruption_resist = pp->corruption_resist;
const int replaced = CharacterDataRepository::ReplaceOne(database, e); const int replaced = CharacterDataRepository::ReplaceOne(database, e);