mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-13 02:38:45 +00:00
Compare commits
114 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9be2485330 | |||
| a2bf10624a | |||
| ed58d16f1f | |||
| e9b45fb360 | |||
| 803972873a | |||
| d9e57eca79 | |||
| 6429dc80d3 | |||
| c4262b3fa6 | |||
| 92128b98fd | |||
| b9cfdea76c | |||
| c8a7066d0e | |||
| 950cc4a325 | |||
| 82b48fe6e8 | |||
| ca9c1fdd24 | |||
| fe08961d25 | |||
| 5b9f7ff4c9 | |||
| 23743a4050 | |||
| 444d688ad2 | |||
| d554eb3423 | |||
| deb298dda7 | |||
| 19e785b842 | |||
| 7b9691d486 | |||
| 30fddcc5a0 | |||
| 235e59a2d8 | |||
| bc1ffe0716 | |||
| 44497414db | |||
| 96e34fe8f7 | |||
| ceca28d2a3 | |||
| b040571427 | |||
| 49664cc1a0 | |||
| 5e4fd43920 | |||
| 937b947597 | |||
| bb70850421 | |||
| 46511365a7 | |||
| 6d69ac7a98 | |||
| 938937c271 | |||
| cd808416c8 | |||
| a05d0752f6 | |||
| 799609fb21 | |||
| 213fe6a9e9 | |||
| be6a5d5f50 | |||
| 1af29bd7b1 | |||
| ef945e6e99 | |||
| 9528c1e7fc | |||
| fd6e5f465d | |||
| d00125abe1 | |||
| 5d69235a4c | |||
| e93785f885 | |||
| 3c2545cfaf | |||
| 8d1a9efac9 | |||
| f6b18fb003 | |||
| 00e77f190c | |||
| 9cb72a6ba7 | |||
| 8203c034bf | |||
| 33ae51f56f | |||
| 30c39194a3 | |||
| 051ce3736f | |||
| 84708edccf | |||
| da4e9ab95b | |||
| a8fea95eab | |||
| 53610c2f0f | |||
| 9f10c12874 | |||
| a0634adb3c | |||
| a2ed6be1f5 | |||
| c33ac40567 | |||
| 9ee095b354 | |||
| 7ab32af4dc | |||
| 0c301419c2 | |||
| d6a21be25e | |||
| 1d4ba082ad | |||
| 94553501ba | |||
| da824d5178 | |||
| 5a1df38900 | |||
| 8cd7148b29 | |||
| 09e079a45e | |||
| 4bc881da4b | |||
| 0615864d51 | |||
| 3638d157b2 | |||
| d41725e325 | |||
| 88580b69b6 | |||
| f7a6fe595a | |||
| 07d14c2681 | |||
| eac7a73fb6 | |||
| c5715f1f14 | |||
| 3902230fa1 | |||
| 212969f5cd | |||
| de4226fdc9 | |||
| 8b13434197 | |||
| 27274397ec | |||
| 2b79a36014 | |||
| 8cf52294e9 | |||
| 0ef79903f8 | |||
| ab14458f9e | |||
| 5b94e736b3 | |||
| c3b8cc9744 | |||
| 89e3b2c72e | |||
| 23c4aa241b | |||
| acb7584e26 | |||
| a885bd9322 | |||
| 20fe1926e0 | |||
| eb7118754b | |||
| 3611b49f68 | |||
| 511d8a8bb3 | |||
| 1598d2e17b | |||
| 805757ba87 | |||
| eb6ac25540 | |||
| cb634cf57d | |||
| 2f7ca2cdc8 | |||
| 425d24c1f4 | |||
| 875df8e64a | |||
| 7a2d2a0c51 | |||
| 5296202e56 | |||
| e2db8ffea8 | |||
| fa2ab11676 |
+255
@@ -1,3 +1,258 @@
|
|||||||
|
## [23.4.0] 3/30/2025
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
* Expose Zoneserver Compile Metadata ([#4815](https://github.com/EQEmu/Server/pull/4815)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Bots
|
||||||
|
|
||||||
|
* Charmed Pets were breaking Mob respawns ([#4780](https://github.com/EQEmu/Server/pull/4780)) @nytmyr 2025-03-16
|
||||||
|
* Enraged positioning ([#4789](https://github.com/EQEmu/Server/pull/4789)) @nytmyr 2025-03-29
|
||||||
|
* Fix IsValidSpellTypeBySpellID to account for all types ([#4764](https://github.com/EQEmu/Server/pull/4764)) @nytmyr 2025-03-19
|
||||||
|
* Fix Rule ZonesWithSpawnLimits/ZonesWithForcedSpawnLimits errors ([#4791](https://github.com/EQEmu/Server/pull/4791)) @nytmyr 2025-03-29
|
||||||
|
* Fix rule Bots:FinishBuffing ([#4788](https://github.com/EQEmu/Server/pull/4788)) @nytmyr 2025-03-29
|
||||||
|
* Line of Sight and Mez optimizations and cleanup ([#4746](https://github.com/EQEmu/Server/pull/4746)) @nytmyr 2025-03-29
|
||||||
|
* Prevent bot pets from despawning on #repop ([#4790](https://github.com/EQEmu/Server/pull/4790)) @nytmyr 2025-03-29
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
* Control flow defaults missed in recent bot updates ([#4817](https://github.com/EQEmu/Server/pull/4817)) @joligario 2025-03-30
|
||||||
|
* Remove Extraneous Time Type in ShowZoneData ([#4806](https://github.com/EQEmu/Server/pull/4806)) @Kinglykrab 2025-03-29
|
||||||
|
* Remove Unused Command Methods ([#4805](https://github.com/EQEmu/Server/pull/4805)) @Kinglykrab 2025-03-29
|
||||||
|
* UCS Member Count ([#4819](https://github.com/EQEmu/Server/pull/4819)) @joligario 2025-03-30
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
* Add #show zone_variables ([#4812](https://github.com/EQEmu/Server/pull/4812)) @Akkadius 2025-03-29
|
||||||
|
* Add Instance Support to #zoneshutdown ([#4807](https://github.com/EQEmu/Server/pull/4807)) @Kinglykrab 2025-03-29
|
||||||
|
|
||||||
|
### Crash
|
||||||
|
|
||||||
|
* Fix Rarer World Crash with Player Event Thread Processor ([#4800](https://github.com/EQEmu/Server/pull/4800)) @Akkadius 2025-03-29
|
||||||
|
* Fix Repop Race Condition Crash ([#4814](https://github.com/EQEmu/Server/pull/4814)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
* Fix Respawn Times Table ([#4802](https://github.com/EQEmu/Server/pull/4802)) @Akkadius 2025-03-29
|
||||||
|
* Wrap PurgeExpiredInstances in a Transaction ([#4824](https://github.com/EQEmu/Server/pull/4824)) @Akkadius 2025-03-30
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* Implement /changename & related script bindings. Clean up #set name ([#4770](https://github.com/EQEmu/Server/pull/4770)) @catapultam-habeo 2025-03-20
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* AllowFVNoDrop Flag trades ([#4809](https://github.com/EQEmu/Server/pull/4809)) @neckkola 2025-03-27
|
||||||
|
* Fix Instance Creation Race Condition ([#4803](https://github.com/EQEmu/Server/pull/4803)) @Akkadius 2025-03-29
|
||||||
|
* Fix zone crash when attempting to add a disappearing client to hate list. ([#4782](https://github.com/EQEmu/Server/pull/4782)) @zimp-wow 2025-03-19
|
||||||
|
* Globally Reloading Quests when not loaded ([#4813](https://github.com/EQEmu/Server/pull/4813)) @Akkadius 2025-03-29
|
||||||
|
* Instance DZ Creation ([#4823](https://github.com/EQEmu/Server/pull/4823)) @Akkadius 2025-03-30
|
||||||
|
* Zone State Entity Variable Load Pre-Spawn ([#4785](https://github.com/EQEmu/Server/pull/4785)) @Akkadius 2025-03-19
|
||||||
|
* Zone State Position Fix ([#4784](https://github.com/EQEmu/Server/pull/4784)) @Akkadius 2025-03-19
|
||||||
|
* Zone State Variables Load First ([#4798](https://github.com/EQEmu/Server/pull/4798)) @Akkadius 2025-03-29
|
||||||
|
* Zone state edge case with 0 hp ([#4787](https://github.com/EQEmu/Server/pull/4787)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Instance
|
||||||
|
|
||||||
|
* Clear Respawn Timers on Creation ([#4801](https://github.com/EQEmu/Server/pull/4801)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Instances
|
||||||
|
|
||||||
|
* Add `expire_at` Column ([#4820](https://github.com/EQEmu/Server/pull/4820)) @Akkadius 2025-03-30
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
* Add several database indexes ([#4811](https://github.com/EQEmu/Server/pull/4811)) @Akkadius 2025-03-29
|
||||||
|
* Have World Send Smarter Guild Updates ([#4796](https://github.com/EQEmu/Server/pull/4796)) @Akkadius 2025-03-29
|
||||||
|
* Improve Character Select DB Performance ([#4799](https://github.com/EQEmu/Server/pull/4799)) @Akkadius 2025-03-29
|
||||||
|
* Reduce Adventure S2S chatter ([#4793](https://github.com/EQEmu/Server/pull/4793)) @Akkadius 2025-03-29
|
||||||
|
* Reduce CorpseOwnerOnline S2S Chatter to World ([#4795](https://github.com/EQEmu/Server/pull/4795)) @Akkadius 2025-03-29
|
||||||
|
* Reduce LFGuild Chatter ([#4794](https://github.com/EQEmu/Server/pull/4794)) @Akkadius 2025-03-29
|
||||||
|
* Reduce UpdateWho S2S Chatter to World ([#4792](https://github.com/EQEmu/Server/pull/4792)) @Akkadius 2025-03-29
|
||||||
|
* Send Smarter Emote Packets ([#4818](https://github.com/EQEmu/Server/pull/4818)) @Akkadius 2025-03-30
|
||||||
|
|
||||||
|
### Quest API
|
||||||
|
|
||||||
|
* Add Support for NPC ID and NPC Name Specificity ([#4781](https://github.com/EQEmu/Server/pull/4781)) @Kinglykrab 2025-03-19
|
||||||
|
|
||||||
|
### Reload
|
||||||
|
|
||||||
|
* Add Reload for Maps / Navs ([#4816](https://github.com/EQEmu/Server/pull/4816)) @Akkadius 2025-03-29
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Zone State Automated Testing and Improvements ([#4808](https://github.com/EQEmu/Server/pull/4808)) @Akkadius 2025-03-30
|
||||||
|
* Zone State Improvements Part 3 ([#4773](https://github.com/EQEmu/Server/pull/4773)) @Akkadius 2025-03-13
|
||||||
|
|
||||||
|
## [23.3.4] 3/14/2025
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Add check for simultaneous direct vendor and parcel Trader/Buyer Purchase ([#4778](https://github.com/EQEmu/Server/pull/4778)) @neckkola 2025-03-14
|
||||||
|
* Fix for rare circumstance where NPC's would have 0 health on restore @Akkadius
|
||||||
|
|
||||||
|
## [23.3.3] 3/13/2025
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
* Add indexes for data_buckets and zone_state_spawns ([#4771](https://github.com/EQEmu/Server/pull/4771)) @Akkadius 2025-03-11
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Update GuildBank to correctly handle items with charges equal to zero ([#4774](https://github.com/EQEmu/Server/pull/4774)) @neckkola 2025-03-13
|
||||||
|
|
||||||
|
### Networking
|
||||||
|
|
||||||
|
* Fix "port in use" error ([#4772](https://github.com/EQEmu/Server/pull/4772)) @Akkadius 2025-03-12
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Zone State Improvements Part 3 ([#4773](https://github.com/EQEmu/Server/pull/4773)) @Akkadius 2025-03-13
|
||||||
|
|
||||||
|
## [23.3.2] 3/11/2025
|
||||||
|
|
||||||
|
### DynamicZones
|
||||||
|
|
||||||
|
* Bulk request dz member statuses on zone boot ([#4769](https://github.com/EQEmu/Server/pull/4769)) @hgtw 2025-03-11
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Zone State Improvements (Continued) ([#4768](https://github.com/EQEmu/Server/pull/4768)) @Akkadius 2025-03-11
|
||||||
|
|
||||||
|
## [23.3.0] 3/8/2025
|
||||||
|
|
||||||
|
### Bots
|
||||||
|
|
||||||
|
* Fix buffs not overwriting lesser buffs ([#4756](https://github.com/EQEmu/Server/pull/4756)) @nytmyr 2025-03-06
|
||||||
|
* Fix taunting bots positioning ([#4754](https://github.com/EQEmu/Server/pull/4754)) @nytmyr 2025-03-06
|
||||||
|
* Move commanded spell map to zone ([#4755](https://github.com/EQEmu/Server/pull/4755)) @nytmyr 2025-03-06
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
* Fix typo in GM tradeskill combine message ([#4762](https://github.com/EQEmu/Server/pull/4762)) @nytmyr 2025-03-08
|
||||||
|
|
||||||
|
### Crash
|
||||||
|
|
||||||
|
* Bot aura crash fix ([#4752](https://github.com/EQEmu/Server/pull/4752)) @nytmyr 2025-03-06
|
||||||
|
|
||||||
|
### Databuckets
|
||||||
|
|
||||||
|
* Nested Databuckets Protections and Improvements ([#4748](https://github.com/EQEmu/Server/pull/4748)) @Akkadius 2025-03-04
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* Add Rule for dealing with augments when an item evolves ([#4758](https://github.com/EQEmu/Server/pull/4758)) @neckkola 2025-03-08
|
||||||
|
* Allow assigning Helm Texture independently of Body Texture for Horses ([#4759](https://github.com/EQEmu/Server/pull/4759)) @catapultam-habeo 2025-03-08
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Add crash checks for certain PlayerEventLogs ([#4761](https://github.com/EQEmu/Server/pull/4761)) @neckkola 2025-03-07
|
||||||
|
* Correct incorrectly calculated stat caps with Heroic Stats ([#4760](https://github.com/EQEmu/Server/pull/4760)) @catapultam-habeo 2025-03-08
|
||||||
|
* Fix sigabort crash from invalid JSON @Akkadius 2025-03-03
|
||||||
|
* Forgot to push up some changes for test output @Akkadius 2025-03-04
|
||||||
|
* Parcel Delivery Updates for two edge cases ([#4753](https://github.com/EQEmu/Server/pull/4753)) @neckkola 2025-03-06
|
||||||
|
* Remove one port check in world @Akkadius 2025-03-03
|
||||||
|
* Zero out currentnpcid whenever spawn is reset. ([#4763](https://github.com/EQEmu/Server/pull/4763)) @zimp-wow 2025-03-08
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
* Convert JSON Error to Data Buckets Logging Category ([#4747](https://github.com/EQEmu/Server/pull/4747)) @Kinglykrab 2025-03-04
|
||||||
|
|
||||||
|
### Pets
|
||||||
|
|
||||||
|
* Fix renamed pets loading as blank names ([#4751](https://github.com/EQEmu/Server/pull/4751)) @nytmyr 2025-03-05
|
||||||
|
|
||||||
|
### Rules
|
||||||
|
|
||||||
|
* Fix EvolvingItems:PercentOfRaidExperience Description ([#4757](https://github.com/EQEmu/Server/pull/4757)) @Kinglykrab 2025-03-07
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
* Cleanup Hand-in Tests ([#4749](https://github.com/EQEmu/Server/pull/4749)) @Akkadius 2025-03-04
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Make zone controller less likely to be visible, immune to all forms of combat ([#4750](https://github.com/EQEmu/Server/pull/4750)) @Akkadius 2025-03-06
|
||||||
|
* State Save Improvements ([#4765](https://github.com/EQEmu/Server/pull/4765)) @Akkadius 2025-03-08
|
||||||
|
|
||||||
|
## [23.2.0] 3/3/2025
|
||||||
|
|
||||||
|
### Crash
|
||||||
|
|
||||||
|
* Database SetMutex crash fix ([#4741](https://github.com/EQEmu/Server/pull/4741)) @Akkadius 2025-03-03
|
||||||
|
* Fix Aura process crash with bots ([#4743](https://github.com/EQEmu/Server/pull/4743)) @Akkadius 2025-03-03
|
||||||
|
* Fix crash in add loot code path ([#4745](https://github.com/EQEmu/Server/pull/4745)) @Akkadius 2025-03-03
|
||||||
|
* Fix world repop crash ([#4742](https://github.com/EQEmu/Server/pull/4742)) @Akkadius 2025-03-03
|
||||||
|
* Potential crash fix in scan close mobs ([#4744](https://github.com/EQEmu/Server/pull/4744)) @Akkadius 2025-03-03
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Cleanup zone buckets on instance purge. ([#4739](https://github.com/EQEmu/Server/pull/4739)) @zimp-wow 2025-03-02
|
||||||
|
* Fix an error causing Endurance Regen to not be applied by items. ([#4738](https://github.com/EQEmu/Server/pull/4738)) @catapultam-habeo 2025-03-02
|
||||||
|
|
||||||
|
### World
|
||||||
|
|
||||||
|
* Check if port in use to avoid double booting mistakes ([#4740](https://github.com/EQEmu/Server/pull/4740)) @Akkadius 2025-03-03
|
||||||
|
|
||||||
|
## [23.1.0] 3/1/2025
|
||||||
|
|
||||||
|
### Bots
|
||||||
|
|
||||||
|
* Fix unresponsive bots in groups upon group wipe ([#4712](https://github.com/EQEmu/Server/pull/4712)) @nytmyr 2025-02-28
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
* More login <-> world code cleanup ([#4724](https://github.com/EQEmu/Server/pull/4724)) @Akkadius 2025-02-28
|
||||||
|
|
||||||
|
### Crash
|
||||||
|
|
||||||
|
* Check for directory existence before traversing in CheckForCompatibleQuestPlugins ([#4730](https://github.com/EQEmu/Server/pull/4730)) @Akkadius 2025-03-02
|
||||||
|
* Fix filesystem crash / exception in DatabaseDumpService::RemoveCredentialsFile() ([#4731](https://github.com/EQEmu/Server/pull/4731)) @Akkadius 2025-03-01
|
||||||
|
* Fix large file size crash in File::GetContents for windows ([#4735](https://github.com/EQEmu/Server/pull/4735)) @Akkadius 2025-03-02
|
||||||
|
* Fix reload concurrency crash when ran from Spire ([#4733](https://github.com/EQEmu/Server/pull/4733)) @Akkadius 2025-03-02
|
||||||
|
* Validate item in SE_SummonItemIntoBag ([#4734](https://github.com/EQEmu/Server/pull/4734)) @Akkadius 2025-03-02
|
||||||
|
* World CLI validation ([#4728](https://github.com/EQEmu/Server/pull/4728)) @Akkadius 2025-03-01
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
* Remove force_interactive from big bag updates ([#4727](https://github.com/EQEmu/Server/pull/4727)) @Akkadius 2025-03-01
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* Add a rule for spells to bypass stacking rules ([#4716](https://github.com/EQEmu/Server/pull/4716)) @catapultam-habeo 2025-02-28
|
||||||
|
* Evolving items Additions ([#4725](https://github.com/EQEmu/Server/pull/4725)) @neckkola 2025-03-01
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Add character_pet_name to player tables schema @Akkadius 2025-03-02
|
||||||
|
* Add client packets to questmanager:setguild ([#4732](https://github.com/EQEmu/Server/pull/4732)) @neckkola 2025-03-01
|
||||||
|
* Clear m_completed_shared_tasks before reloading @Akkadius 2025-02-24
|
||||||
|
* Fix AA Reset Error Message ([#4720](https://github.com/EQEmu/Server/pull/4720)) @Kinglykrab 2025-02-28
|
||||||
|
* Fix Issue with Suffixes/Prefixes ([#4723](https://github.com/EQEmu/Server/pull/4723)) @Kinglykrab 2025-02-28
|
||||||
|
* Fix Trading Items to Bot Pets ([#4721](https://github.com/EQEmu/Server/pull/4721)) @MortimerGreenwald 2025-02-28
|
||||||
|
* Refactor ApplyItemBonuses to fix double-counting of ATK and recommended levels not correctly applying ([#4713](https://github.com/EQEmu/Server/pull/4713)) @catapultam-habeo 2025-03-01
|
||||||
|
|
||||||
|
### Loginserver
|
||||||
|
|
||||||
|
* Minor cleanup ([#4729](https://github.com/EQEmu/Server/pull/4729)) @Akkadius 2025-03-01
|
||||||
|
|
||||||
|
### Quest API
|
||||||
|
|
||||||
|
* Add Key Ring Methods to Perl and Lua ([#4719](https://github.com/EQEmu/Server/pull/4719)) @Kinglykrab 2025-02-28
|
||||||
|
* Implement eq.handin() and quest::handin() ([#4718](https://github.com/EQEmu/Server/pull/4718)) @Akkadius 2025-02-28
|
||||||
|
|
||||||
|
### Tasks
|
||||||
|
|
||||||
|
* Extend IsTaskCompleted to also be aware of shared task completion ([#4714](https://github.com/EQEmu/Server/pull/4714)) @Akkadius 2025-02-24
|
||||||
|
|
||||||
|
### Zone
|
||||||
|
|
||||||
|
* Implement Zone State Saving on Shutdown ([#4715](https://github.com/EQEmu/Server/pull/4715)) @Akkadius 2025-02-28
|
||||||
|
|
||||||
|
### Zone State
|
||||||
|
|
||||||
|
* Wrap all serialization/deserialization in try/catch ([#4726](https://github.com/EQEmu/Server/pull/4726)) @Akkadius 2025-03-01
|
||||||
|
|
||||||
## [23.0.2] 2/21/2025
|
## [23.0.2] 2/21/2025
|
||||||
|
|
||||||
### Bots
|
### Bots
|
||||||
|
|||||||
+38
-1
@@ -245,7 +245,7 @@ uint32 Database::CreateAccount(
|
|||||||
e.password = password;
|
e.password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogInfo("Account Attempting to be created: [{}:{}] status: {}", loginserver, name, status);
|
LogInfo("Account attempting to be created loginserver [{}] name [{}] status [{}]", loginserver, name, status);
|
||||||
|
|
||||||
e = AccountRepository::InsertOne(*this, e);
|
e = AccountRepository::InsertOne(*this, e);
|
||||||
|
|
||||||
@@ -955,6 +955,29 @@ bool Database::UpdateName(const std::string& old_name, const std::string& new_na
|
|||||||
return CharacterDataRepository::UpdateOne(*this, e);
|
return CharacterDataRepository::UpdateOne(*this, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Database::UpdateNameByID(const int character_id, const std::string& new_name)
|
||||||
|
{
|
||||||
|
LogInfo("Renaming [{}] to [{}]", character_id, new_name);
|
||||||
|
|
||||||
|
auto l = CharacterDataRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`id` = {}",
|
||||||
|
character_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (l.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& e = l.front();
|
||||||
|
|
||||||
|
e.name = new_name;
|
||||||
|
|
||||||
|
return CharacterDataRepository::UpdateOne(*this, e);
|
||||||
|
}
|
||||||
|
|
||||||
bool Database::IsNameUsed(const std::string& name)
|
bool Database::IsNameUsed(const std::string& name)
|
||||||
{
|
{
|
||||||
if (RuleB(Bots, Enabled)) {
|
if (RuleB(Bots, Enabled)) {
|
||||||
@@ -982,6 +1005,20 @@ bool Database::IsNameUsed(const std::string& name)
|
|||||||
return !character_data.empty();
|
return !character_data.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Players cannot have the same name as a pet vanity name, or memory corruption occurs.
|
||||||
|
bool Database::IsPetNameUsed(const std::string& name)
|
||||||
|
{
|
||||||
|
const auto& pet_name_data = CharacterPetNameRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`name` = '{}'",
|
||||||
|
Strings::Escape(name)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return !pet_name_data.empty();
|
||||||
|
}
|
||||||
|
|
||||||
uint32 Database::GetServerType()
|
uint32 Database::GetServerType()
|
||||||
{
|
{
|
||||||
const auto& l = VariablesRepository::GetWhere(*this, "`varname` = 'ServerType' LIMIT 1");
|
const auto& l = VariablesRepository::GetWhere(*this, "`varname` = 'ServerType' LIMIT 1");
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ public:
|
|||||||
bool ReserveName(uint32 account_id, const std::string& name);
|
bool ReserveName(uint32 account_id, const std::string& name);
|
||||||
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp);
|
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp);
|
||||||
bool UpdateName(const std::string& old_name, const std::string& new_name);
|
bool UpdateName(const std::string& old_name, const std::string& new_name);
|
||||||
|
bool UpdateNameByID(const int character_id, const std::string& new_name);
|
||||||
bool CopyCharacter(
|
bool CopyCharacter(
|
||||||
const std::string& source_character_name,
|
const std::string& source_character_name,
|
||||||
const std::string& destination_character_name,
|
const std::string& destination_character_name,
|
||||||
@@ -116,6 +117,7 @@ public:
|
|||||||
bool CheckGMIPs(const std::string& login_ip, uint32 account_id);
|
bool CheckGMIPs(const std::string& login_ip, uint32 account_id);
|
||||||
bool CheckNameFilter(const std::string& name, bool surname = false);
|
bool CheckNameFilter(const std::string& name, bool surname = false);
|
||||||
bool IsNameUsed(const std::string& name);
|
bool IsNameUsed(const std::string& name);
|
||||||
|
bool IsPetNameUsed(const std::string& name);
|
||||||
|
|
||||||
uint32 GetAccountIDByChar(const std::string& name, uint32* character_id = 0);
|
uint32 GetAccountIDByChar(const std::string& name, uint32* character_id = 0);
|
||||||
uint32 GetAccountIDByChar(uint32 character_id);
|
uint32 GetAccountIDByChar(uint32 character_id);
|
||||||
@@ -139,6 +141,7 @@ public:
|
|||||||
bool CheckInstanceExpired(uint16 instance_id);
|
bool CheckInstanceExpired(uint16 instance_id);
|
||||||
bool CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration);
|
bool CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration);
|
||||||
bool GetUnusedInstanceID(uint16& instance_id);
|
bool GetUnusedInstanceID(uint16& instance_id);
|
||||||
|
bool TryGetUnusedInstanceID(uint16& instance_id);
|
||||||
bool IsGlobalInstance(uint16 instance_id);
|
bool IsGlobalInstance(uint16 instance_id);
|
||||||
bool RemoveClientFromInstance(uint16 instance_id, uint32 char_id);
|
bool RemoveClientFromInstance(uint16 instance_id, uint32 char_id);
|
||||||
bool RemoveClientsFromInstance(uint16 instance_id);
|
bool RemoveClientsFromInstance(uint16 instance_id);
|
||||||
|
|||||||
@@ -596,8 +596,13 @@ void DatabaseDumpService::BuildCredentialsFile()
|
|||||||
void DatabaseDumpService::RemoveCredentialsFile()
|
void DatabaseDumpService::RemoveCredentialsFile()
|
||||||
{
|
{
|
||||||
if (File::Exists(CREDENTIALS_FILE)) {
|
if (File::Exists(CREDENTIALS_FILE)) {
|
||||||
|
try {
|
||||||
std::filesystem::remove(CREDENTIALS_FILE);
|
std::filesystem::remove(CREDENTIALS_FILE);
|
||||||
}
|
}
|
||||||
|
catch (std::exception &e) {
|
||||||
|
LogError("std::filesystem::remove err [{}]", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DatabaseDumpService::IsDumpStaticInstanceData()
|
bool DatabaseDumpService::IsDumpStaticInstanceData()
|
||||||
|
|||||||
@@ -6417,7 +6417,7 @@ ADD COLUMN `guid` bigint(20) UNSIGNED NOT NULL DEFAULT 0 AFTER `ornament_hero_mo
|
|||||||
ADD PRIMARY KEY (`account_id`, `slot_id`);
|
ADD PRIMARY KEY (`account_id`, `slot_id`);
|
||||||
)",
|
)",
|
||||||
.content_schema_update = false,
|
.content_schema_update = false,
|
||||||
.force_interactive = true
|
.force_interactive = false
|
||||||
},
|
},
|
||||||
ManifestEntry{
|
ManifestEntry{
|
||||||
.version = 9298,
|
.version = 9298,
|
||||||
@@ -6481,7 +6481,7 @@ UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2531) + 11010) WHERE `slot_id`
|
|||||||
UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2
|
UPDATE `sharedbank` SET `slot_id` = ((`slot_id` - 2541) + 11210) WHERE `slot_id` BETWEEN 2541 AND 2550; -- Shared Bank Bag 2
|
||||||
)",
|
)",
|
||||||
.content_schema_update = false,
|
.content_schema_update = false,
|
||||||
.force_interactive = true
|
.force_interactive = false
|
||||||
},
|
},
|
||||||
ManifestEntry{
|
ManifestEntry{
|
||||||
.version = 9299,
|
.version = 9299,
|
||||||
@@ -6792,7 +6792,7 @@ UPDATE `character_corpse_items` SET `equip_slot` = ((`equip_slot` - 341) + 5810)
|
|||||||
},
|
},
|
||||||
ManifestEntry{
|
ManifestEntry{
|
||||||
.version = 9304,
|
.version = 9304,
|
||||||
.description = "2024_12_01_2024_update_guild_bank",
|
.description = "2024_12_01_update_guild_bank",
|
||||||
.check = "SHOW COLUMNS FROM `guild_bank` LIKE 'augment_one_id'",
|
.check = "SHOW COLUMNS FROM `guild_bank` LIKE 'augment_one_id'",
|
||||||
.condition = "empty",
|
.condition = "empty",
|
||||||
.match = "",
|
.match = "",
|
||||||
@@ -6845,7 +6845,7 @@ RENAME TABLE `expedition_lockouts` TO `dynamic_zone_lockouts`;
|
|||||||
.condition = "empty",
|
.condition = "empty",
|
||||||
.match = "",
|
.match = "",
|
||||||
.sql = R"(
|
.sql = R"(
|
||||||
-- ✅ Drop old indexes
|
-- Drop old indexes if exists
|
||||||
DROP INDEX IF EXISTS `keys` ON `data_buckets`;
|
DROP INDEX IF EXISTS `keys` ON `data_buckets`;
|
||||||
DROP INDEX IF EXISTS `idx_npc_expires` ON `data_buckets`;
|
DROP INDEX IF EXISTS `idx_npc_expires` ON `data_buckets`;
|
||||||
DROP INDEX IF EXISTS `idx_bot_expires` ON `data_buckets`;
|
DROP INDEX IF EXISTS `idx_bot_expires` ON `data_buckets`;
|
||||||
@@ -6863,11 +6863,227 @@ ALTER TABLE `data_buckets`
|
|||||||
MODIFY COLUMN `npc_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`,
|
MODIFY COLUMN `npc_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`,
|
||||||
MODIFY COLUMN `bot_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `npc_id`;
|
MODIFY COLUMN `bot_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `npc_id`;
|
||||||
|
|
||||||
-- ✅ Create optimized unique index with `key` first
|
-- Create optimized unique index with `key` first
|
||||||
CREATE UNIQUE INDEX `keys` ON data_buckets (`key`, character_id, npc_id, bot_id, account_id, zone_id, instance_id);
|
CREATE UNIQUE INDEX `keys` ON data_buckets (`key`, character_id, npc_id, bot_id, account_id, zone_id, instance_id);
|
||||||
|
|
||||||
-- ✅ Create indexes for just instance_id (instance deletion)
|
-- Create indexes for just instance_id (instance deletion)
|
||||||
CREATE INDEX idx_instance_id ON data_buckets (instance_id);
|
CREATE INDEX idx_instance_id ON data_buckets (instance_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9307,
|
||||||
|
.description = "2025_02_17_zone_state_spawns.sql",
|
||||||
|
.check = "SHOW TABLES LIKE 'zone_state_spawns'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
CREATE TABLE `zone_state_spawns` (
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||||
|
`zone_id` int(11) unsigned DEFAULT NULL,
|
||||||
|
`instance_id` int(11) unsigned DEFAULT NULL,
|
||||||
|
`is_corpse` tinyint(11) DEFAULT 0,
|
||||||
|
`decay_in_seconds` int(11) DEFAULT 0,
|
||||||
|
`npc_id` int(10) unsigned DEFAULT NULL,
|
||||||
|
`spawn2_id` int(10) unsigned NOT NULL,
|
||||||
|
`spawngroup_id` int(10) unsigned NOT NULL,
|
||||||
|
`x` float NOT NULL,
|
||||||
|
`y` float NOT NULL,
|
||||||
|
`z` float NOT NULL,
|
||||||
|
`heading` float NOT NULL,
|
||||||
|
`respawn_time` int(10) unsigned NOT NULL,
|
||||||
|
`variance` int(10) unsigned NOT NULL,
|
||||||
|
`grid` int(10) unsigned DEFAULT 0,
|
||||||
|
`current_waypoint` int(11) DEFAULT 0,
|
||||||
|
`path_when_zone_idle` smallint(6) DEFAULT 0,
|
||||||
|
`condition_id` smallint(5) unsigned DEFAULT 0,
|
||||||
|
`condition_min_value` smallint(6) DEFAULT 0,
|
||||||
|
`enabled` smallint(6) DEFAULT 1,
|
||||||
|
`anim` smallint(5) unsigned DEFAULT 0,
|
||||||
|
`loot_data` text DEFAULT NULL,
|
||||||
|
`entity_variables` text DEFAULT NULL,
|
||||||
|
`buffs` text DEFAULT NULL,
|
||||||
|
`hp` bigint(20) DEFAULT 0,
|
||||||
|
`mana` bigint(20) DEFAULT 0,
|
||||||
|
`endurance` bigint(20) DEFAULT 0,
|
||||||
|
`created_at` datetime DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9308,
|
||||||
|
.description = "2025_03_29_add_multivalue_support_to_evolving_subtype.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `items_evolving_details` LIKE 'sub_type'",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "varchar(200)",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `items_evolving_details`
|
||||||
|
CHANGE COLUMN `sub_type` `sub_type` VARCHAR(200) NULL DEFAULT '0' AFTER `type`;
|
||||||
|
)",
|
||||||
|
.content_schema_update = true
|
||||||
|
},
|
||||||
|
// this one got missed being added to PEQ dumps so adding it again so it gets added when folks take a new release
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9309,
|
||||||
|
.description = "2025_03_1_create_pet_names_table_if_not_exist.sql",
|
||||||
|
.check = "SHOW TABLES LIKE 'character_pet_name'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
CREATE TABLE `character_pet_name` (
|
||||||
|
`character_id` INT(11) NOT NULL PRIMARY KEY,
|
||||||
|
`name` VARCHAR(64) NOT NULL
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||||
|
)",
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9310,
|
||||||
|
.description = "2025_03_7_expand_horse_def.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `horses` LIKE 'helmtexture'",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "TINYINT(2)",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `horses`
|
||||||
|
ADD COLUMN `helmtexture` TINYINT(2) NOT NULL DEFAULT -1 AFTER `texture`;
|
||||||
|
)",
|
||||||
|
.content_schema_update = true
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9311,
|
||||||
|
.description = "2025_03_09_add_zone_state_is_zone_field.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `zone_state_spawns` LIKE 'is_zone'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `zone_state_spawns`
|
||||||
|
ADD COLUMN `is_zone` tinyint(11) NULL DEFAULT 0 AFTER `is_corpse`;
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9312,
|
||||||
|
.description = "2025_03_11_data_bucket_indexes.sql",
|
||||||
|
.check = "SHOW INDEX FROM data_buckets",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_zone_instance_expires",
|
||||||
|
.sql = R"(
|
||||||
|
DROP INDEX IF EXISTS `idx_zone_instance_expires` ON `data_buckets`;
|
||||||
|
DROP INDEX IF EXISTS `idx_character_expires` ON `data_buckets`;
|
||||||
|
DROP INDEX IF EXISTS `idx_bot_expires` ON `data_buckets`;
|
||||||
|
ALTER TABLE data_buckets ADD INDEX idx_zone_instance_expires (zone_id, instance_id, expires);
|
||||||
|
ALTER TABLE data_buckets ADD INDEX idx_character_expires (character_id, expires);
|
||||||
|
ALTER TABLE data_buckets ADD INDEX idx_bot_expires (bot_id, expires);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9313,
|
||||||
|
.description = "2025_03_11_zone_state_spawns.sql",
|
||||||
|
.check = "SHOW INDEX FROM zone_state_spawns",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_zone_instance",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE zone_state_spawns ADD INDEX idx_zone_instance (zone_id, instance_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9314,
|
||||||
|
.description = "2025_03_12_zone_state_spawns_one_time_truncate.sql",
|
||||||
|
.check = "SELECT * FROM db_version WHERE version >= 9314",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
TRUNCATE TABLE zone_state_spawns;
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9315,
|
||||||
|
.description = "2025_03_29_character_tribute_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM character_tribute",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_character_id",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE character_tribute ADD INDEX idx_character_id (character_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9316,
|
||||||
|
.description = "2025_03_29_player_titlesets_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM player_titlesets",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_char_id",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE player_titlesets ADD INDEX idx_char_id (char_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9317,
|
||||||
|
.description = "2025_03_29_respawn_times_instance_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM respawn_times",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_instance_id",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE respawn_times ADD INDEX idx_instance_id (instance_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9318,
|
||||||
|
.description = "2025_03_29_zone_state_spawns_instance_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM zone_state_spawns",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_instance_id",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE zone_state_spawns ADD INDEX idx_instance_id (instance_id);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9319,
|
||||||
|
.description = "2025_03_29_data_buckets_expires_index.sql",
|
||||||
|
.check = "SHOW INDEX FROM data_buckets",
|
||||||
|
.condition = "missing",
|
||||||
|
.match = "idx_expires",
|
||||||
|
.sql = R"(
|
||||||
|
CREATE INDEX idx_expires ON data_buckets (expires);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9320,
|
||||||
|
.description = "2025_03_23_add_respawn_times_expire_at.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `respawn_times` LIKE 'expire_at'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `respawn_times`
|
||||||
|
ADD COLUMN `expire_at` int(11) UNSIGNED NULL DEFAULT 0 AFTER `duration`;
|
||||||
|
|
||||||
|
UPDATE respawn_times set expire_at = `start` + `duration`; -- backfill existing data
|
||||||
|
|
||||||
|
CREATE INDEX `idx_expire_at` ON `respawn_times` (`expire_at`);
|
||||||
|
)",
|
||||||
|
.content_schema_update = false
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9321,
|
||||||
|
.description = "2025_03_30_instance_list_add_expire_at.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `instance_list` LIKE 'expire_at'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `instance_list`
|
||||||
|
ADD COLUMN `expire_at` bigint(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `duration`;
|
||||||
|
|
||||||
|
UPDATE instance_list set expire_at = `start_time` + `duration`; -- backfill existing data
|
||||||
|
|
||||||
|
CREATE INDEX `idx_expire_at` ON `instance_list` (`expire_at`);
|
||||||
)",
|
)",
|
||||||
.content_schema_update = false
|
.content_schema_update = false
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "../common/repositories/spawn_condition_values_repository.h"
|
#include "../common/repositories/spawn_condition_values_repository.h"
|
||||||
#include "repositories/spawn2_disabled_repository.h"
|
#include "repositories/spawn2_disabled_repository.h"
|
||||||
#include "repositories/data_buckets_repository.h"
|
#include "repositories/data_buckets_repository.h"
|
||||||
|
#include "repositories/zone_state_spawns_repository.h"
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
@@ -128,11 +128,35 @@ bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version
|
|||||||
e.version = version;
|
e.version = version;
|
||||||
e.start_time = std::time(nullptr);
|
e.start_time = std::time(nullptr);
|
||||||
e.duration = duration;
|
e.duration = duration;
|
||||||
|
e.expire_at = e.start_time + duration;
|
||||||
|
|
||||||
return InstanceListRepository::InsertOne(*this, e).id;
|
RespawnTimesRepository::ClearInstanceTimers(*this, e.id);
|
||||||
|
InstanceListRepository::ReplaceOne(*this, e);
|
||||||
|
return instance_id > 0 && e.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Database::GetUnusedInstanceID(uint16 &instance_id)
|
bool Database::GetUnusedInstanceID(uint16 &instance_id)
|
||||||
|
{
|
||||||
|
// attempt to get an unused instance id
|
||||||
|
for (int a = 0; a < 10; a++) {
|
||||||
|
uint16 attempted_id = 0;
|
||||||
|
if (TryGetUnusedInstanceID(attempted_id)) {
|
||||||
|
auto i = InstanceListRepository::NewEntity();
|
||||||
|
i.id = attempted_id;
|
||||||
|
i.notes = "Prefetching";
|
||||||
|
auto n = InstanceListRepository::InsertOne(*this, i);
|
||||||
|
if (n.id > 0) {
|
||||||
|
instance_id = n.id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
instance_id = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Database::TryGetUnusedInstanceID(uint16 &instance_id)
|
||||||
{
|
{
|
||||||
uint32 max_reserved_instance_id = RuleI(Instances, ReservedInstances);
|
uint32 max_reserved_instance_id = RuleI(Instances, ReservedInstances);
|
||||||
uint32 max_instance_id = 32000;
|
uint32 max_instance_id = 32000;
|
||||||
@@ -480,6 +504,9 @@ void Database::DeleteInstance(uint16 instance_id)
|
|||||||
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
||||||
CharacterCorpsesRepository::BuryInstance(*this, instance_id);
|
CharacterCorpsesRepository::BuryInstance(*this, instance_id);
|
||||||
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id = {}", instance_id));
|
||||||
|
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||||
|
ZoneStateSpawnsRepository::DeleteWhere(*this, fmt::format("`instance_id` = {}", instance_id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 group_id)
|
void Database::FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 group_id)
|
||||||
@@ -534,14 +561,12 @@ void Database::GetCharactersInInstance(uint16 instance_id, std::list<uint32> &ch
|
|||||||
|
|
||||||
void Database::PurgeExpiredInstances()
|
void Database::PurgeExpiredInstances()
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Delay purging by a day so that we can continue using adjacent free instance id's
|
|
||||||
* from the table without risking the chance we immediately re-allocate a zone that freshly expired but
|
|
||||||
* has not been fully de-allocated
|
|
||||||
*/
|
|
||||||
auto l = InstanceListRepository::GetWhere(
|
auto l = InstanceListRepository::GetWhere(
|
||||||
*this,
|
*this,
|
||||||
"(start_time + duration) <= (UNIX_TIMESTAMP() - 86400) AND never_expires = 0"
|
fmt::format(
|
||||||
|
"expire_at <= (UNIX_TIMESTAMP() - {}) and expire_at != 0 AND never_expires = 0",
|
||||||
|
RuleI(Instances, ExpireOffsetTimeSeconds)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
if (l.empty()) {
|
if (l.empty()) {
|
||||||
return;
|
return;
|
||||||
@@ -552,16 +577,24 @@ void Database::PurgeExpiredInstances()
|
|||||||
instance_ids.emplace_back(std::to_string(e.id));
|
instance_ids.emplace_back(std::to_string(e.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto imploded_instance_ids = Strings::Implode(",", instance_ids);
|
const auto ids = Strings::Implode(",", instance_ids);
|
||||||
|
|
||||||
InstanceListRepository::DeleteWhere(*this, fmt::format("id IN ({})", imploded_instance_ids));
|
TransactionBegin();
|
||||||
InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id IN ({})", imploded_instance_ids));
|
InstanceListRepository::DeleteWhere(*this, fmt::format("id IN ({})", ids));
|
||||||
RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
InstanceListPlayerRepository::DeleteWhere(*this, fmt::format("id IN ({})", ids));
|
||||||
SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
RespawnTimesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||||
CharacterCorpsesRepository::BuryInstances(*this, imploded_instance_ids);
|
SpawnConditionValuesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||||
DynamicZoneMembersRepository::DeleteByManyInstances(*this, imploded_instance_ids);
|
CharacterCorpsesRepository::BuryInstances(*this, ids);
|
||||||
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
DynamicZoneMembersRepository::DeleteByManyInstances(*this, ids);
|
||||||
Spawn2DisabledRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", imploded_instance_ids));
|
DynamicZonesRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||||
|
Spawn2DisabledRepository::DeleteWhere(*this, fmt::format("instance_id IN ({})", ids));
|
||||||
|
DataBucketsRepository::DeleteWhere(*this, fmt::format("instance_id != 0 and instance_id IN ({})", ids));
|
||||||
|
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||||
|
ZoneStateSpawnsRepository::DeleteWhere(*this, fmt::format("`instance_id` IN ({})", ids));
|
||||||
|
}
|
||||||
|
TransactionCommit();
|
||||||
|
|
||||||
|
LogInfo("Purged [{}] expired instances", l.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
||||||
@@ -573,6 +606,7 @@ void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration)
|
|||||||
|
|
||||||
i.start_time = std::time(nullptr);
|
i.start_time = std::time(nullptr);
|
||||||
i.duration = new_duration;
|
i.duration = new_duration;
|
||||||
|
i.expire_at = i.start_time + i.duration;
|
||||||
|
|
||||||
InstanceListRepository::UpdateOne(*this, i);
|
InstanceListRepository::UpdateOne(*this, i);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ namespace DatabaseSchema {
|
|||||||
"character_pet_buffs",
|
"character_pet_buffs",
|
||||||
"character_pet_info",
|
"character_pet_info",
|
||||||
"character_pet_inventory",
|
"character_pet_inventory",
|
||||||
|
"character_pet_name",
|
||||||
"character_peqzone_flags",
|
"character_peqzone_flags",
|
||||||
"character_potionbelt",
|
"character_potionbelt",
|
||||||
"character_skills",
|
"character_skills",
|
||||||
@@ -350,6 +351,7 @@ namespace DatabaseSchema {
|
|||||||
"shared_task_dynamic_zones",
|
"shared_task_dynamic_zones",
|
||||||
"shared_task_members",
|
"shared_task_members",
|
||||||
"shared_tasks",
|
"shared_tasks",
|
||||||
|
"zone_state_spawns",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -302,7 +302,9 @@ std::string DBcore::Escape(const std::string& s)
|
|||||||
|
|
||||||
void DBcore::SetMutex(Mutex *mutex)
|
void DBcore::SetMutex(Mutex *mutex)
|
||||||
{
|
{
|
||||||
|
if (m_mutex && m_mutex != mutex) {
|
||||||
safe_delete(m_mutex);
|
safe_delete(m_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
DBcore::m_mutex = mutex;
|
DBcore::m_mutex = mutex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,15 +58,16 @@ uint32_t DynamicZoneBase::CreateInstance()
|
|||||||
insert_instance.start_time = static_cast<int>(std::chrono::system_clock::to_time_t(m_start_time));
|
insert_instance.start_time = static_cast<int>(std::chrono::system_clock::to_time_t(m_start_time));
|
||||||
insert_instance.duration = static_cast<int>(m_duration.count());
|
insert_instance.duration = static_cast<int>(m_duration.count());
|
||||||
insert_instance.never_expires = m_never_expires;
|
insert_instance.never_expires = m_never_expires;
|
||||||
|
insert_instance.expire_at = insert_instance.start_time + insert_instance.duration;
|
||||||
|
|
||||||
auto instance = InstanceListRepository::InsertOne(GetDatabase(), insert_instance);
|
auto instance = InstanceListRepository::ReplaceOne(GetDatabase(), insert_instance);
|
||||||
if (instance.id == 0)
|
if (!instance)
|
||||||
{
|
{
|
||||||
LogDynamicZones("Failed to create instance [{}] for zone [{}]", unused_instance_id, m_zone_id);
|
LogDynamicZones("Failed to create instance [{}] for zone [{}]", unused_instance_id, m_zone_id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_instance_id = instance.id;
|
m_instance_id = unused_instance_id;
|
||||||
|
|
||||||
return m_instance_id;
|
return m_instance_id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,6 +287,8 @@ N(OP_InstillDoubt),
|
|||||||
N(OP_InterruptCast),
|
N(OP_InterruptCast),
|
||||||
N(OP_InvokeChangePetName),
|
N(OP_InvokeChangePetName),
|
||||||
N(OP_InvokeChangePetNameImmediate),
|
N(OP_InvokeChangePetNameImmediate),
|
||||||
|
N(OP_InvokeNameChangeImmediate),
|
||||||
|
N(OP_InvokeNameChangeLazy),
|
||||||
N(OP_ItemLinkClick),
|
N(OP_ItemLinkClick),
|
||||||
N(OP_ItemLinkResponse),
|
N(OP_ItemLinkResponse),
|
||||||
N(OP_ItemLinkText),
|
N(OP_ItemLinkText),
|
||||||
|
|||||||
+17
-10
@@ -47,7 +47,7 @@ static const uint32 ADVANCED_LORE_LENGTH = 8192;
|
|||||||
*/
|
*/
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
|
|
||||||
struct LoginInfo_Struct {
|
struct LoginInfo {
|
||||||
/*000*/ char login_info[64];
|
/*000*/ char login_info[64];
|
||||||
/*064*/ uint8 unknown064[124];
|
/*064*/ uint8 unknown064[124];
|
||||||
/*188*/ uint8 zoning; // 01 if zoning, 00 if not
|
/*188*/ uint8 zoning; // 01 if zoning, 00 if not
|
||||||
@@ -5832,21 +5832,28 @@ struct ChangeSize_Struct
|
|||||||
/*16*/
|
/*16*/
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ChangeNameResponse : int {
|
||||||
|
Denied = 0, // 5167: "You have requested an invalid name or a Customer Service Representative has denied your name request. Please try another name."
|
||||||
|
Accepted = 1, // 5976: "Your request for a name change was successful."
|
||||||
|
Timeout = -1, // 5977: "Your request for a name change has timed out. Please try again later."
|
||||||
|
ServerError = -2, // 5978: "The server had an error while processing your name request. Please try again later."
|
||||||
|
RateLimited = -3, // 5979: "You must wait longer before submitting another name request. Please try again in a few minutes."
|
||||||
|
Ineligible = -4, // 5980: "Your character is not eligible for a name change."
|
||||||
|
Pending = -5 // 5193: "You already have a name change pending. Please wait until it is fully processed before attempting another name change."
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AltChangeName_Struct {
|
||||||
|
/*00*/ char new_name[64];
|
||||||
|
/*40*/ char old_name[64];
|
||||||
|
/*80*/ int response_code;
|
||||||
|
};
|
||||||
|
|
||||||
struct ChangePetName_Struct {
|
struct ChangePetName_Struct {
|
||||||
/*00*/ char new_pet_name[64];
|
/*00*/ char new_pet_name[64];
|
||||||
/*40*/ char pet_owner_name[64];
|
/*40*/ char pet_owner_name[64];
|
||||||
/*80*/ int response_code;
|
/*80*/ int response_code;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ChangePetNameResponse : int {
|
|
||||||
Denied = 0, // 5167 You have requested an invalid name or a Customer Service Representative has denied your name request. Please try another name.
|
|
||||||
Accepted = 1, // 5976 Your request for a name change was successful.
|
|
||||||
Timeout = -3, // 5979 You must wait longer before submitting another name request. Please try again in a few minutes.
|
|
||||||
NotEligible = -4, // 5980 Your character is not eligible for a name change.
|
|
||||||
Pending = -5, // 5193 You already have a name change pending. Please wait until it is fully processed before attempting another name change.
|
|
||||||
Unhandled = -1
|
|
||||||
};
|
|
||||||
|
|
||||||
// New OpCode/Struct for SoD+
|
// New OpCode/Struct for SoD+
|
||||||
struct GroupMakeLeader_Struct
|
struct GroupMakeLeader_Struct
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -614,7 +614,7 @@ void EQEmuLogSys::EnableConsoleLogging()
|
|||||||
std::copy(std::begin(pre_silence_settings), std::end(pre_silence_settings), std::begin(log_settings));
|
std::copy(std::begin(pre_silence_settings), std::end(pre_silence_settings), std::begin(log_settings));
|
||||||
}
|
}
|
||||||
|
|
||||||
EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings(bool silent_load)
|
||||||
{
|
{
|
||||||
InjectTablesIfNotExist();
|
InjectTablesIfNotExist();
|
||||||
|
|
||||||
@@ -699,6 +699,10 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (silent_load) {
|
||||||
|
SilenceConsoleLogging();
|
||||||
|
}
|
||||||
|
|
||||||
LogInfo("Loaded [{}] log categories", categories.size());
|
LogInfo("Loaded [{}] log categories", categories.size());
|
||||||
|
|
||||||
auto webhooks = DiscordWebhooksRepository::GetWhere(*m_database, fmt::format("id < {}", MAX_DISCORD_WEBHOOK_ID));
|
auto webhooks = DiscordWebhooksRepository::GetWhere(*m_database, fmt::format("id < {}", MAX_DISCORD_WEBHOOK_ID));
|
||||||
@@ -716,6 +720,10 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
|||||||
log_settings[Logs::Info].log_to_file = static_cast<uint8>(Logs::General);
|
log_settings[Logs::Info].log_to_file = static_cast<uint8>(Logs::General);
|
||||||
log_settings[Logs::Info].log_to_console = static_cast<uint8>(Logs::General);
|
log_settings[Logs::Info].log_to_console = static_cast<uint8>(Logs::General);
|
||||||
|
|
||||||
|
if (silent_load) {
|
||||||
|
SilenceConsoleLogging();
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ namespace Logs {
|
|||||||
BotSpellChecks,
|
BotSpellChecks,
|
||||||
BotSpellTypeChecks,
|
BotSpellTypeChecks,
|
||||||
NpcHandin,
|
NpcHandin,
|
||||||
|
ZoneState,
|
||||||
MaxCategoryID /* Don't Remove this */
|
MaxCategoryID /* Don't Remove this */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -256,7 +257,8 @@ namespace Logs {
|
|||||||
"Bot Settings",
|
"Bot Settings",
|
||||||
"Bot Spell Checks",
|
"Bot Spell Checks",
|
||||||
"Bot Spell Type Checks",
|
"Bot Spell Type Checks",
|
||||||
"NpcHandin"
|
"NpcHandin",
|
||||||
|
"ZoneState"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +279,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
void CloseFileLogs();
|
void CloseFileLogs();
|
||||||
EQEmuLogSys *LoadLogSettingsDefaults();
|
EQEmuLogSys *LoadLogSettingsDefaults();
|
||||||
EQEmuLogSys *LoadLogDatabaseSettings();
|
EQEmuLogSys *LoadLogDatabaseSettings(bool silent_load = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param directory_name
|
* @param directory_name
|
||||||
|
|||||||
@@ -914,6 +914,16 @@
|
|||||||
OutF(LogSys, Logs::Detail, Logs::NpcHandin, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
OutF(LogSys, Logs::Detail, Logs::NpcHandin, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define LogZoneState(message, ...) do {\
|
||||||
|
if (LogSys.IsLogEnabled(Logs::General, Logs::ZoneState))\
|
||||||
|
OutF(LogSys, Logs::General, Logs::ZoneState, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define LogZoneStateDetail(message, ...) do {\
|
||||||
|
if (LogSys.IsLogEnabled(Logs::Detail, Logs::ZoneState))\
|
||||||
|
OutF(LogSys, Logs::Detail, Logs::ZoneState, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define Log(debug_level, log_category, message, ...) do {\
|
#define Log(debug_level, log_category, message, ...) do {\
|
||||||
if (LogSys.IsLogEnabled(debug_level, log_category))\
|
if (LogSys.IsLogEnabled(debug_level, log_category))\
|
||||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||||
|
|||||||
+12
-13
@@ -39,6 +39,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
@@ -90,23 +91,21 @@ std::string File::GetCwd()
|
|||||||
|
|
||||||
FileContentsResult File::GetContents(const std::string &file_name)
|
FileContentsResult File::GetContents(const std::string &file_name)
|
||||||
{
|
{
|
||||||
std::string error;
|
std::ifstream f(file_name, std::ios::in | std::ios::binary);
|
||||||
std::ifstream f;
|
if (!f) {
|
||||||
f.open(file_name);
|
return { .error = fmt::format("Couldn't open file [{}]", file_name) };
|
||||||
std::string line;
|
}
|
||||||
|
|
||||||
|
constexpr size_t CHUNK_SIZE = 4096; // Read 4KB chunks
|
||||||
std::string lines;
|
std::string lines;
|
||||||
if (f.is_open()) {
|
std::vector<char> buffer(CHUNK_SIZE);
|
||||||
while (f) {
|
|
||||||
std::getline(f, line);
|
while (f.read(buffer.data(), CHUNK_SIZE) || f.gcount() > 0) {
|
||||||
lines += line + "\n";
|
lines.append(buffer.data(), f.gcount());
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
error = fmt::format("Couldn't open file [{}]", file_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileContentsResult{
|
return FileContentsResult{
|
||||||
.contents = lines,
|
.contents = lines,
|
||||||
.error = error,
|
.error = {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,3 +259,84 @@ bool IpUtil::IsIPAddress(const std::string &ip_address)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#pragma comment(lib, "ws2_32.lib") // Link against Winsock library
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h> // For inet_pton
|
||||||
|
#pragma comment(lib, "ws2_32.lib") // Link against Winsock library
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h> // For inet_pton
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool IpUtil::IsPortInUse(const std::string& ip, int port) {
|
||||||
|
bool in_use = false;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSADATA wsaData;
|
||||||
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||||
|
std::cerr << "WSAStartup failed\n";
|
||||||
|
return true; // Assume in use on failure
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (sock < 0) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
|
return true; // Assume in use on failure
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
int opt = 1;
|
||||||
|
setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&opt, sizeof(opt)); // Windows-specific
|
||||||
|
#else
|
||||||
|
int opt = 1;
|
||||||
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); // Linux/macOS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sockaddr_in addr{};
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(port);
|
||||||
|
|
||||||
|
// Convert IP address from string to binary format
|
||||||
|
if (inet_pton(AF_INET, ip.c_str(), &addr.sin_addr) <= 0) {
|
||||||
|
std::cerr << "Invalid IP address format: " << ip << std::endl;
|
||||||
|
#ifdef _WIN32
|
||||||
|
closesocket(sock);
|
||||||
|
WSACleanup();
|
||||||
|
#else
|
||||||
|
close(sock);
|
||||||
|
#endif
|
||||||
|
return true; // Assume in use on failure
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||||
|
in_use = true; // Bind failed, port is in use
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
closesocket(sock);
|
||||||
|
WSACleanup();
|
||||||
|
#else
|
||||||
|
close(sock);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return in_use;
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public:
|
|||||||
int port
|
int port
|
||||||
);
|
);
|
||||||
static bool IsIPAddress(const std::string &ip_address);
|
static bool IsIPAddress(const std::string &ip_address);
|
||||||
|
static bool IsPortInUse(const std::string& ip, int port);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -906,26 +906,34 @@ bool EQ::ItemInstance::IsSlotAllowed(int16 slot_id) const {
|
|||||||
|
|
||||||
bool EQ::ItemInstance::IsDroppable(bool recurse) const
|
bool EQ::ItemInstance::IsDroppable(bool recurse) const
|
||||||
{
|
{
|
||||||
if (!m_item)
|
if (!m_item) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
/*if (m_ornamentidfile) // not implemented
|
/*if (m_ornamentidfile) // not implemented
|
||||||
return false;*/
|
return false;*/
|
||||||
if (m_attuned)
|
if (m_attuned) {
|
||||||
return false;
|
return false;
|
||||||
/*if (m_item->FVNoDrop != 0) // not implemented
|
}
|
||||||
return false;*/
|
|
||||||
if (m_item->NoDrop == 0)
|
if (RuleI(World, FVNoDropFlag) == FVNoDropFlagRule::Enabled && m_item->FVNoDrop == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_item->NoDrop == 0) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (recurse) {
|
if (recurse) {
|
||||||
for (auto iter : m_contents) {
|
for (auto iter: m_contents) {
|
||||||
if (!iter.second)
|
if (!iter.second) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!iter.second->IsDroppable(recurse))
|
if (!iter.second->IsDroppable(recurse)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ struct LootItem {
|
|||||||
uint16 trivial_max_level;
|
uint16 trivial_max_level;
|
||||||
uint16 npc_min_level;
|
uint16 npc_min_level;
|
||||||
uint16 npc_max_level;
|
uint16 npc_max_level;
|
||||||
|
uint32 lootdrop_id; // required for zone state referencing
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::list<LootItem*> LootItems;
|
typedef std::list<LootItem*> LootItems;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (opcode == ServerOP_UsertoWorldReq) {
|
if (opcode == ServerOP_UsertoWorldReq) {
|
||||||
auto req_in = (UsertoWorldRequest_Struct*)p.Data();
|
auto req_in = (UsertoWorldRequest*)p.Data();
|
||||||
|
|
||||||
EQ::Net::DynamicPacket req;
|
EQ::Net::DynamicPacket req;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
@@ -45,7 +45,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opcode == ServerOP_LSClientAuth) {
|
if (opcode == ServerOP_LSClientAuth) {
|
||||||
auto req_in = (ClientAuth_Struct*)p.Data();
|
auto req_in = (ClientAuth*)p.Data();
|
||||||
|
|
||||||
EQ::Net::DynamicPacket req;
|
EQ::Net::DynamicPacket req;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
@@ -54,7 +54,7 @@ void EQ::Net::ServertalkServerConnection::Send(uint16_t opcode, EQ::Net::Packet
|
|||||||
req.PutData(i, req_in->key, 30); i += 30;
|
req.PutData(i, req_in->key, 30); i += 30;
|
||||||
req.PutUInt8(i, req_in->lsadmin); i += 1;
|
req.PutUInt8(i, req_in->lsadmin); i += 1;
|
||||||
req.PutUInt16(i, req_in->is_world_admin); i += 2;
|
req.PutUInt16(i, req_in->is_world_admin); i += 2;
|
||||||
req.PutUInt32(i, req_in->ip); i += 4;
|
req.PutUInt32(i, req_in->ip_address); i += 4;
|
||||||
req.PutUInt8(i, req_in->is_client_from_local_network); i += 1;
|
req.PutUInt8(i, req_in->is_client_from_local_network); i += 1;
|
||||||
|
|
||||||
EQ::Net::DynamicPacket out;
|
EQ::Net::DynamicPacket out;
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ void PathManager::LoadPaths()
|
|||||||
constexpr int path_width = 0;
|
constexpr int path_width = 0;
|
||||||
constexpr int break_length = 70;
|
constexpr int break_length = 70;
|
||||||
|
|
||||||
std::cout << std::endl;
|
LogInfo("Loading server paths");
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
LogInfo("{}", Strings::Repeat("-", break_length));
|
||||||
for (const auto& [name, in_path] : paths) {
|
for (const auto& [name, in_path] : paths) {
|
||||||
if (!in_path.empty()) {
|
if (!in_path.empty()) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public:
|
|||||||
int16_t race;
|
int16_t race;
|
||||||
int8_t gender;
|
int8_t gender;
|
||||||
int8_t texture;
|
int8_t texture;
|
||||||
|
int8_t helmtexture;
|
||||||
float mountspeed;
|
float mountspeed;
|
||||||
std::string notes;
|
std::string notes;
|
||||||
};
|
};
|
||||||
@@ -41,6 +42,7 @@ public:
|
|||||||
"race",
|
"race",
|
||||||
"gender",
|
"gender",
|
||||||
"texture",
|
"texture",
|
||||||
|
"helmtexture",
|
||||||
"mountspeed",
|
"mountspeed",
|
||||||
"notes",
|
"notes",
|
||||||
};
|
};
|
||||||
@@ -54,6 +56,7 @@ public:
|
|||||||
"race",
|
"race",
|
||||||
"gender",
|
"gender",
|
||||||
"texture",
|
"texture",
|
||||||
|
"helmtexture",
|
||||||
"mountspeed",
|
"mountspeed",
|
||||||
"notes",
|
"notes",
|
||||||
};
|
};
|
||||||
@@ -101,6 +104,7 @@ public:
|
|||||||
e.race = 216;
|
e.race = 216;
|
||||||
e.gender = 0;
|
e.gender = 0;
|
||||||
e.texture = 0;
|
e.texture = 0;
|
||||||
|
e.helmtexture = -1;
|
||||||
e.mountspeed = 0.75;
|
e.mountspeed = 0.75;
|
||||||
e.notes = "Notes";
|
e.notes = "Notes";
|
||||||
|
|
||||||
@@ -144,8 +148,9 @@ public:
|
|||||||
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
||||||
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
e.mountspeed = row[5] ? strtof(row[5], nullptr) : 0.75;
|
e.helmtexture = row[5] ? static_cast<int8_t>(atoi(row[5])) : -1;
|
||||||
e.notes = row[6] ? row[6] : "Notes";
|
e.mountspeed = row[6] ? strtof(row[6], nullptr) : 0.75;
|
||||||
|
e.notes = row[7] ? row[7] : "Notes";
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -183,8 +188,9 @@ public:
|
|||||||
v.push_back(columns[2] + " = " + std::to_string(e.race));
|
v.push_back(columns[2] + " = " + std::to_string(e.race));
|
||||||
v.push_back(columns[3] + " = " + std::to_string(e.gender));
|
v.push_back(columns[3] + " = " + std::to_string(e.gender));
|
||||||
v.push_back(columns[4] + " = " + std::to_string(e.texture));
|
v.push_back(columns[4] + " = " + std::to_string(e.texture));
|
||||||
v.push_back(columns[5] + " = " + std::to_string(e.mountspeed));
|
v.push_back(columns[5] + " = " + std::to_string(e.helmtexture));
|
||||||
v.push_back(columns[6] + " = '" + Strings::Escape(e.notes) + "'");
|
v.push_back(columns[6] + " = " + std::to_string(e.mountspeed));
|
||||||
|
v.push_back(columns[7] + " = '" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -211,6 +217,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.race));
|
v.push_back(std::to_string(e.race));
|
||||||
v.push_back(std::to_string(e.gender));
|
v.push_back(std::to_string(e.gender));
|
||||||
v.push_back(std::to_string(e.texture));
|
v.push_back(std::to_string(e.texture));
|
||||||
|
v.push_back(std::to_string(e.helmtexture));
|
||||||
v.push_back(std::to_string(e.mountspeed));
|
v.push_back(std::to_string(e.mountspeed));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -247,6 +254,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.race));
|
v.push_back(std::to_string(e.race));
|
||||||
v.push_back(std::to_string(e.gender));
|
v.push_back(std::to_string(e.gender));
|
||||||
v.push_back(std::to_string(e.texture));
|
v.push_back(std::to_string(e.texture));
|
||||||
|
v.push_back(std::to_string(e.helmtexture));
|
||||||
v.push_back(std::to_string(e.mountspeed));
|
v.push_back(std::to_string(e.mountspeed));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -287,8 +295,9 @@ public:
|
|||||||
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
||||||
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
e.mountspeed = row[5] ? strtof(row[5], nullptr) : 0.75;
|
e.helmtexture = row[5] ? static_cast<int8_t>(atoi(row[5])) : -1;
|
||||||
e.notes = row[6] ? row[6] : "Notes";
|
e.mountspeed = row[6] ? strtof(row[6], nullptr) : 0.75;
|
||||||
|
e.notes = row[7] ? row[7] : "Notes";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -318,8 +327,9 @@ public:
|
|||||||
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
e.race = row[2] ? static_cast<int16_t>(atoi(row[2])) : 216;
|
||||||
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
e.gender = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
e.texture = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
e.mountspeed = row[5] ? strtof(row[5], nullptr) : 0.75;
|
e.helmtexture = row[5] ? static_cast<int8_t>(atoi(row[5])) : -1;
|
||||||
e.notes = row[6] ? row[6] : "Notes";
|
e.mountspeed = row[6] ? strtof(row[6], nullptr) : 0.75;
|
||||||
|
e.notes = row[7] ? row[7] : "Notes";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -399,6 +409,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.race));
|
v.push_back(std::to_string(e.race));
|
||||||
v.push_back(std::to_string(e.gender));
|
v.push_back(std::to_string(e.gender));
|
||||||
v.push_back(std::to_string(e.texture));
|
v.push_back(std::to_string(e.texture));
|
||||||
|
v.push_back(std::to_string(e.helmtexture));
|
||||||
v.push_back(std::to_string(e.mountspeed));
|
v.push_back(std::to_string(e.mountspeed));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -428,6 +439,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.race));
|
v.push_back(std::to_string(e.race));
|
||||||
v.push_back(std::to_string(e.gender));
|
v.push_back(std::to_string(e.gender));
|
||||||
v.push_back(std::to_string(e.texture));
|
v.push_back(std::to_string(e.texture));
|
||||||
|
v.push_back(std::to_string(e.helmtexture));
|
||||||
v.push_back(std::to_string(e.mountspeed));
|
v.push_back(std::to_string(e.mountspeed));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public:
|
|||||||
uint8_t is_global;
|
uint8_t is_global;
|
||||||
uint32_t start_time;
|
uint32_t start_time;
|
||||||
uint32_t duration;
|
uint32_t duration;
|
||||||
|
uint64_t expire_at;
|
||||||
uint8_t never_expires;
|
uint8_t never_expires;
|
||||||
std::string notes;
|
std::string notes;
|
||||||
};
|
};
|
||||||
@@ -43,6 +44,7 @@ public:
|
|||||||
"is_global",
|
"is_global",
|
||||||
"start_time",
|
"start_time",
|
||||||
"duration",
|
"duration",
|
||||||
|
"expire_at",
|
||||||
"never_expires",
|
"never_expires",
|
||||||
"notes",
|
"notes",
|
||||||
};
|
};
|
||||||
@@ -57,6 +59,7 @@ public:
|
|||||||
"is_global",
|
"is_global",
|
||||||
"start_time",
|
"start_time",
|
||||||
"duration",
|
"duration",
|
||||||
|
"expire_at",
|
||||||
"never_expires",
|
"never_expires",
|
||||||
"notes",
|
"notes",
|
||||||
};
|
};
|
||||||
@@ -105,6 +108,7 @@ public:
|
|||||||
e.is_global = 0;
|
e.is_global = 0;
|
||||||
e.start_time = 0;
|
e.start_time = 0;
|
||||||
e.duration = 0;
|
e.duration = 0;
|
||||||
|
e.expire_at = 0;
|
||||||
e.never_expires = 0;
|
e.never_expires = 0;
|
||||||
e.notes = "";
|
e.notes = "";
|
||||||
|
|
||||||
@@ -149,8 +153,9 @@ public:
|
|||||||
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||||
e.never_expires = row[6] ? static_cast<uint8_t>(strtoul(row[6], nullptr, 10)) : 0;
|
e.expire_at = row[6] ? strtoull(row[6], nullptr, 10) : 0;
|
||||||
e.notes = row[7] ? row[7] : "";
|
e.never_expires = row[7] ? static_cast<uint8_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.notes = row[8] ? row[8] : "";
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -189,8 +194,9 @@ public:
|
|||||||
v.push_back(columns[3] + " = " + std::to_string(e.is_global));
|
v.push_back(columns[3] + " = " + std::to_string(e.is_global));
|
||||||
v.push_back(columns[4] + " = " + std::to_string(e.start_time));
|
v.push_back(columns[4] + " = " + std::to_string(e.start_time));
|
||||||
v.push_back(columns[5] + " = " + std::to_string(e.duration));
|
v.push_back(columns[5] + " = " + std::to_string(e.duration));
|
||||||
v.push_back(columns[6] + " = " + std::to_string(e.never_expires));
|
v.push_back(columns[6] + " = " + std::to_string(e.expire_at));
|
||||||
v.push_back(columns[7] + " = '" + Strings::Escape(e.notes) + "'");
|
v.push_back(columns[7] + " = " + std::to_string(e.never_expires));
|
||||||
|
v.push_back(columns[8] + " = '" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -218,6 +224,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.is_global));
|
v.push_back(std::to_string(e.is_global));
|
||||||
v.push_back(std::to_string(e.start_time));
|
v.push_back(std::to_string(e.start_time));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.never_expires));
|
v.push_back(std::to_string(e.never_expires));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -255,6 +262,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.is_global));
|
v.push_back(std::to_string(e.is_global));
|
||||||
v.push_back(std::to_string(e.start_time));
|
v.push_back(std::to_string(e.start_time));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.never_expires));
|
v.push_back(std::to_string(e.never_expires));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -296,8 +304,9 @@ public:
|
|||||||
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||||
e.never_expires = row[6] ? static_cast<uint8_t>(strtoul(row[6], nullptr, 10)) : 0;
|
e.expire_at = row[6] ? strtoull(row[6], nullptr, 10) : 0;
|
||||||
e.notes = row[7] ? row[7] : "";
|
e.never_expires = row[7] ? static_cast<uint8_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.notes = row[8] ? row[8] : "";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -328,8 +337,9 @@ public:
|
|||||||
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.is_global = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.start_time = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.duration = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||||
e.never_expires = row[6] ? static_cast<uint8_t>(strtoul(row[6], nullptr, 10)) : 0;
|
e.expire_at = row[6] ? strtoull(row[6], nullptr, 10) : 0;
|
||||||
e.notes = row[7] ? row[7] : "";
|
e.never_expires = row[7] ? static_cast<uint8_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.notes = row[8] ? row[8] : "";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -410,6 +420,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.is_global));
|
v.push_back(std::to_string(e.is_global));
|
||||||
v.push_back(std::to_string(e.start_time));
|
v.push_back(std::to_string(e.start_time));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.never_expires));
|
v.push_back(std::to_string(e.never_expires));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
@@ -440,6 +451,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.is_global));
|
v.push_back(std::to_string(e.is_global));
|
||||||
v.push_back(std::to_string(e.start_time));
|
v.push_back(std::to_string(e.start_time));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.never_expires));
|
v.push_back(std::to_string(e.never_expires));
|
||||||
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
v.push_back("'" + Strings::Escape(e.notes) + "'");
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public:
|
|||||||
uint32_t item_evolve_level;
|
uint32_t item_evolve_level;
|
||||||
uint32_t item_id;
|
uint32_t item_id;
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t sub_type;
|
std::string sub_type;
|
||||||
int64_t required_amount;
|
int64_t required_amount;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ public:
|
|||||||
e.item_evolve_level = 0;
|
e.item_evolve_level = 0;
|
||||||
e.item_id = 0;
|
e.item_id = 0;
|
||||||
e.type = 0;
|
e.type = 0;
|
||||||
e.sub_type = 0;
|
e.sub_type = "0";
|
||||||
e.required_amount = 0;
|
e.required_amount = 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
@@ -144,7 +144,7 @@ public:
|
|||||||
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.sub_type = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.sub_type = row[5] ? row[5] : "0";
|
||||||
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
@@ -183,7 +183,7 @@ public:
|
|||||||
v.push_back(columns[2] + " = " + std::to_string(e.item_evolve_level));
|
v.push_back(columns[2] + " = " + std::to_string(e.item_evolve_level));
|
||||||
v.push_back(columns[3] + " = " + std::to_string(e.item_id));
|
v.push_back(columns[3] + " = " + std::to_string(e.item_id));
|
||||||
v.push_back(columns[4] + " = " + std::to_string(e.type));
|
v.push_back(columns[4] + " = " + std::to_string(e.type));
|
||||||
v.push_back(columns[5] + " = " + std::to_string(e.sub_type));
|
v.push_back(columns[5] + " = '" + Strings::Escape(e.sub_type) + "'");
|
||||||
v.push_back(columns[6] + " = " + std::to_string(e.required_amount));
|
v.push_back(columns[6] + " = " + std::to_string(e.required_amount));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -211,7 +211,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.item_evolve_level));
|
v.push_back(std::to_string(e.item_evolve_level));
|
||||||
v.push_back(std::to_string(e.item_id));
|
v.push_back(std::to_string(e.item_id));
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.sub_type));
|
v.push_back("'" + Strings::Escape(e.sub_type) + "'");
|
||||||
v.push_back(std::to_string(e.required_amount));
|
v.push_back(std::to_string(e.required_amount));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -247,7 +247,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.item_evolve_level));
|
v.push_back(std::to_string(e.item_evolve_level));
|
||||||
v.push_back(std::to_string(e.item_id));
|
v.push_back(std::to_string(e.item_id));
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.sub_type));
|
v.push_back("'" + Strings::Escape(e.sub_type) + "'");
|
||||||
v.push_back(std::to_string(e.required_amount));
|
v.push_back(std::to_string(e.required_amount));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
@@ -287,7 +287,7 @@ public:
|
|||||||
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.sub_type = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.sub_type = row[5] ? row[5] : "0";
|
||||||
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
@@ -318,7 +318,7 @@ public:
|
|||||||
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.sub_type = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
e.sub_type = row[5] ? row[5] : "0";
|
||||||
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
@@ -399,7 +399,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.item_evolve_level));
|
v.push_back(std::to_string(e.item_evolve_level));
|
||||||
v.push_back(std::to_string(e.item_id));
|
v.push_back(std::to_string(e.item_id));
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.sub_type));
|
v.push_back("'" + Strings::Escape(e.sub_type) + "'");
|
||||||
v.push_back(std::to_string(e.required_amount));
|
v.push_back(std::to_string(e.required_amount));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -428,7 +428,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.item_evolve_level));
|
v.push_back(std::to_string(e.item_evolve_level));
|
||||||
v.push_back(std::to_string(e.item_id));
|
v.push_back(std::to_string(e.item_id));
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.sub_type));
|
v.push_back("'" + Strings::Escape(e.sub_type) + "'");
|
||||||
v.push_back(std::to_string(e.required_amount));
|
v.push_back(std::to_string(e.required_amount));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public:
|
|||||||
int32_t id;
|
int32_t id;
|
||||||
int32_t start;
|
int32_t start;
|
||||||
int32_t duration;
|
int32_t duration;
|
||||||
|
uint32_t expire_at;
|
||||||
int16_t instance_id;
|
int16_t instance_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@ public:
|
|||||||
"id",
|
"id",
|
||||||
"start",
|
"start",
|
||||||
"duration",
|
"duration",
|
||||||
|
"expire_at",
|
||||||
"instance_id",
|
"instance_id",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -46,6 +48,7 @@ public:
|
|||||||
"id",
|
"id",
|
||||||
"start",
|
"start",
|
||||||
"duration",
|
"duration",
|
||||||
|
"expire_at",
|
||||||
"instance_id",
|
"instance_id",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -90,6 +93,7 @@ public:
|
|||||||
e.id = 0;
|
e.id = 0;
|
||||||
e.start = 0;
|
e.start = 0;
|
||||||
e.duration = 0;
|
e.duration = 0;
|
||||||
|
e.expire_at = 0;
|
||||||
e.instance_id = 0;
|
e.instance_id = 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
@@ -130,7 +134,8 @@ public:
|
|||||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||||
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||||
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
e.instance_id = row[3] ? static_cast<int16_t>(atoi(row[3])) : 0;
|
e.expire_at = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[4] ? static_cast<int16_t>(atoi(row[4])) : 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -167,7 +172,8 @@ public:
|
|||||||
v.push_back(columns[0] + " = " + std::to_string(e.id));
|
v.push_back(columns[0] + " = " + std::to_string(e.id));
|
||||||
v.push_back(columns[1] + " = " + std::to_string(e.start));
|
v.push_back(columns[1] + " = " + std::to_string(e.start));
|
||||||
v.push_back(columns[2] + " = " + std::to_string(e.duration));
|
v.push_back(columns[2] + " = " + std::to_string(e.duration));
|
||||||
v.push_back(columns[3] + " = " + std::to_string(e.instance_id));
|
v.push_back(columns[3] + " = " + std::to_string(e.expire_at));
|
||||||
|
v.push_back(columns[4] + " = " + std::to_string(e.instance_id));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -192,6 +198,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.start));
|
v.push_back(std::to_string(e.start));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.instance_id));
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -225,6 +232,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.start));
|
v.push_back(std::to_string(e.start));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.instance_id));
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
@@ -262,7 +270,8 @@ public:
|
|||||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||||
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||||
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
e.instance_id = row[3] ? static_cast<int16_t>(atoi(row[3])) : 0;
|
e.expire_at = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[4] ? static_cast<int16_t>(atoi(row[4])) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -290,7 +299,8 @@ public:
|
|||||||
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||||
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.start = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||||
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
e.duration = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
e.instance_id = row[3] ? static_cast<int16_t>(atoi(row[3])) : 0;
|
e.expire_at = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[4] ? static_cast<int16_t>(atoi(row[4])) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -368,6 +378,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.start));
|
v.push_back(std::to_string(e.start));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.instance_id));
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -394,6 +405,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.start));
|
v.push_back(std::to_string(e.start));
|
||||||
v.push_back(std::to_string(e.duration));
|
v.push_back(std::to_string(e.duration));
|
||||||
|
v.push_back(std::to_string(e.expire_at));
|
||||||
v.push_back(std::to_string(e.instance_id));
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
|||||||
@@ -0,0 +1,715 @@
|
|||||||
|
/**
|
||||||
|
* DO NOT MODIFY THIS FILE
|
||||||
|
*
|
||||||
|
* This repository was automatically generated and is NOT to be modified directly.
|
||||||
|
* Any repository modifications are meant to be made to the repository extending the base.
|
||||||
|
* Any modifications to base repositories are to be made by the generator only
|
||||||
|
*
|
||||||
|
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||||
|
* @docs https://docs.eqemu.io/developer/repositories
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EQEMU_BASE_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||||
|
#define EQEMU_BASE_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||||
|
|
||||||
|
#include "../../database.h"
|
||||||
|
#include "../../strings.h"
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
class BaseZoneStateSpawnsRepository {
|
||||||
|
public:
|
||||||
|
struct ZoneStateSpawns {
|
||||||
|
int64_t id;
|
||||||
|
uint32_t zone_id;
|
||||||
|
uint32_t instance_id;
|
||||||
|
int8_t is_corpse;
|
||||||
|
int8_t is_zone;
|
||||||
|
int32_t decay_in_seconds;
|
||||||
|
uint32_t npc_id;
|
||||||
|
uint32_t spawn2_id;
|
||||||
|
uint32_t spawngroup_id;
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
float heading;
|
||||||
|
uint32_t respawn_time;
|
||||||
|
uint32_t variance;
|
||||||
|
uint32_t grid;
|
||||||
|
int32_t current_waypoint;
|
||||||
|
int16_t path_when_zone_idle;
|
||||||
|
uint16_t condition_id;
|
||||||
|
int16_t condition_min_value;
|
||||||
|
int16_t enabled;
|
||||||
|
uint16_t anim;
|
||||||
|
std::string loot_data;
|
||||||
|
std::string entity_variables;
|
||||||
|
std::string buffs;
|
||||||
|
int64_t hp;
|
||||||
|
int64_t mana;
|
||||||
|
int64_t endurance;
|
||||||
|
time_t created_at;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::string PrimaryKey()
|
||||||
|
{
|
||||||
|
return std::string("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> Columns()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"id",
|
||||||
|
"zone_id",
|
||||||
|
"instance_id",
|
||||||
|
"is_corpse",
|
||||||
|
"is_zone",
|
||||||
|
"decay_in_seconds",
|
||||||
|
"npc_id",
|
||||||
|
"spawn2_id",
|
||||||
|
"spawngroup_id",
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
"z",
|
||||||
|
"heading",
|
||||||
|
"respawn_time",
|
||||||
|
"variance",
|
||||||
|
"grid",
|
||||||
|
"current_waypoint",
|
||||||
|
"path_when_zone_idle",
|
||||||
|
"condition_id",
|
||||||
|
"condition_min_value",
|
||||||
|
"enabled",
|
||||||
|
"anim",
|
||||||
|
"loot_data",
|
||||||
|
"entity_variables",
|
||||||
|
"buffs",
|
||||||
|
"hp",
|
||||||
|
"mana",
|
||||||
|
"endurance",
|
||||||
|
"created_at",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> SelectColumns()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"id",
|
||||||
|
"zone_id",
|
||||||
|
"instance_id",
|
||||||
|
"is_corpse",
|
||||||
|
"is_zone",
|
||||||
|
"decay_in_seconds",
|
||||||
|
"npc_id",
|
||||||
|
"spawn2_id",
|
||||||
|
"spawngroup_id",
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
"z",
|
||||||
|
"heading",
|
||||||
|
"respawn_time",
|
||||||
|
"variance",
|
||||||
|
"grid",
|
||||||
|
"current_waypoint",
|
||||||
|
"path_when_zone_idle",
|
||||||
|
"condition_id",
|
||||||
|
"condition_min_value",
|
||||||
|
"enabled",
|
||||||
|
"anim",
|
||||||
|
"loot_data",
|
||||||
|
"entity_variables",
|
||||||
|
"buffs",
|
||||||
|
"hp",
|
||||||
|
"mana",
|
||||||
|
"endurance",
|
||||||
|
"UNIX_TIMESTAMP(created_at)",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ColumnsRaw()
|
||||||
|
{
|
||||||
|
return std::string(Strings::Implode(", ", Columns()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string SelectColumnsRaw()
|
||||||
|
{
|
||||||
|
return std::string(Strings::Implode(", ", SelectColumns()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string TableName()
|
||||||
|
{
|
||||||
|
return std::string("zone_state_spawns");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseSelect()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"SELECT {} FROM {}",
|
||||||
|
SelectColumnsRaw(),
|
||||||
|
TableName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseInsert()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"INSERT INTO {} ({}) ",
|
||||||
|
TableName(),
|
||||||
|
ColumnsRaw()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ZoneStateSpawns NewEntity()
|
||||||
|
{
|
||||||
|
ZoneStateSpawns e{};
|
||||||
|
|
||||||
|
e.id = 0;
|
||||||
|
e.zone_id = 0;
|
||||||
|
e.instance_id = 0;
|
||||||
|
e.is_corpse = 0;
|
||||||
|
e.is_zone = 0;
|
||||||
|
e.decay_in_seconds = 0;
|
||||||
|
e.npc_id = 0;
|
||||||
|
e.spawn2_id = 0;
|
||||||
|
e.spawngroup_id = 0;
|
||||||
|
e.x = 0;
|
||||||
|
e.y = 0;
|
||||||
|
e.z = 0;
|
||||||
|
e.heading = 0;
|
||||||
|
e.respawn_time = 0;
|
||||||
|
e.variance = 0;
|
||||||
|
e.grid = 0;
|
||||||
|
e.current_waypoint = 0;
|
||||||
|
e.path_when_zone_idle = 0;
|
||||||
|
e.condition_id = 0;
|
||||||
|
e.condition_min_value = 0;
|
||||||
|
e.enabled = 1;
|
||||||
|
e.anim = 0;
|
||||||
|
e.loot_data = "";
|
||||||
|
e.entity_variables = "";
|
||||||
|
e.buffs = "";
|
||||||
|
e.hp = 0;
|
||||||
|
e.mana = 0;
|
||||||
|
e.endurance = 0;
|
||||||
|
e.created_at = 0;
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ZoneStateSpawns GetZoneStateSpawns(
|
||||||
|
const std::vector<ZoneStateSpawns> &zone_state_spawnss,
|
||||||
|
int zone_state_spawns_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for (auto &zone_state_spawns : zone_state_spawnss) {
|
||||||
|
if (zone_state_spawns.id == zone_state_spawns_id) {
|
||||||
|
return zone_state_spawns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
static ZoneStateSpawns FindOne(
|
||||||
|
Database& db,
|
||||||
|
int zone_state_spawns_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} WHERE {} = {} LIMIT 1",
|
||||||
|
BaseSelect(),
|
||||||
|
PrimaryKey(),
|
||||||
|
zone_state_spawns_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto row = results.begin();
|
||||||
|
if (results.RowCount() == 1) {
|
||||||
|
ZoneStateSpawns e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoll(row[0], nullptr, 10) : 0;
|
||||||
|
e.zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.is_corpse = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
|
e.is_zone = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
|
e.decay_in_seconds = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||||
|
e.npc_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||||
|
e.spawn2_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.spawngroup_id = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||||
|
e.x = row[9] ? strtof(row[9], nullptr) : 0;
|
||||||
|
e.y = row[10] ? strtof(row[10], nullptr) : 0;
|
||||||
|
e.z = row[11] ? strtof(row[11], nullptr) : 0;
|
||||||
|
e.heading = row[12] ? strtof(row[12], nullptr) : 0;
|
||||||
|
e.respawn_time = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||||
|
e.variance = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
|
e.grid = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||||
|
e.current_waypoint = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||||
|
e.path_when_zone_idle = row[17] ? static_cast<int16_t>(atoi(row[17])) : 0;
|
||||||
|
e.condition_id = row[18] ? static_cast<uint16_t>(strtoul(row[18], nullptr, 10)) : 0;
|
||||||
|
e.condition_min_value = row[19] ? static_cast<int16_t>(atoi(row[19])) : 0;
|
||||||
|
e.enabled = row[20] ? static_cast<int16_t>(atoi(row[20])) : 1;
|
||||||
|
e.anim = row[21] ? static_cast<uint16_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||||
|
e.loot_data = row[22] ? row[22] : "";
|
||||||
|
e.entity_variables = row[23] ? row[23] : "";
|
||||||
|
e.buffs = row[24] ? row[24] : "";
|
||||||
|
e.hp = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||||
|
e.mana = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||||
|
e.endurance = row[27] ? strtoll(row[27], nullptr, 10) : 0;
|
||||||
|
e.created_at = strtoll(row[28] ? row[28] : "-1", nullptr, 10);
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DeleteOne(
|
||||||
|
Database& db,
|
||||||
|
int zone_state_spawns_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"DELETE FROM {} WHERE {} = {}",
|
||||||
|
TableName(),
|
||||||
|
PrimaryKey(),
|
||||||
|
zone_state_spawns_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int UpdateOne(
|
||||||
|
Database& db,
|
||||||
|
const ZoneStateSpawns &e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto columns = Columns();
|
||||||
|
|
||||||
|
v.push_back(columns[1] + " = " + std::to_string(e.zone_id));
|
||||||
|
v.push_back(columns[2] + " = " + std::to_string(e.instance_id));
|
||||||
|
v.push_back(columns[3] + " = " + std::to_string(e.is_corpse));
|
||||||
|
v.push_back(columns[4] + " = " + std::to_string(e.is_zone));
|
||||||
|
v.push_back(columns[5] + " = " + std::to_string(e.decay_in_seconds));
|
||||||
|
v.push_back(columns[6] + " = " + std::to_string(e.npc_id));
|
||||||
|
v.push_back(columns[7] + " = " + std::to_string(e.spawn2_id));
|
||||||
|
v.push_back(columns[8] + " = " + std::to_string(e.spawngroup_id));
|
||||||
|
v.push_back(columns[9] + " = " + std::to_string(e.x));
|
||||||
|
v.push_back(columns[10] + " = " + std::to_string(e.y));
|
||||||
|
v.push_back(columns[11] + " = " + std::to_string(e.z));
|
||||||
|
v.push_back(columns[12] + " = " + std::to_string(e.heading));
|
||||||
|
v.push_back(columns[13] + " = " + std::to_string(e.respawn_time));
|
||||||
|
v.push_back(columns[14] + " = " + std::to_string(e.variance));
|
||||||
|
v.push_back(columns[15] + " = " + std::to_string(e.grid));
|
||||||
|
v.push_back(columns[16] + " = " + std::to_string(e.current_waypoint));
|
||||||
|
v.push_back(columns[17] + " = " + std::to_string(e.path_when_zone_idle));
|
||||||
|
v.push_back(columns[18] + " = " + std::to_string(e.condition_id));
|
||||||
|
v.push_back(columns[19] + " = " + std::to_string(e.condition_min_value));
|
||||||
|
v.push_back(columns[20] + " = " + std::to_string(e.enabled));
|
||||||
|
v.push_back(columns[21] + " = " + std::to_string(e.anim));
|
||||||
|
v.push_back(columns[22] + " = '" + Strings::Escape(e.loot_data) + "'");
|
||||||
|
v.push_back(columns[23] + " = '" + Strings::Escape(e.entity_variables) + "'");
|
||||||
|
v.push_back(columns[24] + " = '" + Strings::Escape(e.buffs) + "'");
|
||||||
|
v.push_back(columns[25] + " = " + std::to_string(e.hp));
|
||||||
|
v.push_back(columns[26] + " = " + std::to_string(e.mana));
|
||||||
|
v.push_back(columns[27] + " = " + std::to_string(e.endurance));
|
||||||
|
v.push_back(columns[28] + " = FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"UPDATE {} SET {} WHERE {} = {}",
|
||||||
|
TableName(),
|
||||||
|
Strings::Implode(", ", v),
|
||||||
|
PrimaryKey(),
|
||||||
|
e.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ZoneStateSpawns InsertOne(
|
||||||
|
Database& db,
|
||||||
|
ZoneStateSpawns e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.zone_id));
|
||||||
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
v.push_back(std::to_string(e.is_corpse));
|
||||||
|
v.push_back(std::to_string(e.is_zone));
|
||||||
|
v.push_back(std::to_string(e.decay_in_seconds));
|
||||||
|
v.push_back(std::to_string(e.npc_id));
|
||||||
|
v.push_back(std::to_string(e.spawn2_id));
|
||||||
|
v.push_back(std::to_string(e.spawngroup_id));
|
||||||
|
v.push_back(std::to_string(e.x));
|
||||||
|
v.push_back(std::to_string(e.y));
|
||||||
|
v.push_back(std::to_string(e.z));
|
||||||
|
v.push_back(std::to_string(e.heading));
|
||||||
|
v.push_back(std::to_string(e.respawn_time));
|
||||||
|
v.push_back(std::to_string(e.variance));
|
||||||
|
v.push_back(std::to_string(e.grid));
|
||||||
|
v.push_back(std::to_string(e.current_waypoint));
|
||||||
|
v.push_back(std::to_string(e.path_when_zone_idle));
|
||||||
|
v.push_back(std::to_string(e.condition_id));
|
||||||
|
v.push_back(std::to_string(e.condition_min_value));
|
||||||
|
v.push_back(std::to_string(e.enabled));
|
||||||
|
v.push_back(std::to_string(e.anim));
|
||||||
|
v.push_back("'" + Strings::Escape(e.loot_data) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.buffs) + "'");
|
||||||
|
v.push_back(std::to_string(e.hp));
|
||||||
|
v.push_back(std::to_string(e.mana));
|
||||||
|
v.push_back(std::to_string(e.endurance));
|
||||||
|
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES ({})",
|
||||||
|
BaseInsert(),
|
||||||
|
Strings::Implode(",", v)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (results.Success()) {
|
||||||
|
e.id = results.LastInsertedID();
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = NewEntity();
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int InsertMany(
|
||||||
|
Database& db,
|
||||||
|
const std::vector<ZoneStateSpawns> &entries
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> insert_chunks;
|
||||||
|
|
||||||
|
for (auto &e: entries) {
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.zone_id));
|
||||||
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
v.push_back(std::to_string(e.is_corpse));
|
||||||
|
v.push_back(std::to_string(e.is_zone));
|
||||||
|
v.push_back(std::to_string(e.decay_in_seconds));
|
||||||
|
v.push_back(std::to_string(e.npc_id));
|
||||||
|
v.push_back(std::to_string(e.spawn2_id));
|
||||||
|
v.push_back(std::to_string(e.spawngroup_id));
|
||||||
|
v.push_back(std::to_string(e.x));
|
||||||
|
v.push_back(std::to_string(e.y));
|
||||||
|
v.push_back(std::to_string(e.z));
|
||||||
|
v.push_back(std::to_string(e.heading));
|
||||||
|
v.push_back(std::to_string(e.respawn_time));
|
||||||
|
v.push_back(std::to_string(e.variance));
|
||||||
|
v.push_back(std::to_string(e.grid));
|
||||||
|
v.push_back(std::to_string(e.current_waypoint));
|
||||||
|
v.push_back(std::to_string(e.path_when_zone_idle));
|
||||||
|
v.push_back(std::to_string(e.condition_id));
|
||||||
|
v.push_back(std::to_string(e.condition_min_value));
|
||||||
|
v.push_back(std::to_string(e.enabled));
|
||||||
|
v.push_back(std::to_string(e.anim));
|
||||||
|
v.push_back("'" + Strings::Escape(e.loot_data) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.buffs) + "'");
|
||||||
|
v.push_back(std::to_string(e.hp));
|
||||||
|
v.push_back(std::to_string(e.mana));
|
||||||
|
v.push_back(std::to_string(e.endurance));
|
||||||
|
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES {}",
|
||||||
|
BaseInsert(),
|
||||||
|
Strings::Implode(",", insert_chunks)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<ZoneStateSpawns> All(Database& db)
|
||||||
|
{
|
||||||
|
std::vector<ZoneStateSpawns> all_entries;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{}",
|
||||||
|
BaseSelect()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.reserve(results.RowCount());
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
ZoneStateSpawns e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoll(row[0], nullptr, 10) : 0;
|
||||||
|
e.zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.is_corpse = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
|
e.is_zone = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
|
e.decay_in_seconds = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||||
|
e.npc_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||||
|
e.spawn2_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.spawngroup_id = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||||
|
e.x = row[9] ? strtof(row[9], nullptr) : 0;
|
||||||
|
e.y = row[10] ? strtof(row[10], nullptr) : 0;
|
||||||
|
e.z = row[11] ? strtof(row[11], nullptr) : 0;
|
||||||
|
e.heading = row[12] ? strtof(row[12], nullptr) : 0;
|
||||||
|
e.respawn_time = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||||
|
e.variance = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
|
e.grid = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||||
|
e.current_waypoint = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||||
|
e.path_when_zone_idle = row[17] ? static_cast<int16_t>(atoi(row[17])) : 0;
|
||||||
|
e.condition_id = row[18] ? static_cast<uint16_t>(strtoul(row[18], nullptr, 10)) : 0;
|
||||||
|
e.condition_min_value = row[19] ? static_cast<int16_t>(atoi(row[19])) : 0;
|
||||||
|
e.enabled = row[20] ? static_cast<int16_t>(atoi(row[20])) : 1;
|
||||||
|
e.anim = row[21] ? static_cast<uint16_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||||
|
e.loot_data = row[22] ? row[22] : "";
|
||||||
|
e.entity_variables = row[23] ? row[23] : "";
|
||||||
|
e.buffs = row[24] ? row[24] : "";
|
||||||
|
e.hp = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||||
|
e.mana = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||||
|
e.endurance = row[27] ? strtoll(row[27], nullptr, 10) : 0;
|
||||||
|
e.created_at = strtoll(row[28] ? row[28] : "-1", nullptr, 10);
|
||||||
|
|
||||||
|
all_entries.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<ZoneStateSpawns> GetWhere(Database& db, const std::string &where_filter)
|
||||||
|
{
|
||||||
|
std::vector<ZoneStateSpawns> all_entries;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} WHERE {}",
|
||||||
|
BaseSelect(),
|
||||||
|
where_filter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.reserve(results.RowCount());
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
ZoneStateSpawns e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoll(row[0], nullptr, 10) : 0;
|
||||||
|
e.zone_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
|
e.instance_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.is_corpse = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||||
|
e.is_zone = row[4] ? static_cast<int8_t>(atoi(row[4])) : 0;
|
||||||
|
e.decay_in_seconds = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||||
|
e.npc_id = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||||
|
e.spawn2_id = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.spawngroup_id = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||||
|
e.x = row[9] ? strtof(row[9], nullptr) : 0;
|
||||||
|
e.y = row[10] ? strtof(row[10], nullptr) : 0;
|
||||||
|
e.z = row[11] ? strtof(row[11], nullptr) : 0;
|
||||||
|
e.heading = row[12] ? strtof(row[12], nullptr) : 0;
|
||||||
|
e.respawn_time = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||||
|
e.variance = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
|
e.grid = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||||
|
e.current_waypoint = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||||
|
e.path_when_zone_idle = row[17] ? static_cast<int16_t>(atoi(row[17])) : 0;
|
||||||
|
e.condition_id = row[18] ? static_cast<uint16_t>(strtoul(row[18], nullptr, 10)) : 0;
|
||||||
|
e.condition_min_value = row[19] ? static_cast<int16_t>(atoi(row[19])) : 0;
|
||||||
|
e.enabled = row[20] ? static_cast<int16_t>(atoi(row[20])) : 1;
|
||||||
|
e.anim = row[21] ? static_cast<uint16_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||||
|
e.loot_data = row[22] ? row[22] : "";
|
||||||
|
e.entity_variables = row[23] ? row[23] : "";
|
||||||
|
e.buffs = row[24] ? row[24] : "";
|
||||||
|
e.hp = row[25] ? strtoll(row[25], nullptr, 10) : 0;
|
||||||
|
e.mana = row[26] ? strtoll(row[26], nullptr, 10) : 0;
|
||||||
|
e.endurance = row[27] ? strtoll(row[27], nullptr, 10) : 0;
|
||||||
|
e.created_at = strtoll(row[28] ? row[28] : "-1", nullptr, 10);
|
||||||
|
|
||||||
|
all_entries.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DeleteWhere(Database& db, const std::string &where_filter)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"DELETE FROM {} WHERE {}",
|
||||||
|
TableName(),
|
||||||
|
where_filter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Truncate(Database& db)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"TRUNCATE TABLE {}",
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64 GetMaxId(Database& db)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT COALESCE(MAX({}), 0) FROM {}",
|
||||||
|
PrimaryKey(),
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64 Count(Database& db, const std::string &where_filter = "")
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT COUNT(*) FROM {} {}",
|
||||||
|
TableName(),
|
||||||
|
(where_filter.empty() ? "" : "WHERE " + where_filter)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseReplace()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"REPLACE INTO {} ({}) ",
|
||||||
|
TableName(),
|
||||||
|
ColumnsRaw()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReplaceOne(
|
||||||
|
Database& db,
|
||||||
|
const ZoneStateSpawns &e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.zone_id));
|
||||||
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
v.push_back(std::to_string(e.is_corpse));
|
||||||
|
v.push_back(std::to_string(e.is_zone));
|
||||||
|
v.push_back(std::to_string(e.decay_in_seconds));
|
||||||
|
v.push_back(std::to_string(e.npc_id));
|
||||||
|
v.push_back(std::to_string(e.spawn2_id));
|
||||||
|
v.push_back(std::to_string(e.spawngroup_id));
|
||||||
|
v.push_back(std::to_string(e.x));
|
||||||
|
v.push_back(std::to_string(e.y));
|
||||||
|
v.push_back(std::to_string(e.z));
|
||||||
|
v.push_back(std::to_string(e.heading));
|
||||||
|
v.push_back(std::to_string(e.respawn_time));
|
||||||
|
v.push_back(std::to_string(e.variance));
|
||||||
|
v.push_back(std::to_string(e.grid));
|
||||||
|
v.push_back(std::to_string(e.current_waypoint));
|
||||||
|
v.push_back(std::to_string(e.path_when_zone_idle));
|
||||||
|
v.push_back(std::to_string(e.condition_id));
|
||||||
|
v.push_back(std::to_string(e.condition_min_value));
|
||||||
|
v.push_back(std::to_string(e.enabled));
|
||||||
|
v.push_back(std::to_string(e.anim));
|
||||||
|
v.push_back("'" + Strings::Escape(e.loot_data) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.buffs) + "'");
|
||||||
|
v.push_back(std::to_string(e.hp));
|
||||||
|
v.push_back(std::to_string(e.mana));
|
||||||
|
v.push_back(std::to_string(e.endurance));
|
||||||
|
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES ({})",
|
||||||
|
BaseReplace(),
|
||||||
|
Strings::Implode(",", v)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ReplaceMany(
|
||||||
|
Database& db,
|
||||||
|
const std::vector<ZoneStateSpawns> &entries
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> insert_chunks;
|
||||||
|
|
||||||
|
for (auto &e: entries) {
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.zone_id));
|
||||||
|
v.push_back(std::to_string(e.instance_id));
|
||||||
|
v.push_back(std::to_string(e.is_corpse));
|
||||||
|
v.push_back(std::to_string(e.is_zone));
|
||||||
|
v.push_back(std::to_string(e.decay_in_seconds));
|
||||||
|
v.push_back(std::to_string(e.npc_id));
|
||||||
|
v.push_back(std::to_string(e.spawn2_id));
|
||||||
|
v.push_back(std::to_string(e.spawngroup_id));
|
||||||
|
v.push_back(std::to_string(e.x));
|
||||||
|
v.push_back(std::to_string(e.y));
|
||||||
|
v.push_back(std::to_string(e.z));
|
||||||
|
v.push_back(std::to_string(e.heading));
|
||||||
|
v.push_back(std::to_string(e.respawn_time));
|
||||||
|
v.push_back(std::to_string(e.variance));
|
||||||
|
v.push_back(std::to_string(e.grid));
|
||||||
|
v.push_back(std::to_string(e.current_waypoint));
|
||||||
|
v.push_back(std::to_string(e.path_when_zone_idle));
|
||||||
|
v.push_back(std::to_string(e.condition_id));
|
||||||
|
v.push_back(std::to_string(e.condition_min_value));
|
||||||
|
v.push_back(std::to_string(e.enabled));
|
||||||
|
v.push_back(std::to_string(e.anim));
|
||||||
|
v.push_back("'" + Strings::Escape(e.loot_data) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.entity_variables) + "'");
|
||||||
|
v.push_back("'" + Strings::Escape(e.buffs) + "'");
|
||||||
|
v.push_back(std::to_string(e.hp));
|
||||||
|
v.push_back(std::to_string(e.mana));
|
||||||
|
v.push_back(std::to_string(e.endurance));
|
||||||
|
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES {}",
|
||||||
|
BaseReplace(),
|
||||||
|
Strings::Implode(",", insert_chunks)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //EQEMU_BASE_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||||
@@ -7,49 +7,11 @@
|
|||||||
|
|
||||||
class InstanceListRepository: public BaseInstanceListRepository {
|
class InstanceListRepository: public BaseInstanceListRepository {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
|
||||||
* This file was auto generated and can be modified and extended upon
|
|
||||||
*
|
|
||||||
* Base repository methods are automatically
|
|
||||||
* generated in the "base" version of this repository. The base repository
|
|
||||||
* is immutable and to be left untouched, while methods in this class
|
|
||||||
* are used as extension methods for more specific persistence-layer
|
|
||||||
* accessors or mutators.
|
|
||||||
*
|
|
||||||
* Base Methods (Subject to be expanded upon in time)
|
|
||||||
*
|
|
||||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
|
||||||
*
|
|
||||||
* InsertOne
|
|
||||||
* UpdateOne
|
|
||||||
* DeleteOne
|
|
||||||
* FindOne
|
|
||||||
* GetWhere(std::string where_filter)
|
|
||||||
* DeleteWhere(std::string where_filter)
|
|
||||||
* InsertMany
|
|
||||||
* All
|
|
||||||
*
|
|
||||||
* Example custom methods in a repository
|
|
||||||
*
|
|
||||||
* InstanceListRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
|
||||||
* InstanceListRepository::GetWhereNeverExpires()
|
|
||||||
* InstanceListRepository::GetWhereXAndY()
|
|
||||||
* InstanceListRepository::DeleteWhereXAndY()
|
|
||||||
*
|
|
||||||
* Most of the above could be covered by base methods, but if you as a developer
|
|
||||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
|
||||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
|
||||||
* method and encapsulate filters there
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Custom extended repository methods here
|
|
||||||
|
|
||||||
static int UpdateDuration(Database& db, uint16 instance_id, uint32_t new_duration)
|
static int UpdateDuration(Database& db, uint16 instance_id, uint32_t new_duration)
|
||||||
{
|
{
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"UPDATE `{}` SET `duration` = {} WHERE `{}` = {}",
|
"UPDATE `{}` SET `duration` = {}, `expire_at` = (`duration` + `start_time`) WHERE `{}` = {}",
|
||||||
TableName(),
|
TableName(),
|
||||||
new_duration,
|
new_duration,
|
||||||
PrimaryKey(),
|
PrimaryKey(),
|
||||||
@@ -65,7 +27,7 @@ public:
|
|||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
SQL(
|
SQL(
|
||||||
SELECT ((start_time + duration) - UNIX_TIMESTAMP()) AS `remaining` FROM `{}`
|
SELECT (`expire_at` - UNIX_TIMESTAMP()) AS `remaining` FROM `{}`
|
||||||
WHERE `id` = {}
|
WHERE `id` = {}
|
||||||
),
|
),
|
||||||
TableName(),
|
TableName(),
|
||||||
|
|||||||
@@ -8,47 +8,11 @@
|
|||||||
class RespawnTimesRepository: public BaseRespawnTimesRepository {
|
class RespawnTimesRepository: public BaseRespawnTimesRepository {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
|
||||||
* This file was auto generated and can be modified and extended upon
|
|
||||||
*
|
|
||||||
* Base repository methods are automatically
|
|
||||||
* generated in the "base" version of this repository. The base repository
|
|
||||||
* is immutable and to be left untouched, while methods in this class
|
|
||||||
* are used as extension methods for more specific persistence-layer
|
|
||||||
* accessors or mutators.
|
|
||||||
*
|
|
||||||
* Base Methods (Subject to be expanded upon in time)
|
|
||||||
*
|
|
||||||
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
|
||||||
*
|
|
||||||
* InsertOne
|
|
||||||
* UpdateOne
|
|
||||||
* DeleteOne
|
|
||||||
* FindOne
|
|
||||||
* GetWhere(std::string where_filter)
|
|
||||||
* DeleteWhere(std::string where_filter)
|
|
||||||
* InsertMany
|
|
||||||
* All
|
|
||||||
*
|
|
||||||
* Example custom methods in a repository
|
|
||||||
*
|
|
||||||
* RespawnTimesRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
|
||||||
* RespawnTimesRepository::GetWhereNeverExpires()
|
|
||||||
* RespawnTimesRepository::GetWhereXAndY()
|
|
||||||
* RespawnTimesRepository::DeleteWhereXAndY()
|
|
||||||
*
|
|
||||||
* Most of the above could be covered by base methods, but if you as a developer
|
|
||||||
* find yourself re-using logic for other parts of the code, its best to just make a
|
|
||||||
* method that can be re-used easily elsewhere especially if it can use a base repository
|
|
||||||
* method and encapsulate filters there
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Custom extended repository methods here
|
|
||||||
static void ClearExpiredRespawnTimers(Database& db)
|
static void ClearExpiredRespawnTimers(Database& db)
|
||||||
{
|
{
|
||||||
db.QueryDatabase(
|
db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"DELETE FROM `{}` WHERE (`start` + `duration`) < UNIX_TIMESTAMP(NOW())",
|
"DELETE FROM `{}` WHERE `expire_at` < UNIX_TIMESTAMP(NOW())",
|
||||||
TableName()
|
TableName()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -77,6 +41,11 @@ public:
|
|||||||
|
|
||||||
return ((r.start + r.duration) - time_seconds);
|
return ((r.start + r.duration) - time_seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ClearInstanceTimers(Database &db, int32_t id)
|
||||||
|
{
|
||||||
|
RespawnTimesRepository::DeleteWhere(db, fmt::format("`instance_id` = {}", id));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_RESPAWN_TIMES_REPOSITORY_H
|
#endif //EQEMU_RESPAWN_TIMES_REPOSITORY_H
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#ifndef EQEMU_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||||
|
#define EQEMU_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||||
|
|
||||||
|
#include "../database.h"
|
||||||
|
#include "../strings.h"
|
||||||
|
#include "base/base_zone_state_spawns_repository.h"
|
||||||
|
|
||||||
|
class ZoneStateSpawnsRepository : public BaseZoneStateSpawnsRepository {
|
||||||
|
public:
|
||||||
|
static void PurgeInvalidZoneStates(Database &database)
|
||||||
|
{
|
||||||
|
std::string query = R"(
|
||||||
|
SELECT zone_id, instance_id
|
||||||
|
FROM zone_state_spawns
|
||||||
|
GROUP BY zone_id, instance_id
|
||||||
|
HAVING COUNT(*) = SUM(
|
||||||
|
CASE
|
||||||
|
WHEN hp = 0
|
||||||
|
AND mana = 0
|
||||||
|
AND endurance = 0
|
||||||
|
AND (loot_data IS NULL OR loot_data = '')
|
||||||
|
AND (entity_variables IS NULL OR entity_variables = '')
|
||||||
|
AND (buffs IS NULL OR buffs = '')
|
||||||
|
THEN 1 ELSE 0
|
||||||
|
END
|
||||||
|
);
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
|
if (!results.Success()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto row: results) {
|
||||||
|
uint32 zone_id = std::stoul(row[0]);
|
||||||
|
uint32 instance_id = std::stoul(row[1]);
|
||||||
|
|
||||||
|
int rows = ZoneStateSpawnsRepository::DeleteWhere(
|
||||||
|
database,
|
||||||
|
fmt::format(
|
||||||
|
"`zone_id` = {} AND `instance_id` = {}",
|
||||||
|
zone_id,
|
||||||
|
instance_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
LogInfo(
|
||||||
|
"Purged invalid zone state data for zone [{}] instance [{}] rows [{}]",
|
||||||
|
zone_id,
|
||||||
|
instance_id,
|
||||||
|
Strings::Commify(rows)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PurgeOldZoneStates(Database &database)
|
||||||
|
{
|
||||||
|
int days = RuleI(Zone, StateSaveClearDays);
|
||||||
|
|
||||||
|
std::string query = fmt::format(
|
||||||
|
"DELETE FROM zone_state_spawns WHERE created_at < NOW() - INTERVAL {} DAY",
|
||||||
|
days
|
||||||
|
);
|
||||||
|
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
|
if (!results.Success()) {
|
||||||
|
LogError("Failed to purge old zone state data older than {} days.", days);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results.RowsAffected() > 0) {
|
||||||
|
LogInfo(
|
||||||
|
"Purged old zone state data older than days [{}] rows [{}]",
|
||||||
|
days,
|
||||||
|
Strings::Commify(results.RowsAffected())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //EQEMU_ZONE_STATE_SPAWNS_REPOSITORY_H
|
||||||
+29
-20
@@ -54,7 +54,7 @@ RULE_INT(Character, CorpseDecayTime, 604800000, "Time after which the corpse dec
|
|||||||
RULE_INT(Character, EmptyCorpseDecayTime, 10800000, "Time after which an empty corpse decays (milliseconds) DEFAULT: 10800000 (3 Hours)")
|
RULE_INT(Character, EmptyCorpseDecayTime, 10800000, "Time after which an empty corpse decays (milliseconds) DEFAULT: 10800000 (3 Hours)")
|
||||||
RULE_INT(Character, CorpseResTime, 10800000, "Time after which the corpse can no longer be resurrected (milliseconds) DEFAULT: 10800000 (3 Hours)")
|
RULE_INT(Character, CorpseResTime, 10800000, "Time after which the corpse can no longer be resurrected (milliseconds) DEFAULT: 10800000 (3 Hours)")
|
||||||
RULE_INT(Character, DuelCorpseResTime, 600000, "Time before cant res corpse after a duel (milliseconds) DEFAULT: 600000 (10 Minutes)")
|
RULE_INT(Character, DuelCorpseResTime, 600000, "Time before cant res corpse after a duel (milliseconds) DEFAULT: 600000 (10 Minutes)")
|
||||||
RULE_INT(Character, CorpseOwnerOnlineTime, 30000, "How often corpse will check if its owner is online DEFAULT: 30000 (30 Seconds)")
|
RULE_INT(Character, CorpseOwnerOnlineCheckTime, 300, "How often corpse will check if its owner is online DEFAULT: 300 (5 minutes)")
|
||||||
RULE_BOOL(Character, LeaveCorpses, true, "Setting whether you leave a corpse behind")
|
RULE_BOOL(Character, LeaveCorpses, true, "Setting whether you leave a corpse behind")
|
||||||
RULE_BOOL(Character, LeaveNakedCorpses, false, "Setting whether you leave a corpse without items")
|
RULE_BOOL(Character, LeaveNakedCorpses, false, "Setting whether you leave a corpse without items")
|
||||||
RULE_INT(Character, MaxDraggedCorpses, 2, "Maximum number of corpses you can drag at once")
|
RULE_INT(Character, MaxDraggedCorpses, 2, "Maximum number of corpses you can drag at once")
|
||||||
@@ -231,6 +231,7 @@ RULE_INT(Character, MendAlwaysSucceedValue, 199, "Value at which mend will alway
|
|||||||
RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false")
|
RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false")
|
||||||
RULE_INT(Character, BandolierSwapDelay, 0, "Bandolier swap delay in milliseconds, default is 0")
|
RULE_INT(Character, BandolierSwapDelay, 0, "Bandolier swap delay in milliseconds, default is 0")
|
||||||
RULE_BOOL(Character, EnableHackedFastCampForGM, false, "Enables hacked fast camp for GM clients, if the GM doesn't have a hacked client they'll camp like normal")
|
RULE_BOOL(Character, EnableHackedFastCampForGM, false, "Enables hacked fast camp for GM clients, if the GM doesn't have a hacked client they'll camp like normal")
|
||||||
|
RULE_BOOL(Character, AlwaysAllowNameChange, false, "Enable this option to allow /changename to work without enabling a name change via scripts.")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Mercs)
|
RULE_CATEGORY(Mercs)
|
||||||
@@ -260,6 +261,7 @@ RULE_INT(Guild, TributeTime, 600000, "Time in ms for guild tributes. Default is
|
|||||||
RULE_INT(Guild, TributeTimeRefreshInterval, 180000, "Time in ms to send all guild members a Tribute Time refresh. Default is 3 mins.")
|
RULE_INT(Guild, TributeTimeRefreshInterval, 180000, "Time in ms to send all guild members a Tribute Time refresh. Default is 3 mins.")
|
||||||
RULE_INT(Guild, TributePlatConversionRate, 10, "The conversion rate of platinum donations. Default is 10 guild favor to 1 platinum.")
|
RULE_INT(Guild, TributePlatConversionRate, 10, "The conversion rate of platinum donations. Default is 10 guild favor to 1 platinum.")
|
||||||
RULE_BOOL(Guild, UseCharacterMaxLevelForGuildTributes, true, "Guild Tributes will adhere to Character:MaxLevel. Default is true.")
|
RULE_BOOL(Guild, UseCharacterMaxLevelForGuildTributes, true, "Guild Tributes will adhere to Character:MaxLevel. Default is true.")
|
||||||
|
RULE_BOOL(Guild, EnableLFGuild, false, "Enable the LFGuild system (Requires queryserv)")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Skills)
|
RULE_CATEGORY(Skills)
|
||||||
@@ -289,6 +291,7 @@ RULE_BOOL(Pets, ClientPetsUseOwnerNameInLastName, true, "Disable this to keep cl
|
|||||||
RULE_BOOL(Pets, CanTakeNoDrop, false, "Setting whether anyone can give no-drop items to pets")
|
RULE_BOOL(Pets, CanTakeNoDrop, false, "Setting whether anyone can give no-drop items to pets")
|
||||||
RULE_INT(Pets, PetTauntRange, 150, "Range at which a pet will taunt targets.")
|
RULE_INT(Pets, PetTauntRange, 150, "Range at which a pet will taunt targets.")
|
||||||
RULE_BOOL(Pets, AlwaysAllowPetRename, false, "Enable this option to allow /changepetname to work without enabling a pet name change via scripts.")
|
RULE_BOOL(Pets, AlwaysAllowPetRename, false, "Enable this option to allow /changepetname to work without enabling a pet name change via scripts.")
|
||||||
|
RULE_BOOL(Pets, PetsRequireLoS, false, "Whether or not pets require line of sight to be told to attack their target")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(GM)
|
RULE_CATEGORY(GM)
|
||||||
@@ -374,6 +377,11 @@ RULE_BOOL(Zone, AllowCrossZoneSpellsOnBots, false, "Set to true to allow cross z
|
|||||||
RULE_BOOL(Zone, AllowCrossZoneSpellsOnMercs, false, "Set to true to allow cross zone spells (cast/remove) to affect mercenaries")
|
RULE_BOOL(Zone, AllowCrossZoneSpellsOnMercs, false, "Set to true to allow cross zone spells (cast/remove) to affect mercenaries")
|
||||||
RULE_BOOL(Zone, AllowCrossZoneSpellsOnPets, false, "Set to true to allow cross zone spells (cast/remove) to affect pets")
|
RULE_BOOL(Zone, AllowCrossZoneSpellsOnPets, false, "Set to true to allow cross zone spells (cast/remove) to affect pets")
|
||||||
RULE_BOOL(Zone, ZoneShardQuestMenuOnly, false, "Set to true if you only want quests to show the zone shard menu")
|
RULE_BOOL(Zone, ZoneShardQuestMenuOnly, false, "Set to true if you only want quests to show the zone shard menu")
|
||||||
|
RULE_BOOL(Zone, StateSaveEntityVariables, true, "Set to true if you want buffs to be saved on shutdown")
|
||||||
|
RULE_BOOL(Zone, StateSaveBuffs, true, "Set to true if you want buffs to be saved on shutdown")
|
||||||
|
RULE_INT(Zone, StateSaveClearDays, 7, "Clears state save data older than this many days")
|
||||||
|
RULE_BOOL(Zone, StateSavingOnShutdown, true, "Set to true if you want zones to save state on shutdown (npcs, corpses, loot, entity variables, buffs etc.)")
|
||||||
|
RULE_INT(Zone, UpdateWhoTimer, 120, "Seconds between updates to /who list, CLE stale timer")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Map)
|
RULE_CATEGORY(Map)
|
||||||
@@ -383,9 +391,10 @@ RULE_BOOL(Map, MobZVisualDebug, false, "Displays spell effects determining wheth
|
|||||||
RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging")
|
RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging")
|
||||||
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply")
|
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply")
|
||||||
RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position")
|
RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position")
|
||||||
RULE_BOOL(Map, CheckForLoSCheat, false, "Runs predefined zone checks to check for LoS cheating through doors and such.")
|
RULE_BOOL(Map, CheckForDoorLoSCheat, true, "Runs LoS checks to prevent cheating through doors.")
|
||||||
RULE_BOOL(Map, EnableLoSCheatExemptions, false, "Enables exemptions for the LoS Cheat check.")
|
RULE_BOOL(Map, EnableLoSCheatExemptions, false, "Enables exemptions for the LoS Cheat check. Must modify source to create these.")
|
||||||
RULE_REAL(Map, RangeCheckForLoSCheat, 20.0, "Default 20.0. Range to check if one is within range of a door.")
|
RULE_REAL(Map, RangeCheckForDoorLoSCheat, 250.0, "Default 250.0. Range to check if a door is blocking LoS from the target.")
|
||||||
|
RULE_STRING(Map, ZonesToCheckDoorCheat, "89,103", "Zones that will check for the door LoS cheat. You can leave it blank to disable, 'all' to check all zones or use a comma-delimited list of zones. Default Sebilis & Chardok")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Pathing)
|
RULE_CATEGORY(Pathing)
|
||||||
@@ -528,6 +537,7 @@ RULE_INT(Spells, TargetedAOEMaxTargets, 4, "Max number of targets a Targeted AOE
|
|||||||
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, PointBlankAOEMaxTargets, 0, "Max number of targets a Point-Blank AOE spell can cast on. Set to 0 for no limit.")
|
||||||
RULE_INT(Spells, DefaultAOEMaxTargets, 0, "Max number of targets that an AOE spell which does not meet other descriptions can cast on. Set to 0 for no limit.")
|
RULE_INT(Spells, DefaultAOEMaxTargets, 0, "Max number of targets that an AOE spell which does not meet other descriptions can cast on. Set to 0 for no limit.")
|
||||||
RULE_BOOL(Spells, AllowFocusOnSkillDamageSpells, false, "Allow focus effects 185, 459, and 482 to enhance SkillAttack spell effect 193")
|
RULE_BOOL(Spells, AllowFocusOnSkillDamageSpells, false, "Allow focus effects 185, 459, and 482 to enhance SkillAttack spell effect 193")
|
||||||
|
RULE_STRING(Spells, AlwaysStackSpells, "", "Comma-Seperated list of spell IDs to always stack with every other spell, except themselves.")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Combat)
|
RULE_CATEGORY(Combat)
|
||||||
@@ -808,6 +818,7 @@ RULE_INT(Bots, PercentChanceToCastDispel, 75, "The chance for a bot to attempt t
|
|||||||
RULE_INT(Bots, PercentChanceToCastInCombatBuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastInCombatBuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastHateLine, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastHateLine, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
|
RULE_INT(Bots, PercentChanceToCastAEMez, 40, "The chance for a bot to attempt to cast the given spell type in combat. Default 40%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.")
|
||||||
@@ -819,8 +830,6 @@ RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 500, "The minimum delay in m
|
|||||||
RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2500ms.")
|
RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2500ms.")
|
||||||
RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 1000, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 1000ms.")
|
RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 1000, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 1000ms.")
|
||||||
RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 2500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 2500ms.")
|
RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 2500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 2500ms.")
|
||||||
RULE_INT(Bots, MezChance, 60, "60 Default. Chance for a bot to attempt to Mez a target after validating it is eligible.")
|
|
||||||
RULE_INT(Bots, AEMezChance, 35, "35 Default. Chance for a bot to attempt to AE Mez targets after validating they are eligible.")
|
|
||||||
RULE_INT(Bots, MezSuccessDelay, 2500, "2500 (2.5 sec) Default. Delay between successful Mez attempts.")
|
RULE_INT(Bots, MezSuccessDelay, 2500, "2500 (2.5 sec) Default. Delay between successful Mez attempts.")
|
||||||
RULE_INT(Bots, AEMezSuccessDelay, 5000, "5000 (5 sec) Default. Delay between successful AEMez attempts.")
|
RULE_INT(Bots, AEMezSuccessDelay, 5000, "5000 (5 sec) Default. Delay between successful AEMez attempts.")
|
||||||
RULE_INT(Bots, MezFailDelay, 1250, "1250 (1.25 sec) Default. Delay between failed Mez attempts.")
|
RULE_INT(Bots, MezFailDelay, 1250, "1250 (1.25 sec) Default. Delay between failed Mez attempts.")
|
||||||
@@ -830,14 +839,14 @@ RULE_INT(Bots, MinGroupCureTargets, 3, "Minimum number of targets in valid range
|
|||||||
RULE_INT(Bots, MinTargetsForAESpell, 3, "Minimum number of targets in valid range that are required for an AE spell to cast. Default 3.")
|
RULE_INT(Bots, MinTargetsForAESpell, 3, "Minimum number of targets in valid range that are required for an AE spell to cast. Default 3.")
|
||||||
RULE_INT(Bots, MinTargetsForGroupSpell, 3, "Minimum number of targets in valid range that are required for an group spell to cast. Default 3.")
|
RULE_INT(Bots, MinTargetsForGroupSpell, 3, "Minimum number of targets in valid range that are required for an group spell to cast. Default 3.")
|
||||||
RULE_BOOL(Bots, AllowBuffingHealingFamiliars, false, "Determines if bots are allowed to buff and heal familiars. Default false.")
|
RULE_BOOL(Bots, AllowBuffingHealingFamiliars, false, "Determines if bots are allowed to buff and heal familiars. Default false.")
|
||||||
RULE_BOOL(Bots, RunSpellTypeChecksOnSpawn, false, "This will run a serious of checks on spell types and output errors to LogBotSpellTypeChecks")
|
RULE_BOOL(Bots, RunSpellTypeChecksOnBoot, false, "This will run a series of checks to find potential errors in your bot_spells_entries table on boot and output to LogBotSpellTypeChecks")
|
||||||
RULE_BOOL(Bots, UseParentSpellTypeForChecks, true, "This will check only the parent instead of AE/Group/Pet types (ex: AENukes/AERains/PBAENukes fall under Nukes or PetBuffs fall under buffs) when RunSpellTypeChecksOnSpawn fires")
|
RULE_BOOL(Bots, UseParentSpellTypeForChecks, true, "This will check only the parent instead of AE/Group/Pet types (ex: AENukes/AERains/PBAENukes fall under Nukes or PetBuffs fall under buffs) when RunSpellTypeChecksOnSpawn fires")
|
||||||
RULE_BOOL(Bots, AllowForcedCastsBySpellID, true, "If enabled, players can use ^cast spellid # to cast a specific spell by ID that is in their spell list")
|
RULE_BOOL(Bots, AllowForcedCastsBySpellID, true, "If enabled, players can use ^cast spellid # to cast a specific spell by ID that is in their spell list")
|
||||||
RULE_BOOL(Bots, AllowCastAAs, true, "If enabled, players can use ^cast aa to cast a clickable AA")
|
RULE_BOOL(Bots, AllowCastAAs, true, "If enabled, players can use ^cast aa to cast a clickable AA")
|
||||||
RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summon their epic pets following the rules AllowMagicianEpicPetLevel")
|
RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summon their epic pets following the rules AllowMagicianEpicPetLevel")
|
||||||
RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level")
|
RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level")
|
||||||
RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement")
|
RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement")
|
||||||
RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their spell list to cast.")
|
RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their usable spell list to cast. Empty uses Manifest Elements - 'SumMageMultiElement'")
|
||||||
RULE_INT(Bots, ReclaimEnergySpellID, 331, "Spell ID for reclaim energy when using ^petsettype. Default 331")
|
RULE_INT(Bots, ReclaimEnergySpellID, 331, "Spell ID for reclaim energy when using ^petsettype. Default 331")
|
||||||
RULE_BOOL(Bots, UseSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.")
|
RULE_BOOL(Bots, UseSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.")
|
||||||
RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots")
|
RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots")
|
||||||
@@ -848,17 +857,13 @@ RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery A
|
|||||||
RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
|
RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
|
||||||
RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).")
|
RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).")
|
||||||
RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.")
|
RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.")
|
||||||
RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.")
|
RULE_REAL(Bots, LowerMeleeDistanceMultiplier, 0.35, "Closest % of the hit box a melee bot will get to the target. Default 0.35")
|
||||||
RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "If UseFlatNormalMeleeRange is enabled, multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.")
|
RULE_REAL(Bots, LowerTauntingMeleeDistanceMultiplier, 0.25, "Closest % of the hit box a taunting melee bot will get to the target. Default 0.25")
|
||||||
RULE_REAL(Bots, PercentMinMeleeDistance, 0.75, "Multiplier of the their melee range - Minimum distance from target a bot will stand while in melee combat before trying to adjust. 0.60 Recommended.")
|
RULE_REAL(Bots, LowerMaxMeleeRangeDistanceMultiplier, 0.80, "Closest % of the hit box a max melee range melee bot will get to the target. Default 0.80")
|
||||||
RULE_REAL(Bots, MaxDistanceForMelee, 20, "Maximum distance bots will stand for melee. Default 20 to allow all special attacks to land.")
|
RULE_REAL(Bots, UpperMeleeDistanceMultiplier, 0.55, "Furthest % of the hit box a melee bot will get from the target. Default 0.55")
|
||||||
RULE_REAL(Bots, TauntNormalMeleeRangeDistance, 0.50, "Multiplier of the max melee range at which a taunting bot will stand in melee combat. 0.50 Recommended, closer than others .")
|
RULE_REAL(Bots, UpperTauntingMeleeDistanceMultiplier, 0.45, "Furthest % of the hit box a taunting melee bot will get from the target. Default 0.45")
|
||||||
RULE_REAL(Bots, PercentTauntMinMeleeDistance, 0.40, "Multiplier of their melee range - Minimum distance from target a taunting bot will stand while in melee combat before trying to adjust. 0.25 Recommended.")
|
RULE_REAL(Bots, UpperMaxMeleeRangeDistanceMultiplier, 0.95, "Furthest % of the hit box a max melee range melee bot will get from the target. Default 0.95")
|
||||||
RULE_REAL(Bots, PercentMaxMeleeRangeDistance, 0.95, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.95 Recommended, max melee while disabling special attacks/taunt.")
|
RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "If true, when bots are at max melee distance, special abilities including taunt will be disabled. Default True.")
|
||||||
RULE_REAL(Bots, PercentMinMaxMeleeRangeDistance, 0.75, "Multiplier of the closest max melee range at which a bot will stand in melee combat before trying to adjust. 0.75 Recommended, max melee while disabling special attacks/taunt.")
|
|
||||||
RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.")
|
|
||||||
RULE_INT(Bots, DistanceTauntingBotsStickMainHate, 10, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.")
|
|
||||||
RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "True Default. If true, when bots are at max melee distance, special abilities including taunt will be disabled.")
|
|
||||||
RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.")
|
RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.")
|
||||||
RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.")
|
RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.")
|
||||||
RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.")
|
RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.")
|
||||||
@@ -899,6 +904,7 @@ RULE_STRING(Bots, ZonesWithForcedSpawnLimits, "", "Comma-delimited list of zones
|
|||||||
RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spawn limits for zones.")
|
RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spawn limits for zones.")
|
||||||
RULE_INT(Bots, AICastSpellTypeDelay, 100, "Delay in milliseconds between AI cast attempts for each spell type. Default 100ms")
|
RULE_INT(Bots, AICastSpellTypeDelay, 100, "Delay in milliseconds between AI cast attempts for each spell type. Default 100ms")
|
||||||
RULE_INT(Bots, AICastSpellTypeHeldDelay, 2500, "Delay in milliseconds between AI cast attempts for each spell type that is held or disabled. Default 2500ms (2.5s)")
|
RULE_INT(Bots, AICastSpellTypeHeldDelay, 2500, "Delay in milliseconds between AI cast attempts for each spell type that is held or disabled. Default 2500ms (2.5s)")
|
||||||
|
RULE_BOOL(Bots, BotsRequireLoS, true, "Whether or not bots require line of sight to be told to attack their target")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Chat)
|
RULE_CATEGORY(Chat)
|
||||||
@@ -1090,6 +1096,7 @@ RULE_CATEGORY(Instances)
|
|||||||
RULE_INT(Instances, ReservedInstances, 100, "Number of instance IDs which are reserved for globals. This value should not be changed while a server is running")
|
RULE_INT(Instances, ReservedInstances, 100, "Number of instance IDs which are reserved for globals. This value should not be changed while a server is running")
|
||||||
RULE_BOOL(Instances, RecycleInstanceIds, true, "Setting whether free instance IDs should be recycled to prevent them from gradually running out at 32k")
|
RULE_BOOL(Instances, RecycleInstanceIds, true, "Setting whether free instance IDs should be recycled to prevent them from gradually running out at 32k")
|
||||||
RULE_INT(Instances, GuildHallExpirationDays, 90, "Amount of days before a Guild Hall instance expires")
|
RULE_INT(Instances, GuildHallExpirationDays, 90, "Amount of days before a Guild Hall instance expires")
|
||||||
|
RULE_INT(Instances, ExpireOffsetTimeSeconds, 3600, "Amount of seconds to beyond instance expiration time we wait to purge the entry from the database. (Default: 1 Hour)")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Expedition)
|
RULE_CATEGORY(Expedition)
|
||||||
@@ -1145,6 +1152,7 @@ RULE_BOOL(Items, DisableSpellFocusEffects, false, "Enable this to disable Spell
|
|||||||
RULE_BOOL(Items, SummonItemAllowInvisibleAugments, false, "Enable this to allow augments to be put in invisible augment slots of items in Client::SummonItem")
|
RULE_BOOL(Items, SummonItemAllowInvisibleAugments, false, "Enable this to allow augments to be put in invisible augment slots of items in Client::SummonItem")
|
||||||
RULE_BOOL(Items, AugmentItemAllowInvisibleAugments, false, "Enable this to allow augments to be put in invisible augment slots by players")
|
RULE_BOOL(Items, AugmentItemAllowInvisibleAugments, false, "Enable this to allow augments to be put in invisible augment slots by players")
|
||||||
RULE_BOOL(Items, AlwaysReturnHandins, true, "Enable this to always return handins to the player")
|
RULE_BOOL(Items, AlwaysReturnHandins, true, "Enable this to always return handins to the player")
|
||||||
|
RULE_BOOL(Items, NPCUseRecommendedLevels, false, "Enable to have NPCs scale item stats by recommended levels")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Parcel)
|
RULE_CATEGORY(Parcel)
|
||||||
@@ -1160,8 +1168,9 @@ RULE_CATEGORY_END()
|
|||||||
RULE_CATEGORY(EvolvingItems)
|
RULE_CATEGORY(EvolvingItems)
|
||||||
RULE_REAL(EvolvingItems, PercentOfSoloExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.")
|
RULE_REAL(EvolvingItems, PercentOfSoloExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.")
|
||||||
RULE_REAL(EvolvingItems, PercentOfGroupExperience, 0.1, "Percentage of group experience allocated to evolving items that require experience.")
|
RULE_REAL(EvolvingItems, PercentOfGroupExperience, 0.1, "Percentage of group experience allocated to evolving items that require experience.")
|
||||||
RULE_REAL(EvolvingItems, PercentOfRaidExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.")
|
RULE_REAL(EvolvingItems, PercentOfRaidExperience, 0.1, "Percentage of raid experience allocated to evolving items that require experience.")
|
||||||
RULE_INT(EvolvingItems, DelayUponEquipping, 30000, "Delay in ms before an evolving item will earn rewards after equipping. Default is 30000ms or 30s.")
|
RULE_INT(EvolvingItems, DelayUponEquipping, 30000, "Delay in ms before an evolving item will earn rewards after equipping. Default is 30000ms or 30s.")
|
||||||
|
RULE_BOOL(EvolvingItems, DestroyAugmentsOnEvolve, false, "If this is enabled, any augments in an item will be destroyed when the item evolves. Otherwise, send augments to the player via the parcel system (requires that the Parcel System be enabled).")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
#undef RULE_CATEGORY
|
#undef RULE_CATEGORY
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ namespace ServerReload {
|
|||||||
LevelEXPMods,
|
LevelEXPMods,
|
||||||
Logs,
|
Logs,
|
||||||
Loot,
|
Loot,
|
||||||
|
Maps,
|
||||||
Merchants,
|
Merchants,
|
||||||
NPCEmotes,
|
NPCEmotes,
|
||||||
NPCSpells,
|
NPCSpells,
|
||||||
@@ -61,6 +62,7 @@ namespace ServerReload {
|
|||||||
"Level EXP Mods",
|
"Level EXP Mods",
|
||||||
"Logs",
|
"Logs",
|
||||||
"Loot",
|
"Loot",
|
||||||
|
"Maps",
|
||||||
"Merchants",
|
"Merchants",
|
||||||
"NPC Emotes",
|
"NPC Emotes",
|
||||||
"NPC Spells",
|
"NPC Spells",
|
||||||
|
|||||||
+43
-18
@@ -196,6 +196,7 @@
|
|||||||
#define ServerOP_DzSaveInvite 0x0466
|
#define ServerOP_DzSaveInvite 0x0466
|
||||||
#define ServerOP_DzRequestInvite 0x0467
|
#define ServerOP_DzRequestInvite 0x0467
|
||||||
#define ServerOP_DzMakeLeader 0x0468
|
#define ServerOP_DzMakeLeader 0x0468
|
||||||
|
#define ServerOP_DzGetBulkMemberStatuses 0x0469
|
||||||
|
|
||||||
#define ServerOP_LSInfo 0x1000
|
#define ServerOP_LSInfo 0x1000
|
||||||
#define ServerOP_LSStatus 0x1001
|
#define ServerOP_LSStatus 0x1001
|
||||||
@@ -677,36 +678,53 @@ struct ServerLSPlayerZoneChange_Struct {
|
|||||||
uint32 to; // 0 = world
|
uint32 to; // 0 = world
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClientAuth_Struct {
|
struct ClientAuth {
|
||||||
uint32 loginserver_account_id; // ID# in login server's db
|
uint32 loginserver_account_id; // ID# in login server's db
|
||||||
char loginserver_name[64];
|
char loginserver_name[64];
|
||||||
char account_name[30]; // username in login server's db
|
char account_name[30]; // username in login server's db
|
||||||
char key[30]; // the Key the client will present
|
char key[30]; // the key the client will present
|
||||||
uint8 lsadmin; // login server admin level
|
uint8 lsadmin; // login server admin level
|
||||||
int16 is_world_admin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it
|
int16 is_world_admin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it
|
||||||
uint32 ip;
|
uint32 ip_address;
|
||||||
uint8 is_client_from_local_network; // 1 if the client is from the local network
|
uint8 is_client_from_local_network; // 1 if the client is from the local network
|
||||||
|
|
||||||
template <class Archive>
|
template<class Archive>
|
||||||
void serialize(Archive &ar)
|
void serialize(Archive &ar)
|
||||||
{
|
{
|
||||||
ar(loginserver_account_id, loginserver_name, account_name, key, lsadmin, is_world_admin, ip, is_client_from_local_network);
|
ar(
|
||||||
|
loginserver_account_id,
|
||||||
|
loginserver_name,
|
||||||
|
account_name,
|
||||||
|
key,
|
||||||
|
lsadmin,
|
||||||
|
is_world_admin,
|
||||||
|
ip_address,
|
||||||
|
is_client_from_local_network
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClientAuthLegacy_Struct {
|
struct ClientAuthLegacy {
|
||||||
uint32 loginserver_account_id; // ID# in login server's db
|
uint32 loginserver_account_id; // ID# in login server's db
|
||||||
char loginserver_account_name[30]; // username in login server's db
|
char loginserver_account_name[30]; // username in login server's db
|
||||||
char key[30]; // the Key the client will present
|
char key[30]; // the key the client will present
|
||||||
uint8 loginserver_admin_level; // login server admin level
|
uint8 loginserver_admin_level; // login server admin level
|
||||||
int16 is_world_admin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it
|
int16 is_world_admin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it
|
||||||
uint32 ip;
|
uint32 ip_address;
|
||||||
uint8 is_client_from_local_network; // 1 if the client is from the local network
|
uint8 is_client_from_local_network; // 1 if the client is from the local network
|
||||||
|
|
||||||
template <class Archive>
|
template<class Archive>
|
||||||
void serialize(Archive &ar)
|
void serialize(Archive &ar)
|
||||||
{
|
{
|
||||||
ar(loginserver_account_id, loginserver_account_name, key, loginserver_admin_level, is_world_admin, ip, is_client_from_local_network);
|
ar(
|
||||||
|
loginserver_account_id,
|
||||||
|
loginserver_account_name,
|
||||||
|
key,
|
||||||
|
loginserver_admin_level,
|
||||||
|
is_world_admin,
|
||||||
|
ip_address,
|
||||||
|
is_client_from_local_network
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -834,7 +852,7 @@ struct ServerSyncWorldList_Struct {
|
|||||||
bool placeholder;
|
bool placeholder;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UsertoWorldRequestLegacy_Struct {
|
struct UsertoWorldRequestLegacy {
|
||||||
uint32 lsaccountid;
|
uint32 lsaccountid;
|
||||||
uint32 worldid;
|
uint32 worldid;
|
||||||
uint32 FromID;
|
uint32 FromID;
|
||||||
@@ -842,16 +860,16 @@ struct UsertoWorldRequestLegacy_Struct {
|
|||||||
char IPAddr[64];
|
char IPAddr[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UsertoWorldRequest_Struct {
|
struct UsertoWorldRequest {
|
||||||
uint32 lsaccountid;
|
uint32 lsaccountid;
|
||||||
uint32 worldid;
|
uint32 worldid;
|
||||||
uint32 FromID;
|
uint32 FromID; // appears to be unused today
|
||||||
uint32 ToID;
|
uint32 ToID; // appears to be unused today
|
||||||
char IPAddr[64];
|
char IPAddr[64];
|
||||||
char login[64];
|
char login[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UsertoWorldResponseLegacy_Struct {
|
struct UsertoWorldResponseLegacy {
|
||||||
uint32 lsaccountid;
|
uint32 lsaccountid;
|
||||||
uint32 worldid;
|
uint32 worldid;
|
||||||
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
||||||
@@ -859,12 +877,12 @@ struct UsertoWorldResponseLegacy_Struct {
|
|||||||
uint32 ToID;
|
uint32 ToID;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UsertoWorldResponse_Struct {
|
struct UsertoWorldResponse {
|
||||||
uint32 lsaccountid;
|
uint32 lsaccountid;
|
||||||
uint32 worldid;
|
uint32 worldid;
|
||||||
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed
|
||||||
uint32 FromID;
|
uint32 FromID; // appears to be unused today
|
||||||
uint32 ToID;
|
uint32 ToID; // appears to be unused today
|
||||||
char login[64];
|
char login[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1538,6 +1556,13 @@ struct ServerDzMemberStatuses_Struct {
|
|||||||
ServerDzMemberStatusEntry_Struct entries[0];
|
ServerDzMemberStatusEntry_Struct entries[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ServerDzCerealData_Struct {
|
||||||
|
uint16_t zone_id;
|
||||||
|
uint16_t inst_id;
|
||||||
|
uint32_t cereal_size;
|
||||||
|
char cereal_data[1];
|
||||||
|
};
|
||||||
|
|
||||||
struct ServerDzMovePC_Struct {
|
struct ServerDzMovePC_Struct {
|
||||||
uint32 dz_id;
|
uint32 dz_id;
|
||||||
uint16 sender_zone_id;
|
uint16 sender_zone_id;
|
||||||
|
|||||||
+19
-19
@@ -1470,9 +1470,10 @@ bool IsFastHealSpell(uint16 spell_id) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id && IsValidSpell(spell_id)) {
|
if (IsValidSpell(spell_id)) {
|
||||||
if (
|
if (
|
||||||
spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME &&
|
spell_id != SPELL_MINOR_HEALING &&
|
||||||
|
(spells[spell_id].cast_time > MAX_VERY_FAST_HEAL_CASTING_TIME && spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME) &&
|
||||||
spells[spell_id].good_effect &&
|
spells[spell_id].good_effect &&
|
||||||
!IsGroupSpell(spell_id)
|
!IsGroupSpell(spell_id)
|
||||||
) {
|
) {
|
||||||
@@ -1509,8 +1510,9 @@ bool IsVeryFastHealSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id) {
|
if (IsValidSpell(spell_id)) {
|
||||||
if (
|
if (
|
||||||
|
spell_id != SPELL_MINOR_HEALING &&
|
||||||
spells[spell_id].cast_time <= MAX_VERY_FAST_HEAL_CASTING_TIME &&
|
spells[spell_id].cast_time <= MAX_VERY_FAST_HEAL_CASTING_TIME &&
|
||||||
spells[spell_id].good_effect &&
|
spells[spell_id].good_effect &&
|
||||||
!IsGroupSpell(spell_id)
|
!IsGroupSpell(spell_id)
|
||||||
@@ -1548,8 +1550,13 @@ bool IsRegularSingleTargetHealSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id) {
|
if (IsValidSpell(spell_id)) {
|
||||||
|
if (spell_id == SPELL_MINOR_HEALING) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
spells[spell_id].cast_time > MAX_FAST_HEAL_CASTING_TIME &&
|
||||||
spells[spell_id].target_type == ST_Target &&
|
spells[spell_id].target_type == ST_Target &&
|
||||||
!IsCompleteHealSpell(spell_id) &&
|
!IsCompleteHealSpell(spell_id) &&
|
||||||
!IsHealOverTimeSpell(spell_id) &&
|
!IsHealOverTimeSpell(spell_id) &&
|
||||||
@@ -1589,9 +1596,14 @@ bool IsRegularPetHealSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id && IsValidSpell(spell_id)) {
|
if (IsValidSpell(spell_id)) {
|
||||||
|
if (spell_id == SPELL_MINOR_HEALING) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_Undead) &&
|
spells[spell_id].cast_time > MAX_FAST_HEAL_CASTING_TIME &&
|
||||||
|
(spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_SummonedPet) &&
|
||||||
!IsCompleteHealSpell(spell_id) &&
|
!IsCompleteHealSpell(spell_id) &&
|
||||||
!IsHealOverTimeSpell(spell_id) &&
|
!IsHealOverTimeSpell(spell_id) &&
|
||||||
!IsGroupSpell(spell_id)
|
!IsGroupSpell(spell_id)
|
||||||
@@ -1630,7 +1642,7 @@ bool IsRegularGroupHealSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_id) {
|
if (IsValidSpell(spell_id)) {
|
||||||
if (
|
if (
|
||||||
IsGroupSpell(spell_id) &&
|
IsGroupSpell(spell_id) &&
|
||||||
!IsCompleteHealSpell(spell_id) &&
|
!IsCompleteHealSpell(spell_id) &&
|
||||||
@@ -2796,18 +2808,6 @@ bool IsLichSpell(uint16 spell_id)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsValidSpellAndLoS(uint32 spell_id, bool has_los) {
|
|
||||||
if (!IsValidSpell(spell_id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_los && IsTargetRequiredForSpell(spell_id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsInstantHealSpell(uint32 spell_id) {
|
bool IsInstantHealSpell(uint32 spell_id) {
|
||||||
if (!IsValidSpell(spell_id)) {
|
if (!IsValidSpell(spell_id)) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+6
-3
@@ -215,6 +215,7 @@
|
|||||||
#define SPELL_AMPLIFICATION 2603
|
#define SPELL_AMPLIFICATION 2603
|
||||||
#define SPELL_DIVINE_REZ 2738
|
#define SPELL_DIVINE_REZ 2738
|
||||||
#define SPELL_NATURES_RECOVERY 2520
|
#define SPELL_NATURES_RECOVERY 2520
|
||||||
|
#define SPELL_MINOR_HEALING 200
|
||||||
#define SPELL_ADRENALINE_SWELL 14445
|
#define SPELL_ADRENALINE_SWELL 14445
|
||||||
#define SPELL_ADRENALINE_SWELL_RK2 14446
|
#define SPELL_ADRENALINE_SWELL_RK2 14446
|
||||||
#define SPELL_ADRENALINE_SWELL_RK3 14447
|
#define SPELL_ADRENALINE_SWELL_RK3 14447
|
||||||
@@ -741,6 +742,7 @@ namespace BotSpellTypes
|
|||||||
constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed
|
constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed
|
||||||
constexpr uint16 DISCIPLINE_START = BotSpellTypes::Discipline; // Do not remove or change this
|
constexpr uint16 DISCIPLINE_START = BotSpellTypes::Discipline; // Do not remove or change this
|
||||||
constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed
|
constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed
|
||||||
|
constexpr uint16 PARENT_TYPE_END = BotSpellTypes::PreCombatBuffSong; // This is the last ID of the original bot spell types, the rest are considered sub types.
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::map<uint16, std::string> spell_type_names = {
|
static std::map<uint16, std::string> spell_type_names = {
|
||||||
@@ -898,8 +900,8 @@ const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellTy
|
|||||||
const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root);
|
const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root);
|
||||||
|
|
||||||
// Bot related functions
|
// Bot related functions
|
||||||
bool IsBotSpellTypeDetrimental (uint16 spell_type);
|
bool IsBotSpellTypeDetrimental(uint16 spell_type);
|
||||||
bool IsBotSpellTypeBeneficial (uint16 spell_type);
|
bool IsBotSpellTypeBeneficial(uint16 spell_type);
|
||||||
bool BotSpellTypeUsesTargetSettings(uint16 spell_type);
|
bool BotSpellTypeUsesTargetSettings(uint16 spell_type);
|
||||||
bool IsBotSpellTypeInnate (uint16 spell_type);
|
bool IsBotSpellTypeInnate (uint16 spell_type);
|
||||||
bool IsAEBotSpellType(uint16 spell_type);
|
bool IsAEBotSpellType(uint16 spell_type);
|
||||||
@@ -915,6 +917,8 @@ bool IsCommandedBotSpellType(uint16 spell_type);
|
|||||||
bool IsPullingBotSpellType(uint16 spell_type);
|
bool IsPullingBotSpellType(uint16 spell_type);
|
||||||
uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id);
|
uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id);
|
||||||
uint16 GetPetBotSpellType(uint16 spell_type);
|
uint16 GetPetBotSpellType(uint16 spell_type);
|
||||||
|
bool IsBotBuffSpellType(uint16 spell_type);
|
||||||
|
bool BotRequiresLoSToCast(uint16 spell_type, uint16 spell_id);
|
||||||
|
|
||||||
// These should not be used to determine spell category..
|
// These should not be used to determine spell category..
|
||||||
// They are a graphical affects (effects?) index only
|
// They are a graphical affects (effects?) index only
|
||||||
@@ -1812,7 +1816,6 @@ bool IsEffectInSpell(uint16 spell_id, int effect_id);
|
|||||||
uint16 GetSpellTriggerSpellID(uint16 spell_id, int effect_id);
|
uint16 GetSpellTriggerSpellID(uint16 spell_id, int effect_id);
|
||||||
bool IsBlankSpellEffect(uint16 spell_id, int effect_index);
|
bool IsBlankSpellEffect(uint16 spell_id, int effect_index);
|
||||||
bool IsValidSpell(uint32 spell_id);
|
bool IsValidSpell(uint32 spell_id);
|
||||||
bool IsValidSpellAndLoS(uint32 spell_id, bool has_los = true);
|
|
||||||
bool IsSummonSpell(uint16 spell_id);
|
bool IsSummonSpell(uint16 spell_id);
|
||||||
bool IsDamageSpell(uint16 spell_id);
|
bool IsDamageSpell(uint16 spell_id);
|
||||||
bool IsAnyDamageSpell(uint16 spell_id);
|
bool IsAnyDamageSpell(uint16 spell_id);
|
||||||
|
|||||||
+42
-254
@@ -1,4 +1,5 @@
|
|||||||
#include "spdat.h"
|
#include "spdat.h"
|
||||||
|
#include "../zone/bot.h"
|
||||||
|
|
||||||
bool IsBotSpellTypeDetrimental(uint16 spell_type) {
|
bool IsBotSpellTypeDetrimental(uint16 spell_type) {
|
||||||
switch (spell_type) {
|
switch (spell_type) {
|
||||||
@@ -417,264 +418,23 @@ uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id) {
|
|||||||
return UINT16_MAX;
|
return UINT16_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 correct_type = UINT16_MAX;
|
uint16 correct_type = spell_type;
|
||||||
SPDat_Spell_Struct spell = spells[spell_id];
|
|
||||||
std::string teleport_zone = spell.teleport_zone;
|
|
||||||
|
|
||||||
if (IsCharmSpell(spell_id)) {
|
if (!Bot::IsValidSpellTypeBySpellID(spell_type, spell_id)) {
|
||||||
correct_type = BotSpellTypes::Charm;
|
correct_type = UINT16_MAX;
|
||||||
}
|
|
||||||
else if (IsFearSpell(spell_id)) {
|
auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START });
|
||||||
correct_type = BotSpellTypes::Fear;
|
auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END });
|
||||||
}
|
|
||||||
else if (IsEffectInSpell(spell_id, SE_Revive)) {
|
for (int i = end; i >= start; --i) {
|
||||||
correct_type = BotSpellTypes::Resurrect;
|
if (!Bot::IsValidBotSpellType(i) || i == BotSpellTypes::InCombatBuff) {
|
||||||
}
|
continue;
|
||||||
else if (IsHarmonySpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Lull;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
teleport_zone.compare("") &&
|
|
||||||
!IsEffectInSpell(spell_id, SE_GateToHomeCity) &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Teleport;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_Succor)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Succor;
|
|
||||||
}
|
|
||||||
else if (IsEffectInSpell(spell_id, SE_BindAffinity)) {
|
|
||||||
correct_type = BotSpellTypes::BindAffinity;
|
|
||||||
}
|
|
||||||
else if (IsEffectInSpell(spell_id, SE_Identify)) {
|
|
||||||
correct_type = BotSpellTypes::Identify;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::Levitate &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_Levitate)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Levitate;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::Rune &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Rune;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::WaterBreathing &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_WaterBreathing)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::WaterBreathing;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::Size &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Size;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::Invisibility &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Invisibility;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::MovementSpeed &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_MovementSpeed)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::MovementSpeed;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
!teleport_zone.compare("") &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
(IsEffectInSpell(spell_id, SE_Translocate) || IsEffectInSpell(spell_id, SE_GateToHomeCity))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::SendHome;
|
|
||||||
}
|
|
||||||
else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) {
|
|
||||||
correct_type = BotSpellTypes::SummonCorpse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (correct_type == UINT16_MAX) {
|
if (Bot::IsValidSpellTypeBySpellID(i, spell_id)) {
|
||||||
if (
|
correct_type = i;
|
||||||
IsSummonPetSpell(spell_id) ||
|
|
||||||
IsEffectInSpell(spell_id, SE_TemporaryPets)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Pet;
|
|
||||||
}
|
|
||||||
else if (IsMesmerizeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Mez;
|
|
||||||
}
|
|
||||||
else if (IsEscapeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Escape;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_Root)
|
|
||||||
) {
|
|
||||||
if (IsAnyAESpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::AERoot;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
correct_type = BotSpellTypes::Root;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsLifetapSpell(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Lifetap;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_MovementSpeed)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Snare;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
(IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::DOT;
|
|
||||||
}
|
|
||||||
else if (IsDispelSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Dispel;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsSlowSpell(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Slow;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDebuffSpell(spell_id) &&
|
|
||||||
!IsHateReduxSpell(spell_id) &&
|
|
||||||
!IsHateSpell(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Debuff;
|
|
||||||
}
|
|
||||||
else if (IsHateReduxSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::HateRedux;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsDetrimentalSpell(spell_id) &&
|
|
||||||
IsHateSpell(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::HateLine;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
IsBuffSpell(spell_id) &&
|
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsBardSong(spell_id)
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
spell_type == BotSpellTypes::InCombatBuffSong ||
|
|
||||||
spell_type == BotSpellTypes::OutOfCombatBuffSong ||
|
|
||||||
spell_type == BotSpellTypes::PreCombatBuffSong
|
|
||||||
) {
|
|
||||||
correct_type = spell_type;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
correct_type = BotSpellTypes::OutOfCombatBuffSong;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
!IsBardSong(spell_id) &&
|
|
||||||
(
|
|
||||||
(IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) ||
|
|
||||||
(spell_type == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id))
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::InCombatBuff;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
spell_type == BotSpellTypes::PreCombatBuff &&
|
|
||||||
IsAnyBuffSpell(spell_id) &&
|
|
||||||
!IsBardSong(spell_id)
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::PreCombatBuff;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
(IsCureSpell(spell_id) && spell_type == BotSpellTypes::Cure) ||
|
|
||||||
(IsCureSpell(spell_id) && !IsAnyHealSpell(spell_id))
|
|
||||||
) {
|
|
||||||
correct_type = BotSpellTypes::Cure;
|
|
||||||
}
|
|
||||||
else if (IsAnyNukeOrStunSpell(spell_id)) {
|
|
||||||
if (IsAnyAESpell(spell_id)) {
|
|
||||||
if (IsAERainSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::AERains;
|
|
||||||
}
|
|
||||||
else if (IsPBAENukeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::PBAENuke;
|
|
||||||
}
|
|
||||||
else if (IsStunSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::AEStun;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
correct_type = BotSpellTypes::AENukes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsStunSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Stun;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
correct_type = BotSpellTypes::Nuke;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsAnyHealSpell(spell_id)) {
|
|
||||||
if (IsGroupSpell(spell_id)) {
|
|
||||||
if (IsGroupCompleteHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::GroupCompleteHeals;
|
|
||||||
}
|
|
||||||
else if (IsGroupHealOverTimeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::GroupHoTHeals;
|
|
||||||
}
|
|
||||||
else if (IsRegularGroupHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::GroupHeals;
|
|
||||||
}
|
|
||||||
|
|
||||||
return correct_type;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if (IsVeryFastHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::VeryFastHeals;
|
|
||||||
}
|
|
||||||
else if (IsFastHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::FastHeals;
|
|
||||||
}
|
|
||||||
else if (IsCompleteHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::CompleteHeal;
|
|
||||||
}
|
|
||||||
else if (IsHealOverTimeSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::HoTHeals;
|
|
||||||
}
|
|
||||||
else if (IsRegularSingleTargetHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::RegularHeal;
|
|
||||||
}
|
|
||||||
else if (IsRegularPetHealSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::RegularHeal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (IsAnyBuffSpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::Buff;
|
|
||||||
|
|
||||||
if (IsResistanceOnlySpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::ResistBuffs;
|
|
||||||
}
|
|
||||||
else if (IsDamageShieldOnlySpell(spell_id)) {
|
|
||||||
correct_type = BotSpellTypes::DamageShields;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -708,3 +468,31 @@ uint16 GetPetBotSpellType(uint16 spell_type) {
|
|||||||
|
|
||||||
return spell_type;
|
return spell_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsBotBuffSpellType(uint16 spell_type) {
|
||||||
|
switch (spell_type) {
|
||||||
|
case BotSpellTypes::Buff:
|
||||||
|
case BotSpellTypes::PetBuffs:
|
||||||
|
case BotSpellTypes::ResistBuffs:
|
||||||
|
case BotSpellTypes::PetResistBuffs:
|
||||||
|
case BotSpellTypes::DamageShields:
|
||||||
|
case BotSpellTypes::PetDamageShields:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BotRequiresLoSToCast(uint16 spell_type, uint16 spell_id) {
|
||||||
|
if (!BotSpellTypeRequiresTarget(spell_type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsTargetRequiredForSpell(spell_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|||||||
@@ -936,3 +936,11 @@ std::string Strings::Slugify(const std::string& input, const std::string& separa
|
|||||||
|
|
||||||
return slug;
|
return slug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Strings::IsValidJson(const std::string &json)
|
||||||
|
{
|
||||||
|
rapidjson::Document doc;
|
||||||
|
rapidjson::ParseResult result = doc.Parse(json.c_str());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
#include <cereal/external/rapidjson/document.h>
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
// this doesn't appear to affect linux-based systems..need feedback for _WIN64
|
// this doesn't appear to affect linux-based systems..need feedback for _WIN64
|
||||||
@@ -188,6 +189,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string Slugify(const std::string &input, const std::string &separator = "-");
|
static std::string Slugify(const std::string &input, const std::string &separator = "-");
|
||||||
|
static bool IsValidJson(const std::string& json);
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::string StringFormat(const char *format, ...);
|
const std::string StringFormat(const char *format, ...);
|
||||||
|
|||||||
@@ -196,3 +196,25 @@ const uint32 Timer::SetCurrentTime()
|
|||||||
return current_time;
|
return current_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint32 Timer::RollForward(uint32 seconds)
|
||||||
|
{
|
||||||
|
struct timeval read_time{};
|
||||||
|
uint32 this_time;
|
||||||
|
|
||||||
|
gettimeofday(&read_time, nullptr);
|
||||||
|
this_time = read_time.tv_sec * 1000 + read_time.tv_usec / 1000;
|
||||||
|
|
||||||
|
if (last_time == 0) {
|
||||||
|
current_time = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
current_time += this_time - last_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_time = this_time;
|
||||||
|
|
||||||
|
// Roll forward the specified number of seconds (converted to milliseconds)
|
||||||
|
current_time += seconds * 1000;
|
||||||
|
|
||||||
|
return current_time;
|
||||||
|
}
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ public:
|
|||||||
inline uint32 GetDuration() { return(timer_time); }
|
inline uint32 GetDuration() { return(timer_time); }
|
||||||
|
|
||||||
static const uint32 SetCurrentTime();
|
static const uint32 SetCurrentTime();
|
||||||
|
static const uint32 RollForward(uint32 seconds);
|
||||||
static const uint32 GetCurrentTime();
|
static const uint32 GetCurrentTime();
|
||||||
static const uint32 GetTimeSeconds();
|
static const uint32 GetTimeSeconds();
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
// Build variables
|
// Build variables
|
||||||
// these get injected during the build pipeline
|
// these get injected during the build pipeline
|
||||||
#define CURRENT_VERSION "23.0.2-dev" // always append -dev to the current version for custom-builds
|
#define CURRENT_VERSION "23.4.0-dev" // always append -dev to the current version for custom-builds
|
||||||
#define LOGIN_VERSION "0.8.0"
|
#define LOGIN_VERSION "0.8.0"
|
||||||
#define COMPILE_DATE __DATE__
|
#define COMPILE_DATE __DATE__
|
||||||
#define COMPILE_TIME __TIME__
|
#define COMPILE_TIME __TIME__
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define CURRENT_BINARY_DATABASE_VERSION 9306
|
#define CURRENT_BINARY_DATABASE_VERSION 9321
|
||||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
|
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
|
|||||||
packet.ToString()
|
packet.ToString()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (packet.Length() < sizeof(UsertoWorldResponseLegacy_Struct)) {
|
if (packet.Length() < sizeof(UsertoWorldResponseLegacy)) {
|
||||||
LogError(
|
LogError(
|
||||||
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
||||||
"but was too small. Discarded to avoid buffer overrun"
|
"but was too small. Discarded to avoid buffer overrun"
|
||||||
@@ -152,7 +152,7 @@ void WorldServer::ProcessUserToWorldResponseLegacy(uint16_t opcode, const EQ::Ne
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *res = (UsertoWorldResponseLegacy_Struct *) packet.Data();
|
auto *res = (UsertoWorldResponseLegacy *) packet.Data();
|
||||||
|
|
||||||
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
||||||
Client *c = server.client_manager->GetClient(res->lsaccountid, "eqemu");
|
Client *c = server.client_manager->GetClient(res->lsaccountid, "eqemu");
|
||||||
@@ -229,7 +229,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
|
|||||||
packet.ToString()
|
packet.ToString()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (packet.Length() < sizeof(UsertoWorldResponse_Struct)) {
|
if (packet.Length() < sizeof(UsertoWorldResponse)) {
|
||||||
LogError(
|
LogError(
|
||||||
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
"Received application packet from server that had opcode ServerOP_UsertoWorldResp, "
|
||||||
"but was too small. Discarded to avoid buffer overrun"
|
"but was too small. Discarded to avoid buffer overrun"
|
||||||
@@ -238,7 +238,7 @@ void WorldServer::ProcessUserToWorldResponse(uint16_t opcode, const EQ::Net::Pac
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = (UsertoWorldResponse_Struct *) packet.Data();
|
auto res = (UsertoWorldResponse *) packet.Data();
|
||||||
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
LogDebug("Trying to find client with user id of [{}]", res->lsaccountid);
|
||||||
|
|
||||||
Client *c = server.client_manager->GetClient(
|
Client *c = server.client_manager->GetClient(
|
||||||
@@ -358,14 +358,12 @@ void WorldServer::ProcessLSAccountUpdate(uint16_t opcode, const EQ::Net::Packet
|
|||||||
void WorldServer::HandleNewWorldserver(LoginserverNewWorldRequest *req)
|
void WorldServer::HandleNewWorldserver(LoginserverNewWorldRequest *req)
|
||||||
{
|
{
|
||||||
if (m_is_server_logged_in) {
|
if (m_is_server_logged_in) {
|
||||||
LogError(
|
LogInfo("Login server was already marked as logged in, returning");
|
||||||
"Login server was already marked as logged in, aborting"
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!HandleNewWorldserverValidation(req)) {
|
if (!HandleNewWorldserverValidation(req)) {
|
||||||
LogError("WorldServer::HandleNewWorldserver failed validation rules");
|
LogError("failed validation rules");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,7 +492,7 @@ void WorldServer::HandleWorldserverStatusUpdate(LoginserverWorldStatusUpdate *u)
|
|||||||
void WorldServer::SendClientAuthToWorld(Client *c)
|
void WorldServer::SendClientAuthToWorld(Client *c)
|
||||||
{
|
{
|
||||||
EQ::Net::DynamicPacket outapp;
|
EQ::Net::DynamicPacket outapp;
|
||||||
ClientAuth_Struct a{};
|
ClientAuth a{};
|
||||||
|
|
||||||
a.loginserver_account_id = c->GetAccountID();
|
a.loginserver_account_id = c->GetAccountID();
|
||||||
|
|
||||||
@@ -503,7 +501,7 @@ void WorldServer::SendClientAuthToWorld(Client *c)
|
|||||||
|
|
||||||
a.lsadmin = 0;
|
a.lsadmin = 0;
|
||||||
a.is_world_admin = 0;
|
a.is_world_admin = 0;
|
||||||
a.ip = inet_addr(c->GetConnection()->GetRemoteAddr().c_str());
|
a.ip_address = inet_addr(c->GetConnection()->GetRemoteAddr().c_str());
|
||||||
strncpy(a.loginserver_name, &c->GetLoginServerName()[0], 64);
|
strncpy(a.loginserver_name, &c->GetLoginServerName()[0], 64);
|
||||||
|
|
||||||
const std::string &client_address(c->GetConnection()->GetRemoteAddr());
|
const std::string &client_address(c->GetConnection()->GetRemoteAddr());
|
||||||
@@ -521,7 +519,7 @@ void WorldServer::SendClientAuthToWorld(Client *c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct in_addr ip_addr{};
|
struct in_addr ip_addr{};
|
||||||
ip_addr.s_addr = a.ip;
|
ip_addr.s_addr = a.ip_address;
|
||||||
|
|
||||||
LogInfo(
|
LogInfo(
|
||||||
"Client authentication response: world_address [{}] client_address [{}]",
|
"Client authentication response: world_address [{}] client_address [{}]",
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ private:
|
|||||||
std::string m_server_version;
|
std::string m_server_version;
|
||||||
bool m_is_server_authorized_to_list;
|
bool m_is_server_authorized_to_list;
|
||||||
bool m_is_server_logged_in;
|
bool m_is_server_logged_in;
|
||||||
bool m_is_server_trusted;
|
bool m_is_server_trusted; // this is primarily for worldserver being able to push updates to the loginserver
|
||||||
|
|
||||||
static void FormatWorldServerName(char *name, int8 server_list_type);
|
static void FormatWorldServerName(char *name, int8 server_list_type);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -154,9 +154,9 @@ void WorldServerManager::SendUserLoginToWorldRequest(
|
|||||||
|
|
||||||
if (iter != m_world_servers.end()) {
|
if (iter != m_world_servers.end()) {
|
||||||
EQ::Net::DynamicPacket outapp;
|
EQ::Net::DynamicPacket outapp;
|
||||||
outapp.Resize(sizeof(UsertoWorldRequest_Struct));
|
outapp.Resize(sizeof(UsertoWorldRequest));
|
||||||
|
|
||||||
auto *r = reinterpret_cast<UsertoWorldRequest_Struct *>(outapp.Data());
|
auto *r = reinterpret_cast<UsertoWorldRequest *>(outapp.Data());
|
||||||
r->worldid = server_id;
|
r->worldid = server_id;
|
||||||
r->lsaccountid = client_account_id;
|
r->lsaccountid = client_account_id;
|
||||||
strncpy(r->login, client_loginserver.c_str(), 64);
|
strncpy(r->login, client_loginserver.c_str(), 64);
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "eqemu-server",
|
"name": "eqemu-server",
|
||||||
"version": "23.0.2",
|
"version": "23.4.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/EQEmu/Server.git"
|
"url": "https://github.com/EQEmu/Server.git"
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ int main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (player_event_process_timer.Check()) {
|
if (player_event_process_timer.Check()) {
|
||||||
std::jthread player_event_thread(&PlayerEventLogs::Process, &player_event_logs);
|
player_event_logs.Process();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -177,7 +177,7 @@ void ChatChannelList::SendAllChannels(Client *c) {
|
|||||||
|
|
||||||
std::string Message;
|
std::string Message;
|
||||||
|
|
||||||
char CountString[10];
|
char CountString[13];
|
||||||
|
|
||||||
while(iterator.MoreElements()) {
|
while(iterator.MoreElements()) {
|
||||||
|
|
||||||
@@ -408,7 +408,7 @@ void ChatChannel::SendChannelMembers(Client *c) {
|
|||||||
|
|
||||||
if(!c) return;
|
if(!c) return;
|
||||||
|
|
||||||
char CountString[10];
|
char CountString[13];
|
||||||
|
|
||||||
sprintf(CountString, "(%i)", MemberCount(c->GetAccountStatus()));
|
sprintf(CountString, "(%i)", MemberCount(c->GetAccountStatus()));
|
||||||
|
|
||||||
|
|||||||
@@ -746,3 +746,6 @@ OP_TradeSkillRecipeInspect=0x4f7e
|
|||||||
OP_InvokeChangePetNameImmediate=0x046d
|
OP_InvokeChangePetNameImmediate=0x046d
|
||||||
OP_InvokeChangePetName=0x4506
|
OP_InvokeChangePetName=0x4506
|
||||||
OP_ChangePetName=0x5dab
|
OP_ChangePetName=0x5dab
|
||||||
|
|
||||||
|
OP_InvokeNameChangeImmediate=0x4fe2
|
||||||
|
OP_InvokeNameChangeLazy=0x2f2e
|
||||||
|
|||||||
@@ -56,8 +56,10 @@ echo "# Running shared_memory"
|
|||||||
echo "# Running NPC hand-in tests"
|
echo "# Running NPC hand-in tests"
|
||||||
./bin/zone tests:npc-handins 2>&1 | tee test_output.log
|
./bin/zone tests:npc-handins 2>&1 | tee test_output.log
|
||||||
./bin/zone tests:npc-handins-multiquest 2>&1 | tee -a test_output.log
|
./bin/zone tests:npc-handins-multiquest 2>&1 | tee -a test_output.log
|
||||||
|
./bin/zone tests:databuckets 2>&1 | tee -a test_output.log
|
||||||
|
./bin/zone tests:zone-state 2>&1 | tee -a test_output.log
|
||||||
|
|
||||||
if grep -E -q "QueryErr|Error" test_output.log; then
|
if grep -E -q "QueryErr|Error|FAILED" test_output.log; then
|
||||||
echo "Error found in test output! Failing build."
|
echo "Error found in test output! Failing build."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ require (
|
|||||||
require (
|
require (
|
||||||
github.com/golang/protobuf v1.3.2 // indirect
|
github.com/golang/protobuf v1.3.2 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
golang.org/x/crypto v0.31.0 // indirect
|
golang.org/x/crypto v0.35.0 // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
golang.org/x/net v0.36.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD
|
|||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
|||||||
+14
-14
@@ -447,30 +447,30 @@ void Client::SendPostEnterWorld() {
|
|||||||
|
|
||||||
bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
if (app->size != sizeof(LoginInfo_Struct)) {
|
if (app->size != sizeof(LoginInfo)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *login_info = (LoginInfo_Struct *) app->pBuffer;
|
auto *r = (LoginInfo *) app->pBuffer;
|
||||||
|
|
||||||
// Quagmire - max len for name is 18, pass 15
|
// Quagmire - max len for name is 18, pass 15
|
||||||
char name[19] = {0};
|
char name[19] = {0};
|
||||||
char password[16] = {0};
|
char password[16] = {0};
|
||||||
strn0cpy(name, (char *) login_info->login_info, 18);
|
strn0cpy(name, (char *) r->login_info, 18);
|
||||||
strn0cpy(password, (char *) &(login_info->login_info[strlen(name) + 1]), 15);
|
strn0cpy(password, (char *) &(r->login_info[strlen(name) + 1]), 15);
|
||||||
|
|
||||||
LogDebug("Receiving Login Info Packet from Client | name [{0}] password [{1}]", name, password);
|
LogDebug("Receiving login info packet from client | name [{}] password [{}]", name, password);
|
||||||
|
|
||||||
if (strlen(password) <= 1) {
|
if (strlen(password) <= 1) {
|
||||||
LogInfo("Login without a password");
|
LogInfo("Login without a password");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_player_zoning = (login_info->zoning == 1);
|
is_player_zoning = (r->zoning == 1);
|
||||||
|
|
||||||
uint32 id = Strings::ToInt(name);
|
uint32 id = Strings::ToInt(name);
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
LogWarning("Receiving Login Info Packet from Client | account_id is 0 - disconnecting");
|
LogWarning("Receiving login info packet from client | account_id is 0 - disconnecting");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,15 +480,15 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
|||||||
LogClientLogin("Checking authentication id [{}] passed", id);
|
LogClientLogin("Checking authentication id [{}] passed", id);
|
||||||
if (!is_player_zoning) {
|
if (!is_player_zoning) {
|
||||||
// Track who is in and who is out of the game
|
// Track who is in and who is out of the game
|
||||||
char *inout= (char *) "";
|
std::string in_out;
|
||||||
|
|
||||||
if (cle->GetOnline() == CLE_Status::Never){
|
if (cle->GetOnline() == CLE_Status::Never) {
|
||||||
// Desktop -> Char Select
|
// Desktop -> Char Select
|
||||||
inout = (char *) "In";
|
in_out = "in";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Game -> Char Select
|
// Game -> Char Select
|
||||||
inout=(char *) "Out";
|
in_out = "out";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always at Char select at this point.
|
// Always at Char select at this point.
|
||||||
@@ -497,7 +497,7 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
|||||||
// Could use a Logging Out Completely message somewhere.
|
// Could use a Logging Out Completely message somewhere.
|
||||||
cle->SetOnline(CLE_Status::CharSelect);
|
cle->SetOnline(CLE_Status::CharSelect);
|
||||||
|
|
||||||
LogInfo("Account ({}) Logging({}) to character select :: LSID [{}] ", cle->AccountName(), inout, cle->LSID());
|
LogInfo("Account ({}) Logging ({}) to character select :: LSID [{}] ", cle->AccountName(), in_out, cle->LSID());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cle->SetOnline();
|
cle->SetOnline();
|
||||||
@@ -545,7 +545,7 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app)
|
|||||||
if (!skip_char_info && !custom_files_key.empty() && cle->Admin() < RuleI(World, CustomFilesAdminLevel)) {
|
if (!skip_char_info && !custom_files_key.empty() && cle->Admin() < RuleI(World, CustomFilesAdminLevel)) {
|
||||||
// Modified clients can utilize this unused block in login_info to send custom payloads on login
|
// Modified clients can utilize this unused block in login_info to send custom payloads on login
|
||||||
// which indicates they are using custom client files with the correct version, based on key payload.
|
// which indicates they are using custom client files with the correct version, based on key payload.
|
||||||
const auto client_key = std::string(reinterpret_cast<char*>(login_info->unknown064));
|
const auto client_key = std::string(reinterpret_cast<char*>(r->unknown064));
|
||||||
if (custom_files_key != client_key) {
|
if (custom_files_key != client_key) {
|
||||||
std::string message = fmt::format("Missing Files [{}]", RuleS(World, CustomFilesUrl) );
|
std::string message = fmt::format("Missing Files [{}]", RuleS(World, CustomFilesUrl) );
|
||||||
SendUnsupportedClientPacket(message);
|
SendUnsupportedClientPacket(message);
|
||||||
@@ -1434,7 +1434,7 @@ void Client::EnterWorld(bool TryBootup) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (TryBootup) {
|
if (TryBootup) {
|
||||||
LogInfo("Attempting autobootup of [{}] ([{}]:[{}])", zone_name, zone_id, instance_id);
|
LogInfo("Attempting autobootup of [{}] [{}] [{}]", zone_name, zone_id, instance_id);
|
||||||
autobootup_timeout.Start();
|
autobootup_timeout.Start();
|
||||||
zone_waiting_for_bootup = zoneserver_list.TriggerBootup(zone_id, instance_id);
|
zone_waiting_for_bootup = zoneserver_list.TriggerBootup(zone_id, instance_id);
|
||||||
if (zone_waiting_for_bootup == 0) {
|
if (zone_waiting_for_bootup == 0) {
|
||||||
|
|||||||
+155
-178
@@ -1,20 +1,3 @@
|
|||||||
/* EQEMu: Everquest Server Emulator
|
|
||||||
Copyright (C) 2001-2005 EQEMu Development Team (http://eqemulator.net)
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; version 2 of the License.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
|
||||||
are required to give you total support for your newly bought product;
|
|
||||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
*/
|
|
||||||
#include "../common/global_define.h"
|
#include "../common/global_define.h"
|
||||||
#include "cliententry.h"
|
#include "cliententry.h"
|
||||||
#include "clientlist.h"
|
#include "clientlist.h"
|
||||||
@@ -24,8 +7,6 @@
|
|||||||
#include "worlddb.h"
|
#include "worlddb.h"
|
||||||
#include "zoneserver.h"
|
#include "zoneserver.h"
|
||||||
#include "world_config.h"
|
#include "world_config.h"
|
||||||
#include "../common/guilds.h"
|
|
||||||
#include "../common/strings.h"
|
|
||||||
|
|
||||||
extern uint32 numplayers;
|
extern uint32 numplayers;
|
||||||
extern LoginServerList loginserverlist;
|
extern LoginServerList loginserverlist;
|
||||||
@@ -33,84 +14,79 @@ extern ClientList client_list;
|
|||||||
extern volatile bool RunLoops;
|
extern volatile bool RunLoops;
|
||||||
extern SharedTaskManager shared_task_manager;
|
extern SharedTaskManager shared_task_manager;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param in_id
|
|
||||||
* @param in_loginserver_id
|
|
||||||
* @param in_loginserver_name
|
|
||||||
* @param in_login_name
|
|
||||||
* @param in_login_key
|
|
||||||
* @param in_is_world_admin
|
|
||||||
* @param ip
|
|
||||||
* @param local
|
|
||||||
*/
|
|
||||||
ClientListEntry::ClientListEntry(
|
ClientListEntry::ClientListEntry(
|
||||||
uint32 in_id,
|
uint32 id,
|
||||||
uint32 in_loginserver_id,
|
uint32 login_server_id,
|
||||||
const char *in_loginserver_name,
|
const char *login_server_name,
|
||||||
const char *in_login_name,
|
const char *account_name,
|
||||||
const char *in_login_key,
|
const char *login_key,
|
||||||
int16 in_is_world_admin,
|
int16 is_world_admin,
|
||||||
uint32 ip,
|
uint32 ip_address,
|
||||||
uint8 local
|
uint8 local
|
||||||
)
|
)
|
||||||
: id(in_id)
|
: m_id(id)
|
||||||
{
|
{
|
||||||
ClearVars(true);
|
ClearVars(true);
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"in_id [{0}] in_loginserver_id [{1}] in_loginserver_name [{2}] in_login_name [{3}] in_login_key [{4}] "
|
"id [{}] loginserver_id [{}] loginserver_name [{}] login_name [{}] login_key [{}] is_world_admin [{}] ip [{}] local [{}]",
|
||||||
" in_is_world_admin [{5}] ip [{6}] local [{7}]",
|
id,
|
||||||
in_id,
|
login_server_id,
|
||||||
in_loginserver_id,
|
login_server_name,
|
||||||
in_loginserver_name,
|
account_name,
|
||||||
in_login_name,
|
login_key,
|
||||||
in_login_key,
|
is_world_admin,
|
||||||
in_is_world_admin,
|
ip_address,
|
||||||
ip,
|
|
||||||
local
|
local
|
||||||
);
|
);
|
||||||
|
|
||||||
pIP = ip;
|
m_ip_address = ip_address;
|
||||||
pLSID = in_loginserver_id;
|
m_login_server_id = login_server_id;
|
||||||
if (in_loginserver_id > 0) {
|
if (login_server_id > 0) {
|
||||||
paccountid = database.GetAccountIDFromLSID(in_loginserver_name, in_loginserver_id, paccountname, &padmin);
|
m_account_id = database.GetAccountIDFromLSID(
|
||||||
|
login_server_name,
|
||||||
|
login_server_id,
|
||||||
|
m_account_name,
|
||||||
|
&m_admin
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
strn0cpy(loginserver_account_name, in_login_name, sizeof(loginserver_account_name));
|
strn0cpy(m_login_account_name, account_name, sizeof(m_login_account_name));
|
||||||
strn0cpy(plskey, in_login_key, sizeof(plskey));
|
strn0cpy(m_key, login_key, sizeof(m_key));
|
||||||
strn0cpy(source_loginserver, in_loginserver_name, sizeof(source_loginserver));
|
strn0cpy(m_source_loginserver, login_server_name, sizeof(m_source_loginserver));
|
||||||
pworldadmin = in_is_world_admin;
|
|
||||||
plocal = (local == 1);
|
|
||||||
|
|
||||||
memset(pLFGComments, 0, 64);
|
m_world_admin = is_world_admin;
|
||||||
|
m_is_local = (local == 1);
|
||||||
|
|
||||||
|
memset(m_lfg_comments, 0, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer *iZS, ServerClientList_Struct *scl, CLE_Status iOnline)
|
ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer *z, ServerClientList_Struct *scl, CLE_Status online)
|
||||||
: id(in_id)
|
: m_id(in_id)
|
||||||
{
|
{
|
||||||
ClearVars(true);
|
ClearVars(true);
|
||||||
|
|
||||||
pIP = 0;
|
m_ip_address = 0;
|
||||||
pLSID = scl->LSAccountID;
|
m_login_server_id = scl->LSAccountID;
|
||||||
strn0cpy(loginserver_account_name, scl->name, sizeof(loginserver_account_name));
|
strn0cpy(m_login_account_name, scl->name, sizeof(m_login_account_name));
|
||||||
strn0cpy(plskey, scl->lskey, sizeof(plskey));
|
strn0cpy(m_key, scl->lskey, sizeof(m_key));
|
||||||
pworldadmin = 0;
|
m_world_admin = 0;
|
||||||
|
|
||||||
paccountid = scl->AccountID;
|
m_account_id = scl->AccountID;
|
||||||
strn0cpy(paccountname, scl->AccountName, sizeof(paccountname));
|
strn0cpy(m_account_name, scl->AccountName, sizeof(m_account_name));
|
||||||
padmin = scl->Admin;
|
m_admin = scl->Admin;
|
||||||
|
|
||||||
pinstance = 0;
|
m_instance = 0;
|
||||||
pLFGFromLevel = 0;
|
m_lfg_from_level = 0;
|
||||||
pLFGToLevel = 0;
|
m_lfg_to_level = 0;
|
||||||
pLFGMatchFilter = false;
|
m_lfg_match_filter = false;
|
||||||
memset(pLFGComments, 0, 64);
|
memset(m_lfg_comments, 0, 64);
|
||||||
|
|
||||||
if (iOnline >= CLE_Status::Zoning) {
|
if (online >= CLE_Status::Zoning) {
|
||||||
Update(iZS, scl, iOnline);
|
Update(z, scl, online);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SetOnline(iOnline);
|
SetOnline(online);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,16 +96,16 @@ ClientListEntry::~ClientListEntry()
|
|||||||
Camp(); // updates zoneserver's numplayers
|
Camp(); // updates zoneserver's numplayers
|
||||||
client_list.RemoveCLEReferances(this);
|
client_list.RemoveCLEReferances(this);
|
||||||
}
|
}
|
||||||
for (auto& elem : tell_queue) {
|
for (auto &elem: m_tell_queue) {
|
||||||
safe_delete_array(elem);
|
safe_delete_array(elem);
|
||||||
}
|
}
|
||||||
tell_queue.clear();
|
m_tell_queue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientListEntry::SetChar(uint32 iCharID, const char *iCharName)
|
void ClientListEntry::SetChar(uint32 iCharID, const char *iCharName)
|
||||||
{
|
{
|
||||||
pcharid = iCharID;
|
m_char_id = iCharID;
|
||||||
strn0cpy(pname, iCharName, sizeof(pname));
|
strn0cpy(m_char_name, iCharName, sizeof(m_char_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientListEntry::SetOnline(CLE_Status iOnline)
|
void ClientListEntry::SetOnline(CLE_Status iOnline)
|
||||||
@@ -142,20 +118,20 @@ void ClientListEntry::SetOnline(CLE_Status iOnline)
|
|||||||
static_cast<int>(iOnline)
|
static_cast<int>(iOnline)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (iOnline >= CLE_Status::Online && pOnline < CLE_Status::Online) {
|
if (iOnline >= CLE_Status::Online && m_online < CLE_Status::Online) {
|
||||||
numplayers++;
|
numplayers++;
|
||||||
}
|
}
|
||||||
else if (iOnline < CLE_Status::Online && pOnline >= CLE_Status::Online) {
|
else if (iOnline < CLE_Status::Online && m_online >= CLE_Status::Online) {
|
||||||
numplayers--;
|
numplayers--;
|
||||||
}
|
}
|
||||||
if (iOnline != CLE_Status::Online || pOnline < CLE_Status::Online) {
|
if (iOnline != CLE_Status::Online || m_online < CLE_Status::Online) {
|
||||||
pOnline = iOnline;
|
m_online = iOnline;
|
||||||
}
|
}
|
||||||
if (iOnline < CLE_Status::Zoning) {
|
if (iOnline < CLE_Status::Zoning) {
|
||||||
Camp();
|
Camp();
|
||||||
}
|
}
|
||||||
if (pOnline >= CLE_Status::Online) {
|
if (m_online >= CLE_Status::Online) {
|
||||||
stale = 0;
|
m_stale = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,49 +169,49 @@ void ClientListEntry::LSZoneChange(ZoneToZone_Struct *ztz)
|
|||||||
|
|
||||||
void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_Status iOnline)
|
void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_Status iOnline)
|
||||||
{
|
{
|
||||||
if (pzoneserver != iZS) {
|
if (m_zone_server != iZS) {
|
||||||
if (pzoneserver) {
|
if (m_zone_server) {
|
||||||
pzoneserver->RemovePlayer();
|
m_zone_server->RemovePlayer();
|
||||||
LSUpdate(pzoneserver);
|
LSUpdate(m_zone_server);
|
||||||
}
|
}
|
||||||
if (iZS) {
|
if (iZS) {
|
||||||
iZS->AddPlayer();
|
iZS->AddPlayer();
|
||||||
LSUpdate(iZS);
|
LSUpdate(iZS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pzoneserver = iZS;
|
m_zone_server = iZS;
|
||||||
pzone = scl->zone;
|
m_zone = scl->zone;
|
||||||
pinstance = scl->instance_id;
|
m_instance = scl->instance_id;
|
||||||
pcharid = scl->charid;
|
m_char_id = scl->charid;
|
||||||
|
|
||||||
strcpy(pname, scl->name);
|
strcpy(m_char_name, scl->name);
|
||||||
if (paccountid == 0) {
|
if (m_account_id == 0) {
|
||||||
paccountid = scl->AccountID;
|
m_account_id = scl->AccountID;
|
||||||
strcpy(paccountname, scl->AccountName);
|
strcpy(m_account_name, scl->AccountName);
|
||||||
strcpy(loginserver_account_name, scl->AccountName);
|
strcpy(m_login_account_name, scl->AccountName);
|
||||||
pIP = scl->IP;
|
m_ip_address = scl->IP;
|
||||||
pLSID = scl->LSAccountID;
|
m_login_server_id = scl->LSAccountID;
|
||||||
strn0cpy(plskey, scl->lskey, sizeof(plskey));
|
strn0cpy(m_key, scl->lskey, sizeof(m_key));
|
||||||
}
|
}
|
||||||
padmin = scl->Admin;
|
m_admin = scl->Admin;
|
||||||
plevel = scl->level;
|
m_level = scl->level;
|
||||||
pclass_ = scl->class_;
|
m_class_ = scl->class_;
|
||||||
prace = scl->race;
|
m_race = scl->race;
|
||||||
panon = scl->anon;
|
m_anon = scl->anon;
|
||||||
ptellsoff = scl->tellsoff;
|
m_tells_off = scl->tellsoff;
|
||||||
pguild_id = scl->guild_id;
|
m_guild_id = scl->guild_id;
|
||||||
pguild_rank = scl->guild_rank;
|
m_guild_rank = scl->guild_rank;
|
||||||
pguild_tribute_opt_in = scl->guild_tribute_opt_in;
|
m_guild_tribute_opt_in = scl->guild_tribute_opt_in;
|
||||||
pLFG = scl->LFG;
|
m_lfg = scl->LFG;
|
||||||
gm = scl->gm;
|
m_gm = scl->gm;
|
||||||
pClientVersion = scl->ClientVersion;
|
m_client_version = scl->ClientVersion;
|
||||||
|
|
||||||
// Fields from the LFG Window
|
// Fields from the LFG Window
|
||||||
if ((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) {
|
if ((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) {
|
||||||
pLFGFromLevel = scl->LFGFromLevel;
|
m_lfg_from_level = scl->LFGFromLevel;
|
||||||
pLFGToLevel = scl->LFGToLevel;
|
m_lfg_to_level = scl->LFGToLevel;
|
||||||
pLFGMatchFilter = scl->LFGMatchFilter;
|
m_lfg_match_filter = scl->LFGMatchFilter;
|
||||||
memcpy(pLFGComments, scl->LFGComments, sizeof(pLFGComments));
|
memcpy(m_lfg_comments, scl->LFGComments, sizeof(m_lfg_comments));
|
||||||
}
|
}
|
||||||
|
|
||||||
SetOnline(iOnline);
|
SetOnline(iOnline);
|
||||||
@@ -243,76 +219,76 @@ void ClientListEntry::Update(ZoneServer *iZS, ServerClientList_Struct *scl, CLE_
|
|||||||
|
|
||||||
void ClientListEntry::LeavingZone(ZoneServer *iZS, CLE_Status iOnline)
|
void ClientListEntry::LeavingZone(ZoneServer *iZS, CLE_Status iOnline)
|
||||||
{
|
{
|
||||||
if (iZS != 0 && iZS != pzoneserver) {
|
if (iZS != 0 && iZS != m_zone_server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SetOnline(iOnline);
|
SetOnline(iOnline);
|
||||||
|
|
||||||
shared_task_manager.RemoveActiveInvitationByCharacterID(CharID());
|
shared_task_manager.RemoveActiveInvitationByCharacterID(CharID());
|
||||||
|
|
||||||
if (pzoneserver) {
|
if (m_zone_server) {
|
||||||
pzoneserver->RemovePlayer();
|
m_zone_server->RemovePlayer();
|
||||||
LSUpdate(pzoneserver);
|
LSUpdate(m_zone_server);
|
||||||
}
|
}
|
||||||
pzoneserver = 0;
|
m_zone_server = 0;
|
||||||
pzone = 0;
|
m_zone = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientListEntry::ClearVars(bool iAll)
|
void ClientListEntry::ClearVars(bool iAll)
|
||||||
{
|
{
|
||||||
if (iAll) {
|
if (iAll) {
|
||||||
pOnline = CLE_Status::Never;
|
m_online = CLE_Status::Never;
|
||||||
stale = 0;
|
m_stale = 0;
|
||||||
|
|
||||||
pLSID = 0;
|
m_login_server_id = 0;
|
||||||
memset(loginserver_account_name, 0, sizeof(loginserver_account_name));
|
memset(m_login_account_name, 0, sizeof(m_login_account_name));
|
||||||
memset(plskey, 0, sizeof(plskey));
|
memset(m_key, 0, sizeof(m_key));
|
||||||
pworldadmin = 0;
|
m_world_admin = 0;
|
||||||
|
|
||||||
paccountid = 0;
|
m_account_id = 0;
|
||||||
memset(paccountname, 0, sizeof(paccountname));
|
memset(m_account_name, 0, sizeof(m_account_name));
|
||||||
padmin = AccountStatus::Player;
|
m_admin = AccountStatus::Player;
|
||||||
}
|
}
|
||||||
pzoneserver = 0;
|
m_zone_server = 0;
|
||||||
pzone = 0;
|
m_zone = 0;
|
||||||
pcharid = 0;
|
m_char_id = 0;
|
||||||
memset(pname, 0, sizeof(pname));
|
memset(m_char_name, 0, sizeof(m_char_name));
|
||||||
plevel = 0;
|
m_level = 0;
|
||||||
pclass_ = 0;
|
m_class_ = 0;
|
||||||
prace = 0;
|
m_race = 0;
|
||||||
panon = 0;
|
m_anon = 0;
|
||||||
ptellsoff = 0;
|
m_tells_off = 0;
|
||||||
pguild_id = GUILD_NONE;
|
m_guild_id = GUILD_NONE;
|
||||||
pguild_rank = 0;
|
m_guild_rank = 0;
|
||||||
pLFG = 0;
|
m_lfg = 0;
|
||||||
gm = 0;
|
m_gm = 0;
|
||||||
pClientVersion = 0;
|
m_client_version = 0;
|
||||||
for (auto& elem : tell_queue) {
|
for (auto &elem: m_tell_queue) {
|
||||||
safe_delete_array(elem);
|
safe_delete_array(elem);
|
||||||
}
|
}
|
||||||
tell_queue.clear();
|
m_tell_queue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientListEntry::Camp(ZoneServer *iZS)
|
void ClientListEntry::Camp(ZoneServer *iZS)
|
||||||
{
|
{
|
||||||
if (iZS != 0 && iZS != pzoneserver) {
|
if (iZS != 0 && iZS != m_zone_server) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (pzoneserver) {
|
if (m_zone_server) {
|
||||||
pzoneserver->RemovePlayer();
|
m_zone_server->RemovePlayer();
|
||||||
LSUpdate(pzoneserver);
|
LSUpdate(m_zone_server);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearVars();
|
ClearVars();
|
||||||
|
|
||||||
stale = 0;
|
m_stale = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClientListEntry::CheckStale()
|
bool ClientListEntry::CheckStale()
|
||||||
{
|
{
|
||||||
stale++;
|
m_stale++;
|
||||||
if (stale > 20) {
|
if (m_stale > 20) {
|
||||||
if (pOnline > CLE_Status::Offline) {
|
if (m_online > CLE_Status::Offline) {
|
||||||
SetOnline(CLE_Status::Offline);
|
SetOnline(CLE_Status::Offline);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,48 +300,50 @@ bool ClientListEntry::CheckStale()
|
|||||||
bool ClientListEntry::CheckAuth(uint32 loginserver_account_id, const char *key_password)
|
bool ClientListEntry::CheckAuth(uint32 loginserver_account_id, const char *key_password)
|
||||||
{
|
{
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"ls_account_id [{0}] key_password [{1}] plskey [{2}]",
|
"ls_account_id [{}] key_password [{}] key [{}]",
|
||||||
loginserver_account_id,
|
loginserver_account_id,
|
||||||
key_password,
|
key_password,
|
||||||
plskey
|
m_key
|
||||||
);
|
);
|
||||||
if (pLSID == loginserver_account_id && strncmp(plskey, key_password, 10) == 0) {
|
|
||||||
|
|
||||||
|
if (m_login_server_id == loginserver_account_id && strncmp(m_key, key_password, 10) == 0) {
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"ls_account_id [{0}] key_password [{1}] plskey [{2}] lsid [{3}] paccountid [{4}]",
|
"ls_account_id [{}] key_password [{}] m_key [{}] lsid [{}] m_account_id [{}]",
|
||||||
loginserver_account_id,
|
loginserver_account_id,
|
||||||
key_password,
|
key_password,
|
||||||
plskey,
|
m_key,
|
||||||
LSID(),
|
LSID(),
|
||||||
paccountid
|
m_account_id
|
||||||
);
|
);
|
||||||
|
|
||||||
if (paccountid == 0 && LSID() > 0) {
|
// create account if it doesn't exist
|
||||||
|
if (m_account_id == 0 && LSID() > 0) {
|
||||||
int16 default_account_status = WorldConfig::get()->DefaultStatus;
|
int16 default_account_status = WorldConfig::get()->DefaultStatus;
|
||||||
|
|
||||||
paccountid = database.CreateAccount(
|
m_account_id = database.CreateAccount(
|
||||||
loginserver_account_name,
|
m_login_account_name,
|
||||||
std::string(),
|
std::string(),
|
||||||
default_account_status,
|
default_account_status,
|
||||||
source_loginserver,
|
m_source_loginserver,
|
||||||
LSID()
|
LSID()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!paccountid) {
|
if (!m_account_id) {
|
||||||
LogInfo(
|
LogError(
|
||||||
"Error adding local account for LS login: [{0}:{1}], duplicate name",
|
"Error adding local account for LS login [{}] [{}], duplicate name",
|
||||||
source_loginserver,
|
m_source_loginserver,
|
||||||
loginserver_account_name
|
m_login_account_name
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
strn0cpy(paccountname, loginserver_account_name, sizeof(paccountname));
|
strn0cpy(m_account_name, m_login_account_name, sizeof(m_account_name));
|
||||||
padmin = default_account_status;
|
m_admin = default_account_status;
|
||||||
}
|
}
|
||||||
std::string lsworldadmin;
|
std::string lsworldadmin;
|
||||||
if (database.GetVariable("honorlsworldadmin", lsworldadmin)) {
|
if (database.GetVariable("honorlsworldadmin", lsworldadmin)) {
|
||||||
if (Strings::ToInt(lsworldadmin) == 1 && pworldadmin != 0 && (padmin < pworldadmin || padmin == AccountStatus::Player)) {
|
if (Strings::ToInt(lsworldadmin) == 1 && m_world_admin != 0 &&
|
||||||
padmin = pworldadmin;
|
(m_admin < m_world_admin || m_admin == AccountStatus::Player)) {
|
||||||
|
m_admin = m_world_admin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -380,8 +358,8 @@ void ClientListEntry::ProcessTellQueue()
|
|||||||
}
|
}
|
||||||
|
|
||||||
ServerPacket *pack;
|
ServerPacket *pack;
|
||||||
auto it = tell_queue.begin();
|
auto it = m_tell_queue.begin();
|
||||||
while (it != tell_queue.end()) {
|
while (it != m_tell_queue.end()) {
|
||||||
pack = new ServerPacket(
|
pack = new ServerPacket(
|
||||||
ServerOP_ChannelMessage,
|
ServerOP_ChannelMessage,
|
||||||
sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1
|
sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1
|
||||||
@@ -390,8 +368,7 @@ void ClientListEntry::ProcessTellQueue()
|
|||||||
Server()->SendPacket(pack);
|
Server()->SendPacket(pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
safe_delete_array(*it);
|
safe_delete_array(*it);
|
||||||
it = tell_queue.erase(it);
|
it = m_tell_queue.erase(it);
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+97
-115
@@ -8,8 +8,7 @@
|
|||||||
#include "../common/rulesys.h"
|
#include "../common/rulesys.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
typedef enum
|
typedef enum {
|
||||||
{
|
|
||||||
Never,
|
Never,
|
||||||
Offline,
|
Offline,
|
||||||
Online,
|
Online,
|
||||||
@@ -18,7 +17,7 @@ typedef enum
|
|||||||
InZone
|
InZone
|
||||||
} CLE_Status;
|
} CLE_Status;
|
||||||
|
|
||||||
static const char * CLEStatusString[] = {
|
static const char *CLEStatusString[] = {
|
||||||
"Never",
|
"Never",
|
||||||
"Offline",
|
"Offline",
|
||||||
"Online",
|
"Online",
|
||||||
@@ -33,148 +32,131 @@ struct ServerClientList_Struct;
|
|||||||
class ClientListEntry {
|
class ClientListEntry {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
|
||||||
* @param id
|
|
||||||
* @param in_loginserver_id
|
|
||||||
* @param in_loginserver_name
|
|
||||||
* @param in_login_name
|
|
||||||
* @param in_login_key
|
|
||||||
* @param in_is_world_admin
|
|
||||||
* @param ip
|
|
||||||
* @param local
|
|
||||||
*/
|
|
||||||
ClientListEntry(
|
ClientListEntry(
|
||||||
uint32 id,
|
uint32 id,
|
||||||
uint32 in_loginserver_id,
|
uint32 login_server_id,
|
||||||
const char *in_loginserver_name,
|
const char *login_server_name,
|
||||||
const char *in_login_name,
|
const char *account_name,
|
||||||
const char *in_login_key,
|
const char *login_key,
|
||||||
int16 in_is_world_admin = 0,
|
int16 is_world_admin = 0,
|
||||||
uint32 ip = 0,
|
uint32 ip_address = 0,
|
||||||
uint8 local = 0
|
uint8 local = 0
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
ClientListEntry(uint32 id, ZoneServer *z, ServerClientList_Struct *scl, CLE_Status online);
|
||||||
* @param id
|
|
||||||
* @param iZS
|
|
||||||
* @param scl
|
|
||||||
* @param iOnline
|
|
||||||
*/
|
|
||||||
ClientListEntry(uint32 id, uint32 iAccID, const char* iAccName, MD5& iMD5Pass, int16 iAdmin = AccountStatus::Player);
|
|
||||||
ClientListEntry(uint32 id, ZoneServer* iZS, ServerClientList_Struct* scl, CLE_Status iOnline);
|
|
||||||
~ClientListEntry();
|
~ClientListEntry();
|
||||||
bool CheckStale();
|
bool CheckStale();
|
||||||
void Update(ZoneServer* zoneserver, ServerClientList_Struct* scl, CLE_Status iOnline = CLE_Status::InZone);
|
void Update(ZoneServer *zoneserver, ServerClientList_Struct *scl, CLE_Status iOnline = CLE_Status::InZone);
|
||||||
void LSUpdate(ZoneServer* zoneserver);
|
void LSUpdate(ZoneServer *zoneserver);
|
||||||
void LSZoneChange(ZoneToZone_Struct* ztz);
|
void LSZoneChange(ZoneToZone_Struct *ztz);
|
||||||
bool CheckAuth(uint32 loginserver_account_id, const char* key_password);
|
bool CheckAuth(uint32 loginserver_account_id, const char *key_password);
|
||||||
void SetOnline(CLE_Status iOnline = CLE_Status::Online);
|
void SetOnline(CLE_Status iOnline = CLE_Status::Online);
|
||||||
void SetChar(uint32 iCharID, const char* iCharName);
|
void SetChar(uint32 iCharID, const char *iCharName);
|
||||||
inline CLE_Status Online() { return pOnline; }
|
inline CLE_Status Online() { return m_online; }
|
||||||
inline const uint32 GetID() const { return id; }
|
inline const uint32 GetID() const { return m_id; }
|
||||||
inline const uint32 GetIP() const { return pIP; }
|
inline const uint32 GetIP() const { return m_ip_address; }
|
||||||
inline void SetIP(const uint32& iIP) { pIP = iIP; }
|
inline void SetIP(const uint32 &iIP) { m_ip_address = iIP; }
|
||||||
inline void KeepAlive() { stale = 0; }
|
inline void KeepAlive() { m_stale = 0; }
|
||||||
inline uint8 GetStaleCounter() const { return stale; }
|
inline uint8 GetStaleCounter() const { return m_stale; }
|
||||||
void LeavingZone(ZoneServer* iZS = 0, CLE_Status iOnline = CLE_Status::Offline);
|
void LeavingZone(ZoneServer *iZS = 0, CLE_Status iOnline = CLE_Status::Offline);
|
||||||
void Camp(ZoneServer* iZS = 0);
|
void Camp(ZoneServer *iZS = 0);
|
||||||
|
|
||||||
// Login Server stuff
|
// Login Server stuff
|
||||||
inline const char* LoginServer() const { return source_loginserver; }
|
inline const char *LoginServer() const { return m_source_loginserver; }
|
||||||
inline uint32 LSID() const { return pLSID; }
|
inline uint32 LSID() const { return m_login_server_id; }
|
||||||
inline uint32 LSAccountID() const { return pLSID; }
|
inline uint32 LSAccountID() const { return m_login_server_id; }
|
||||||
inline const char* LSName() const { return loginserver_account_name; }
|
inline const char *LSName() const { return m_login_account_name; }
|
||||||
inline int16 WorldAdmin() const { return pworldadmin; }
|
inline int16 WorldAdmin() const { return m_world_admin; }
|
||||||
inline const char* GetLSKey() const { return plskey; }
|
inline const char *GetLSKey() const { return m_key; }
|
||||||
inline const CLE_Status GetOnline() const { return pOnline; }
|
inline const CLE_Status GetOnline() const { return m_online; }
|
||||||
|
|
||||||
// Account stuff
|
// Account stuff
|
||||||
inline uint32 AccountID() const { return paccountid; }
|
inline uint32 AccountID() const { return m_account_id; }
|
||||||
inline const char* AccountName() const { return paccountname; }
|
inline const char *AccountName() const { return m_account_name; }
|
||||||
inline int16 Admin() const { return padmin; }
|
inline int16 Admin() const { return m_admin; }
|
||||||
inline void SetAdmin(uint16 iAdmin) { padmin = iAdmin; }
|
inline void SetAdmin(uint16 iAdmin) { m_admin = iAdmin; }
|
||||||
|
|
||||||
// Character info
|
// Character info
|
||||||
inline ZoneServer *Server() const { return pzoneserver; }
|
inline ZoneServer *Server() const { return m_zone_server; }
|
||||||
inline void ClearServer() { pzoneserver = 0; }
|
inline void ClearServer() { m_zone_server = 0; }
|
||||||
inline uint32 CharID() const { return pcharid; }
|
inline uint32 CharID() const { return m_char_id; }
|
||||||
inline const char *name() const { return pname; }
|
inline const char *name() const { return m_char_name; }
|
||||||
inline uint32 zone() const { return pzone; }
|
inline uint32 zone() const { return m_zone; }
|
||||||
inline uint16 instance() const { return pinstance; }
|
inline uint16 instance() const { return m_instance; }
|
||||||
inline uint8 level() const { return plevel; }
|
inline uint8 level() const { return m_level; }
|
||||||
inline uint8 class_() const { return pclass_; }
|
inline uint8 class_() const { return m_class_; }
|
||||||
inline uint16 race() const { return prace; }
|
inline uint16 race() const { return m_race; }
|
||||||
inline uint8 Anon() { return panon; }
|
inline uint8 Anon() { return m_anon; }
|
||||||
inline uint8 TellsOff() const { return ptellsoff; }
|
inline uint8 TellsOff() const { return m_tells_off; }
|
||||||
inline uint32 GuildID() const { return pguild_id; }
|
inline uint32 GuildID() const { return m_guild_id; }
|
||||||
inline uint32 GuildRank() const { return pguild_rank; }
|
inline uint32 GuildRank() const { return m_guild_rank; }
|
||||||
inline bool GuildTributeOptIn() const { return pguild_tribute_opt_in; }
|
inline bool GuildTributeOptIn() const { return m_guild_tribute_opt_in; }
|
||||||
inline void SetGuild(uint32 guild_id) { pguild_id = guild_id; }
|
inline void SetGuild(uint32 guild_id) { m_guild_id = guild_id; }
|
||||||
inline void SetGuildTributeOptIn(bool opt) { pguild_tribute_opt_in = opt; }
|
inline void SetGuildTributeOptIn(bool opt) { m_guild_tribute_opt_in = opt; }
|
||||||
inline bool LFG() const { return pLFG; }
|
inline bool LFG() const { return m_lfg; }
|
||||||
inline uint8 GetGM() const { return gm; }
|
inline uint8 GetGM() const { return m_gm; }
|
||||||
inline void SetGM(uint8 igm) { gm = igm; }
|
inline void SetGM(uint8 igm) { m_gm = igm; }
|
||||||
inline void SetZone(uint32 zone) { pzone = zone; }
|
inline void SetZone(uint32 zone) { m_zone = zone; }
|
||||||
inline bool IsLocalClient() const { return plocal; }
|
inline bool IsLocalClient() const { return m_is_local; }
|
||||||
inline uint8 GetLFGFromLevel() const { return pLFGFromLevel; }
|
inline uint8 GetLFGFromLevel() const { return m_lfg_from_level; }
|
||||||
inline uint8 GetLFGToLevel() const { return pLFGToLevel; }
|
inline uint8 GetLFGToLevel() const { return m_lfg_to_level; }
|
||||||
inline bool GetLFGMatchFilter() const { return pLFGMatchFilter; }
|
inline bool GetLFGMatchFilter() const { return m_lfg_match_filter; }
|
||||||
inline const char *GetLFGComments() const { return pLFGComments; }
|
inline const char *GetLFGComments() const { return m_lfg_comments; }
|
||||||
inline uint8 GetClientVersion() { return pClientVersion; }
|
inline uint8 GetClientVersion() { return m_client_version; }
|
||||||
|
|
||||||
inline bool TellQueueFull() const { return tell_queue.size() >= RuleI(World, TellQueueSize); }
|
inline bool TellQueueFull() const { return m_tell_queue.size() >= RuleI(World, TellQueueSize); }
|
||||||
inline bool TellQueueEmpty() const { return tell_queue.empty(); }
|
inline bool TellQueueEmpty() const { return m_tell_queue.empty(); }
|
||||||
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); }
|
inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { m_tell_queue.push_back(scm); }
|
||||||
void ProcessTellQueue();
|
void ProcessTellQueue();
|
||||||
|
|
||||||
void SetPendingDzInvite(ServerPacket* pack) { m_dz_invite.reset(pack->Copy()); };
|
void SetPendingDzInvite(ServerPacket *pack) { m_dz_invite.reset(pack->Copy()); };
|
||||||
std::unique_ptr<ServerPacket> GetPendingDzInvite() { return std::move(m_dz_invite); }
|
std::unique_ptr<ServerPacket> GetPendingDzInvite() { return std::move(m_dz_invite); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ClearVars(bool iAll = false);
|
void ClearVars(bool iAll = false);
|
||||||
|
|
||||||
const uint32 id;
|
const uint32 m_id;
|
||||||
uint32 pIP;
|
uint32 m_ip_address;
|
||||||
CLE_Status pOnline;
|
CLE_Status m_online;
|
||||||
uint8 stale;
|
uint8 m_stale;
|
||||||
|
|
||||||
// Login Server stuff
|
// Login Server stuff
|
||||||
char source_loginserver[64]{}; //Loginserver we came from.
|
char m_source_loginserver[64]{}; //Loginserver we came from.
|
||||||
uint32 pLSID;
|
uint32 m_login_server_id;
|
||||||
char loginserver_account_name[32]{};
|
char m_login_account_name[32]{};
|
||||||
char plskey[16]{};
|
char m_key[16]{};
|
||||||
int16 pworldadmin; // Login server's suggested admin status setting
|
int16 m_world_admin; // Login server's suggested admin status setting
|
||||||
bool plocal;
|
bool m_is_local;
|
||||||
|
|
||||||
// Account stuff
|
// Account stuff
|
||||||
uint32 paccountid;
|
uint32 m_account_id;
|
||||||
char paccountname[32]{};
|
char m_account_name[32]{};
|
||||||
int16 padmin{};
|
int16 m_admin{};
|
||||||
|
|
||||||
// Character info
|
// Character info
|
||||||
ZoneServer* pzoneserver{};
|
ZoneServer *m_zone_server{};
|
||||||
uint32 pzone{};
|
uint32 m_zone{};
|
||||||
uint16 pinstance{};
|
uint16 m_instance{};
|
||||||
uint32 pcharid{};
|
uint32 m_char_id{};
|
||||||
char pname[64]{};
|
char m_char_name[64]{};
|
||||||
uint8 plevel{};
|
uint8 m_level{};
|
||||||
uint8 pclass_{};
|
uint8 m_class_{};
|
||||||
uint16 prace{};
|
uint16 m_race{};
|
||||||
uint8 panon{};
|
uint8 m_anon{};
|
||||||
uint8 ptellsoff{};
|
uint8 m_tells_off{};
|
||||||
uint32 pguild_id{};
|
uint32 m_guild_id{};
|
||||||
uint32 pguild_rank;
|
uint32 m_guild_rank;
|
||||||
bool pguild_tribute_opt_in{};
|
bool m_guild_tribute_opt_in{};
|
||||||
bool pLFG{};
|
bool m_lfg{};
|
||||||
uint8 gm{};
|
uint8 m_gm{};
|
||||||
uint8 pClientVersion{};
|
uint8 m_client_version{};
|
||||||
uint8 pLFGFromLevel{};
|
uint8 m_lfg_from_level{};
|
||||||
uint8 pLFGToLevel{};
|
uint8 m_lfg_to_level{};
|
||||||
bool pLFGMatchFilter{};
|
bool m_lfg_match_filter{};
|
||||||
char pLFGComments[64]{};
|
char m_lfg_comments[64]{};
|
||||||
|
|
||||||
// Tell Queue -- really a vector :D
|
// Tell Queue -- really a vector :D
|
||||||
std::vector<ServerChannelMessage_Struct *> tell_queue;
|
std::vector<ServerChannelMessage_Struct *> m_tell_queue;
|
||||||
|
|
||||||
std::unique_ptr<ServerPacket> m_dz_invite;
|
std::unique_ptr<ServerPacket> m_dz_invite;
|
||||||
};
|
};
|
||||||
|
|||||||
+92
-5
@@ -331,8 +331,26 @@ void ClientList::SendCLEList(const int16& admin, const char* to, WorldTCPConnect
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ClientList::CLEAdd(uint32 iLSID, const char *iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin, uint32 ip, uint8 local) {
|
void ClientList::CLEAdd(
|
||||||
auto tmp = new ClientListEntry(GetNextCLEID(), iLSID, iLoginServerName, iLoginName, iLoginKey, iWorldAdmin, ip, local);
|
uint32 login_server_id,
|
||||||
|
const char *login_server_name,
|
||||||
|
const char *login_name,
|
||||||
|
const char *login_key,
|
||||||
|
int16 world_admin,
|
||||||
|
uint32 ip_address,
|
||||||
|
uint8 is_local
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto tmp = new ClientListEntry(
|
||||||
|
GetNextCLEID(),
|
||||||
|
login_server_id,
|
||||||
|
login_server_name,
|
||||||
|
login_name,
|
||||||
|
login_key,
|
||||||
|
world_admin,
|
||||||
|
ip_address,
|
||||||
|
is_local
|
||||||
|
);
|
||||||
|
|
||||||
clientlist.Append(tmp);
|
clientlist.Append(tmp);
|
||||||
}
|
}
|
||||||
@@ -457,19 +475,19 @@ void ClientList::CLEKeepAlive(uint32 numupdates, uint32* wid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientListEntry *ClientList::CheckAuth(uint32 iLSID, const char *iKey)
|
ClientListEntry *ClientList::CheckAuth(uint32 loginserver_account_id, const char *key)
|
||||||
{
|
{
|
||||||
LinkedListIterator<ClientListEntry *> iterator(clientlist);
|
LinkedListIterator<ClientListEntry *> iterator(clientlist);
|
||||||
|
|
||||||
iterator.Reset();
|
iterator.Reset();
|
||||||
while (iterator.MoreElements()) {
|
while (iterator.MoreElements()) {
|
||||||
if (iterator.GetData()->CheckAuth(iLSID, iKey)) {
|
if (iterator.GetData()->CheckAuth(loginserver_account_id, key)) {
|
||||||
return iterator.GetData();
|
return iterator.GetData();
|
||||||
}
|
}
|
||||||
iterator.Advance();
|
iterator.Advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientList::SendOnlineGuildMembers(uint32 FromID, uint32 GuildID)
|
void ClientList::SendOnlineGuildMembers(uint32 FromID, uint32 GuildID)
|
||||||
@@ -1832,3 +1850,72 @@ std::map<uint32, ClientListEntry *> ClientList::GetGuildClientsWithTributeOptIn(
|
|||||||
}
|
}
|
||||||
return guild_members;
|
return guild_members;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
std::vector<uint32_t> ClientList::GetGuildZoneServers(uint32 guild_id)
|
||||||
|
{
|
||||||
|
std::vector<uint32_t> zone_server_ids;
|
||||||
|
std::unordered_set<uint32_t> seen_ids;
|
||||||
|
|
||||||
|
LinkedListIterator<ClientListEntry *> iterator(clientlist);
|
||||||
|
|
||||||
|
iterator.Reset();
|
||||||
|
while (iterator.MoreElements()) {
|
||||||
|
ClientListEntry *cle = iterator.GetData();
|
||||||
|
|
||||||
|
if (cle->Online() != CLE_Status::InZone) {
|
||||||
|
iterator.Advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cle->Server()) {
|
||||||
|
iterator.Advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cle->GuildID() == guild_id) {
|
||||||
|
uint32_t id = cle->Server()->GetID();
|
||||||
|
if (seen_ids.insert(id).second) {
|
||||||
|
zone_server_ids.emplace_back(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator.Advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
return zone_server_ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> ClientList::GetZoneServersWithGMs()
|
||||||
|
{
|
||||||
|
std::vector<uint32_t> zone_server_ids;
|
||||||
|
std::unordered_set<uint32_t> seen_ids;
|
||||||
|
LinkedListIterator<ClientListEntry *> iterator(clientlist);
|
||||||
|
|
||||||
|
iterator.Reset();
|
||||||
|
while (iterator.MoreElements()) {
|
||||||
|
ClientListEntry *cle = iterator.GetData();
|
||||||
|
|
||||||
|
if (cle->Online() != CLE_Status::InZone) {
|
||||||
|
iterator.Advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cle->Server()) {
|
||||||
|
iterator.Advance();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cle->Admin() > 0) {
|
||||||
|
uint32_t id = cle->Server()->GetID();
|
||||||
|
if (seen_ids.insert(id).second) {
|
||||||
|
zone_server_ids.emplace_back(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator.Advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
return zone_server_ids;
|
||||||
|
}
|
||||||
|
|||||||
+4
-2
@@ -51,7 +51,7 @@ public:
|
|||||||
|
|
||||||
void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl);
|
void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl);
|
||||||
void CLERemoveZSRef(ZoneServer* iZS);
|
void CLERemoveZSRef(ZoneServer* iZS);
|
||||||
ClientListEntry* CheckAuth(uint32 iLSID, const char* iKey);
|
ClientListEntry* CheckAuth(uint32 loginserver_account_id, const char* key);
|
||||||
ClientListEntry* FindCharacter(const char* name);
|
ClientListEntry* FindCharacter(const char* name);
|
||||||
ClientListEntry* FindCLEByAccountID(uint32 iAccID);
|
ClientListEntry* FindCLEByAccountID(uint32 iAccID);
|
||||||
ClientListEntry* FindCLEByCharacterID(uint32 iCharID);
|
ClientListEntry* FindCLEByCharacterID(uint32 iCharID);
|
||||||
@@ -59,7 +59,9 @@ public:
|
|||||||
void DisconnectByIP(uint32 in_ip);
|
void DisconnectByIP(uint32 in_ip);
|
||||||
void CLCheckStale();
|
void CLCheckStale();
|
||||||
void CLEKeepAlive(uint32 numupdates, uint32* wid);
|
void CLEKeepAlive(uint32 numupdates, uint32* wid);
|
||||||
void CLEAdd(uint32 iLSID, const char* iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = AccountStatus::Player, uint32 ip = 0, uint8 local=0);
|
void CLEAdd(uint32 login_server_id, const char* login_server_name, const char* login_name, const char* login_key, int16 world_admin = AccountStatus::Player, uint32 ip_address = 0, uint8 is_local=0);
|
||||||
|
std::vector<uint32_t> GetGuildZoneServers(uint32 guild_id);
|
||||||
|
std::vector<uint32_t> GetZoneServersWithGMs();
|
||||||
void UpdateClientGuild(uint32 char_id, uint32 guild_id);
|
void UpdateClientGuild(uint32 char_id, uint32 guild_id);
|
||||||
bool IsAccountInGame(uint32 iLSID);
|
bool IsAccountInGame(uint32 iLSID);
|
||||||
|
|
||||||
|
|||||||
@@ -137,8 +137,11 @@ void DynamicZone::SetSecondsRemaining(uint32_t seconds_remaining)
|
|||||||
m_expire_time = now + new_remaining;
|
m_expire_time = now + new_remaining;
|
||||||
m_duration = std::chrono::duration_cast<std::chrono::seconds>(m_expire_time - m_start_time);
|
m_duration = std::chrono::duration_cast<std::chrono::seconds>(m_expire_time - m_start_time);
|
||||||
|
|
||||||
InstanceListRepository::UpdateDuration(database,
|
InstanceListRepository::UpdateDuration(
|
||||||
GetInstanceID(), static_cast<uint32_t>(m_duration.count()));
|
database,
|
||||||
|
GetInstanceID(),
|
||||||
|
static_cast<uint32_t>(m_duration.count())
|
||||||
|
);
|
||||||
|
|
||||||
SendZonesDurationUpdate(); // update zone caches and actual instance's timer
|
SendZonesDurationUpdate(); // update zone caches and actual instance's timer
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "zoneserver.h"
|
#include "zoneserver.h"
|
||||||
#include "../common/rulesys.h"
|
#include "../common/rulesys.h"
|
||||||
#include "../common/repositories/dynamic_zone_lockouts_repository.h"
|
#include "../common/repositories/dynamic_zone_lockouts_repository.h"
|
||||||
|
#include <cereal/types/utility.hpp>
|
||||||
|
|
||||||
extern ClientList client_list;
|
extern ClientList client_list;
|
||||||
extern ZSList zoneserver_list;
|
extern ZSList zoneserver_list;
|
||||||
@@ -169,6 +170,33 @@ void DynamicZoneManager::LoadTemplates()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DynamicZoneManager::SendBulkMemberStatuses(uint32_t zone_id, uint16_t inst_id)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<uint32_t, std::vector<DynamicZoneMember>>> dzs;
|
||||||
|
dzs.reserve(dynamic_zone_cache.size());
|
||||||
|
|
||||||
|
for (const auto& [dz_id, dz] : dynamic_zone_cache)
|
||||||
|
{
|
||||||
|
dzs.emplace_back(dz_id, dz->GetMembers());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream ss;
|
||||||
|
{
|
||||||
|
cereal::BinaryOutputArchive archive(ss);
|
||||||
|
archive(dzs);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view sv = ss.view();
|
||||||
|
|
||||||
|
size_t size = sizeof(ServerDzCerealData_Struct) + sv.size();
|
||||||
|
ServerPacket pack(ServerOP_DzGetBulkMemberStatuses, static_cast<uint32_t>(size));
|
||||||
|
auto buf = reinterpret_cast<ServerDzCerealData_Struct*>(pack.pBuffer);
|
||||||
|
buf->cereal_size = static_cast<uint32_t>(sv.size());
|
||||||
|
memcpy(buf->cereal_data, sv.data(), sv.size());
|
||||||
|
|
||||||
|
zoneserver_list.SendPacket(zone_id, inst_id, &pack);
|
||||||
|
}
|
||||||
|
|
||||||
void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack)
|
void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack)
|
||||||
{
|
{
|
||||||
switch (pack->opcode)
|
switch (pack->opcode)
|
||||||
@@ -338,6 +366,15 @@ void DynamicZoneManager::HandleZoneMessage(ServerPacket* pack)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ServerOP_DzGetBulkMemberStatuses:
|
||||||
|
{
|
||||||
|
auto buf = reinterpret_cast<ServerDzCerealData_Struct*>(pack->pBuffer);
|
||||||
|
if (buf->zone_id != 0 && !dynamic_zone_cache.empty())
|
||||||
|
{
|
||||||
|
SendBulkMemberStatuses(buf->zone_id, buf->inst_id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ServerOP_DzUpdateMemberStatus:
|
case ServerOP_DzUpdateMemberStatus:
|
||||||
{
|
{
|
||||||
auto buf = reinterpret_cast<ServerDzMemberStatus_Struct*>(pack->pBuffer);
|
auto buf = reinterpret_cast<ServerDzMemberStatus_Struct*>(pack->pBuffer);
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ public:
|
|||||||
std::unordered_map<uint32_t, std::unique_ptr<DynamicZone>> dynamic_zone_cache;
|
std::unordered_map<uint32_t, std::unique_ptr<DynamicZone>> dynamic_zone_cache;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SendBulkMemberStatuses(uint32_t zone_id, uint16_t inst_id);
|
||||||
|
|
||||||
Timer m_process_throttle_timer{};
|
Timer m_process_throttle_timer{};
|
||||||
std::unordered_map<uint32_t, DynamicZoneTemplatesRepository::DynamicZoneTemplates> m_dz_templates;
|
std::unordered_map<uint32_t, DynamicZoneTemplatesRepository::DynamicZoneTemplates> m_dz_templates;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ void callGetZoneList(Json::Value &response)
|
|||||||
row["client_address"] = zone->GetCAddress();
|
row["client_address"] = zone->GetCAddress();
|
||||||
row["client_local_address"] = zone->GetCLocalAddress();
|
row["client_local_address"] = zone->GetCLocalAddress();
|
||||||
row["client_port"] = zone->GetCPort();
|
row["client_port"] = zone->GetCPort();
|
||||||
|
row["compile_version"] = zone->GetCurrentVersion();
|
||||||
|
row["compile_date"] = zone->GetCompileDate();
|
||||||
row["compile_time"] = zone->GetCompileTime();
|
row["compile_time"] = zone->GetCompileTime();
|
||||||
row["id"] = zone->GetID();
|
row["id"] = zone->GetID();
|
||||||
row["instance_id"] = zone->GetInstanceID();
|
row["instance_id"] = zone->GetInstanceID();
|
||||||
|
|||||||
+38
-38
@@ -46,7 +46,7 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
const WorldConfig *Config = WorldConfig::get();
|
const WorldConfig *Config = WorldConfig::get();
|
||||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
UsertoWorldRequestLegacy_Struct *utwr = (UsertoWorldRequestLegacy_Struct *) p.Data();
|
UsertoWorldRequestLegacy *utwr = (UsertoWorldRequestLegacy *) p.Data();
|
||||||
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
|
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
|
||||||
int16 status = database.GetAccountStatus(id);
|
int16 status = database.GetAccountStatus(id);
|
||||||
|
|
||||||
@@ -63,11 +63,11 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
|
|
||||||
ServerPacket outpack;
|
ServerPacket outpack;
|
||||||
outpack.opcode = ServerOP_UsertoWorldRespLeg;
|
outpack.opcode = ServerOP_UsertoWorldRespLeg;
|
||||||
outpack.size = sizeof(UsertoWorldResponseLegacy_Struct);
|
outpack.size = sizeof(UsertoWorldResponseLegacy);
|
||||||
outpack.pBuffer = new uchar[outpack.size];
|
outpack.pBuffer = new uchar[outpack.size];
|
||||||
memset(outpack.pBuffer, 0, outpack.size);
|
memset(outpack.pBuffer, 0, outpack.size);
|
||||||
|
|
||||||
UsertoWorldResponseLegacy_Struct *utwrs = (UsertoWorldResponseLegacy_Struct *) outpack.pBuffer;
|
UsertoWorldResponseLegacy *utwrs = (UsertoWorldResponseLegacy *) outpack.pBuffer;
|
||||||
utwrs->lsaccountid = utwr->lsaccountid;
|
utwrs->lsaccountid = utwr->lsaccountid;
|
||||||
utwrs->ToID = utwr->FromID;
|
utwrs->ToID = utwr->FromID;
|
||||||
utwrs->worldid = utwr->worldid;
|
utwrs->worldid = utwr->worldid;
|
||||||
@@ -126,7 +126,7 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
const WorldConfig *Config = WorldConfig::get();
|
const WorldConfig *Config = WorldConfig::get();
|
||||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
UsertoWorldRequest_Struct *utwr = (UsertoWorldRequest_Struct *) p.Data();
|
UsertoWorldRequest *utwr = (UsertoWorldRequest *) p.Data();
|
||||||
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
|
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
|
||||||
int16 status = database.GetAccountStatus(id);
|
int16 status = database.GetAccountStatus(id);
|
||||||
|
|
||||||
@@ -143,11 +143,11 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
|
|
||||||
ServerPacket outpack;
|
ServerPacket outpack;
|
||||||
outpack.opcode = ServerOP_UsertoWorldResp;
|
outpack.opcode = ServerOP_UsertoWorldResp;
|
||||||
outpack.size = sizeof(UsertoWorldResponse_Struct);
|
outpack.size = sizeof(UsertoWorldResponse);
|
||||||
outpack.pBuffer = new uchar[outpack.size];
|
outpack.pBuffer = new uchar[outpack.size];
|
||||||
memset(outpack.pBuffer, 0, outpack.size);
|
memset(outpack.pBuffer, 0, outpack.size);
|
||||||
|
|
||||||
UsertoWorldResponse_Struct *utwrs = (UsertoWorldResponse_Struct *) outpack.pBuffer;
|
UsertoWorldResponse *utwrs = (UsertoWorldResponse *) outpack.pBuffer;
|
||||||
utwrs->lsaccountid = utwr->lsaccountid;
|
utwrs->lsaccountid = utwr->lsaccountid;
|
||||||
utwrs->ToID = utwr->FromID;
|
utwrs->ToID = utwr->FromID;
|
||||||
strn0cpy(utwrs->login, utwr->login, 64);
|
strn0cpy(utwrs->login, utwr->login, 64);
|
||||||
@@ -208,27 +208,27 @@ void LoginServer::ProcessLSClientAuthLegacy(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto client_authentication_request = p.GetSerialize<ClientAuthLegacy_Struct>(0);
|
auto r = p.GetSerialize<ClientAuthLegacy>(0);
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"Processing Loginserver Auth Legacy | account_id [{0}] account_name [{1}] key [{2}] admin [{3}] ip [{4}] "
|
"Processing Loginserver Auth Legacy | account_id [{}] account_name [{}] key [{}] admin [{}] ip [{}] "
|
||||||
"local_network [{5}]",
|
"local_network [{}]",
|
||||||
client_authentication_request.loginserver_account_id,
|
r.loginserver_account_id,
|
||||||
client_authentication_request.loginserver_account_name,
|
r.loginserver_account_name,
|
||||||
client_authentication_request.key,
|
r.key,
|
||||||
client_authentication_request.is_world_admin,
|
r.is_world_admin,
|
||||||
client_authentication_request.ip,
|
r.ip_address,
|
||||||
client_authentication_request.is_client_from_local_network
|
r.is_client_from_local_network
|
||||||
);
|
);
|
||||||
|
|
||||||
client_list.CLEAdd(
|
client_list.CLEAdd(
|
||||||
client_authentication_request.loginserver_account_id,
|
r.loginserver_account_id,
|
||||||
"eqemu",
|
"eqemu",
|
||||||
client_authentication_request.loginserver_account_name,
|
r.loginserver_account_name,
|
||||||
client_authentication_request.key,
|
r.key,
|
||||||
client_authentication_request.is_world_admin,
|
r.is_world_admin,
|
||||||
client_authentication_request.ip,
|
r.ip_address,
|
||||||
client_authentication_request.is_client_from_local_network
|
r.is_client_from_local_network
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (std::exception &ex) {
|
catch (std::exception &ex) {
|
||||||
@@ -242,28 +242,28 @@ void LoginServer::ProcessLSClientAuth(uint16_t opcode, EQ::Net::Packet &p)
|
|||||||
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
LogNetcode("Received ServerPacket from LS OpCode {:#04x}", opcode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto client_authentication_request = p.GetSerialize<ClientAuth_Struct>(0);
|
auto r = p.GetSerialize<ClientAuth>(0);
|
||||||
|
|
||||||
LogDebug(
|
LogDebug(
|
||||||
"Processing Loginserver Auth | account_id [{0}] account_name [{1}] loginserver_name [{2}] key [{3}] "
|
"Processing Loginserver Auth | account_id [{}] account_name [{}] loginserver_name [{}] key [{}] "
|
||||||
"admin [{4}] ip [{5}] local_network [{6}]",
|
"admin [{}] ip [{}] local_network [{}]",
|
||||||
client_authentication_request.loginserver_account_id,
|
r.loginserver_account_id,
|
||||||
client_authentication_request.account_name,
|
r.account_name,
|
||||||
client_authentication_request.loginserver_name,
|
r.loginserver_name,
|
||||||
client_authentication_request.key,
|
r.key,
|
||||||
client_authentication_request.is_world_admin,
|
r.is_world_admin,
|
||||||
client_authentication_request.ip,
|
r.ip_address,
|
||||||
client_authentication_request.is_client_from_local_network
|
r.is_client_from_local_network
|
||||||
);
|
);
|
||||||
|
|
||||||
client_list.CLEAdd(
|
client_list.CLEAdd(
|
||||||
client_authentication_request.loginserver_account_id,
|
r.loginserver_account_id,
|
||||||
client_authentication_request.loginserver_name,
|
r.loginserver_name,
|
||||||
client_authentication_request.account_name,
|
r.account_name,
|
||||||
client_authentication_request.key,
|
r.key,
|
||||||
client_authentication_request.is_world_admin,
|
r.is_world_admin,
|
||||||
client_authentication_request.ip,
|
r.ip_address,
|
||||||
client_authentication_request.is_client_from_local_network
|
r.is_client_from_local_network
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
catch (std::exception &ex) {
|
catch (std::exception &ex) {
|
||||||
|
|||||||
+17
-5
@@ -89,6 +89,7 @@
|
|||||||
#include "../common/events/player_event_logs.h"
|
#include "../common/events/player_event_logs.h"
|
||||||
#include "../common/skill_caps.h"
|
#include "../common/skill_caps.h"
|
||||||
#include "../common/repositories/character_parcels_repository.h"
|
#include "../common/repositories/character_parcels_repository.h"
|
||||||
|
#include "../common/ip_util.h"
|
||||||
|
|
||||||
SkillCaps skill_caps;
|
SkillCaps skill_caps;
|
||||||
ZoneStore zone_store;
|
ZoneStore zone_store;
|
||||||
@@ -188,6 +189,11 @@ int main(int argc, char **argv)
|
|||||||
launcher_list.LoadList();
|
launcher_list.LoadList();
|
||||||
zoneserver_list.Init();
|
zoneserver_list.Init();
|
||||||
|
|
||||||
|
if (IpUtil::IsPortInUse(Config->WorldIP, Config->WorldTCPPort)) {
|
||||||
|
LogError("World port [{}] already in use", Config->WorldTCPPort);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<EQ::Net::ConsoleServer> console;
|
std::unique_ptr<EQ::Net::ConsoleServer> console;
|
||||||
if (Config->TelnetEnabled) {
|
if (Config->TelnetEnabled) {
|
||||||
LogInfo("Console (TCP) listener started on [{}:{}]", Config->TelnetIP, Config->TelnetTCPPort);
|
LogInfo("Console (TCP) listener started on [{}:{}]", Config->TelnetIP, Config->TelnetTCPPort);
|
||||||
@@ -375,11 +381,19 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
Timer player_event_process_timer(1000);
|
|
||||||
if (player_event_logs.LoadDatabaseConnection()) {
|
if (player_event_logs.LoadDatabaseConnection()) {
|
||||||
player_event_logs.Init();
|
player_event_logs.Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto event_log_processor = std::jthread([](const std::stop_token& stoken) {
|
||||||
|
while (!stoken.stop_requested()) {
|
||||||
|
if (!RuleB(Logging, PlayerEventsQSProcess)) {
|
||||||
|
player_event_logs.Process();
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
auto loop_fn = [&](EQ::Timer* t) {
|
auto loop_fn = [&](EQ::Timer* t) {
|
||||||
Timer::SetCurrentTime();
|
Timer::SetCurrentTime();
|
||||||
|
|
||||||
@@ -442,10 +456,6 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player_event_process_timer.Check()) {
|
|
||||||
std::jthread event_thread(&PlayerEventLogs::Process, &player_event_logs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PurgeInstanceTimer.Check()) {
|
if (PurgeInstanceTimer.Check()) {
|
||||||
database.PurgeExpiredInstances();
|
database.PurgeExpiredInstances();
|
||||||
database.PurgeAllDeletedDataBuckets();
|
database.PurgeAllDeletedDataBuckets();
|
||||||
@@ -496,6 +506,8 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
EQ::EventLoop::Get().Run();
|
EQ::EventLoop::Get().Run();
|
||||||
|
|
||||||
|
event_log_processor.request_stop();
|
||||||
|
|
||||||
LogInfo("World main loop completed");
|
LogInfo("World main loop completed");
|
||||||
LogInfo("Shutting down zone connections (if any)");
|
LogInfo("Shutting down zone connections (if any)");
|
||||||
zoneserver_list.KillAll();
|
zoneserver_list.KillAll();
|
||||||
|
|||||||
+20
-15
@@ -50,7 +50,7 @@ void WorldGuildManager::SendGuildRefresh(uint32 guild_id, bool name, bool motd,
|
|||||||
s->motd_change = motd;
|
s->motd_change = motd;
|
||||||
s->rank_change = rank;
|
s->rank_change = rank;
|
||||||
s->relation_change = relation;
|
s->relation_change = relation;
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(guild_id, pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ void WorldGuildManager::SendCharRefresh(uint32 old_guild_id, uint32 guild_id, ui
|
|||||||
s->guild_id = guild_id;
|
s->guild_id = guild_id;
|
||||||
s->old_guild_id = old_guild_id;
|
s->old_guild_id = old_guild_id;
|
||||||
s->char_id = charid;
|
s->char_id = charid;
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(guild_id, pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ void WorldGuildManager::SendGuildDelete(uint32 guild_id) {
|
|||||||
auto pack = new ServerPacket(ServerOP_DeleteGuild, sizeof(ServerGuildID_Struct));
|
auto pack = new ServerPacket(ServerOP_DeleteGuild, sizeof(ServerGuildID_Struct));
|
||||||
ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer;
|
ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer;
|
||||||
s->guild_id = guild_id;
|
s->guild_id = guild_id;
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(guild_id, pack);
|
||||||
safe_delete(pack);
|
safe_delete(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,15 +85,14 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer;
|
ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer;
|
||||||
LogGuilds("Received and broadcasting guild refresh for [{}], changes: name=[{}], motd=[{}], rank=d, relation=[{}]", s->guild_id, s->name_change, s->motd_change, s->rank_change, s->relation_change);
|
LogGuilds("Received and broadcasting guild refresh for [{}], changes: name=[{}], motd=[{}], rank=d, relation=[{}]", s->guild_id, s->name_change, s->motd_change, s->rank_change, s->relation_change);
|
||||||
|
|
||||||
//broadcast this packet to all zones.
|
|
||||||
zoneserver_list.SendPacket(pack);
|
|
||||||
|
|
||||||
//preform a local refresh.
|
//preform a local refresh.
|
||||||
if(!RefreshGuild(s->guild_id)) {
|
if(!RefreshGuild(s->guild_id)) {
|
||||||
LogGuilds("Unable to preform local refresh on guild [{}]", s->guild_id);
|
BaseGuildManager::RefreshGuild(s->guild_id);
|
||||||
//can we do anything?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//broadcast this packet to all zones.
|
||||||
|
zoneserver_list.SendPacketToZonesWithGuild(s->guild_id, pack);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +107,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
|
|
||||||
//broadcast this update to any zone with a member in this guild.
|
//broadcast this update to any zone with a member in this guild.
|
||||||
//because im sick of this not working, sending it to all zones, just spends a bit more bandwidth.
|
//because im sick of this not working, sending it to all zones, just spends a bit more bandwidth.
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(s->guild_id, pack);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -147,7 +146,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
auto s = (ServerGuildID_Struct *)pack->pBuffer;
|
auto s = (ServerGuildID_Struct *)pack->pBuffer;
|
||||||
RefreshGuild(s->guild_id);
|
RefreshGuild(s->guild_id);
|
||||||
|
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(s->guild_id, pack);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ServerOP_GuildPermissionUpdate:
|
case ServerOP_GuildPermissionUpdate:
|
||||||
@@ -179,7 +178,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
sg->function_value
|
sg->function_value
|
||||||
);
|
);
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(sg->guild_id, pack);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LogError("World Received ServerOP_GuildPermissionUpdate for guild [{}] function id {} with value of {} but guild could not be found.",
|
LogError("World Received ServerOP_GuildPermissionUpdate for guild [{}] function id {} with value of {} but guild could not be found.",
|
||||||
@@ -213,7 +212,7 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
rnc->rank,
|
rnc->rank,
|
||||||
rnc->rank_name
|
rnc->rank_name
|
||||||
);
|
);
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(rnc->guild_id, pack);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LogError("World Received ServerOP_GuildRankNameChange from zone for guild [{}] rank id {} with new name of {} but could not find guild.",
|
LogError("World Received ServerOP_GuildRankNameChange from zone for guild [{}] rank id {} with new name of {} but could not find guild.",
|
||||||
@@ -230,10 +229,10 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
case ServerOP_GuildChannel:
|
case ServerOP_GuildChannel:
|
||||||
case ServerOP_GuildURL:
|
case ServerOP_GuildURL:
|
||||||
case ServerOP_GuildMemberRemove:
|
case ServerOP_GuildMemberRemove:
|
||||||
case ServerOP_GuildSendGuildList:
|
|
||||||
case ServerOP_GuildMembersList:
|
case ServerOP_GuildMembersList:
|
||||||
{
|
{
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
auto in = (ServerOP_GuildMessage_Struct *) pack->pBuffer;
|
||||||
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, pack);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ServerOP_GuildMemberAdd:
|
case ServerOP_GuildMemberAdd:
|
||||||
@@ -244,9 +243,15 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
|||||||
BaseGuildManager::RefreshGuild(in->guild_id);
|
BaseGuildManager::RefreshGuild(in->guild_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, pack);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ServerOP_GuildSendGuildList: {
|
||||||
|
auto in = (ServerOP_GuildMessage_Struct *) pack->pBuffer;
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToBootedZones(pack);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LogGuilds("Unknown packet {:#04x} received from zone??", pack->opcode);
|
LogGuilds("Unknown packet {:#04x} received from zone??", pack->opcode);
|
||||||
break;
|
break;
|
||||||
@@ -451,6 +456,6 @@ void WorldGuildManager::SendGuildTributeFavorAndTimer(uint32 guild_id, uint32 fa
|
|||||||
data->tribute_timer = time;
|
data->tribute_timer = time;
|
||||||
data->trophy_timer = 0;
|
data->trophy_timer = 0;
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(sp);
|
zoneserver_list.SendPacketToZonesWithGuild(guild_id, sp);
|
||||||
safe_delete(sp)
|
safe_delete(sp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "../common/zone_store.h"
|
#include "../common/zone_store.h"
|
||||||
#include "../common/path_manager.h"
|
#include "../common/path_manager.h"
|
||||||
#include "../common/database/database_update.h"
|
#include "../common/database/database_update.h"
|
||||||
|
#include "../common/repositories/zone_state_spawns_repository.h"
|
||||||
|
|
||||||
extern ZSList zoneserver_list;
|
extern ZSList zoneserver_list;
|
||||||
extern WorldConfig Config;
|
extern WorldConfig Config;
|
||||||
@@ -101,6 +102,13 @@ bool WorldBoot::HandleCommandInput(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if we ran a valid command, this whole CLI handler needs to be improved at a later time
|
||||||
|
std::string arg1 = argc >= 2 ? argv[1] : "";
|
||||||
|
if (argc >= 2 && !Strings::Contains(arg1, ":")) {
|
||||||
|
std::cout << "Invalid command, use --help to see available commands" << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,6 +413,11 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
|
|||||||
LogInfo("Cleaning up instance corpses");
|
LogInfo("Cleaning up instance corpses");
|
||||||
database.CleanupInstanceCorpses();
|
database.CleanupInstanceCorpses();
|
||||||
|
|
||||||
|
if (RuleB(Zone, StateSavingOnShutdown)) {
|
||||||
|
ZoneStateSpawnsRepository::PurgeInvalidZoneStates(database);
|
||||||
|
ZoneStateSpawnsRepository::PurgeOldZoneStates(database);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+234
-276
@@ -29,18 +29,16 @@
|
|||||||
#include "../common/repositories/inventory_repository.h"
|
#include "../common/repositories/inventory_repository.h"
|
||||||
#include "../common/repositories/criteria/content_filter_criteria.h"
|
#include "../common/repositories/criteria/content_filter_criteria.h"
|
||||||
#include "../common/zone_store.h"
|
#include "../common/zone_store.h"
|
||||||
|
#include "../common/repositories/character_data_repository.h"
|
||||||
|
#include "../common/repositories/character_bind_repository.h"
|
||||||
|
#include "../common/repositories/character_material_repository.h"
|
||||||
|
#include "../common/repositories/start_zones_repository.h"
|
||||||
|
|
||||||
WorldDatabase database;
|
WorldDatabase database;
|
||||||
WorldDatabase content_db;
|
WorldDatabase content_db;
|
||||||
extern std::vector<RaceClassAllocation> character_create_allocations;
|
extern std::vector<RaceClassAllocation> character_create_allocations;
|
||||||
extern std::vector<RaceClassCombos> character_create_race_class_combos;
|
extern std::vector<RaceClassCombos> character_create_race_class_combos;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param account_id
|
|
||||||
* @param out_app
|
|
||||||
* @param client_version_bit
|
|
||||||
*/
|
|
||||||
void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit)
|
void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **out_app, uint32 client_version_bit)
|
||||||
{
|
{
|
||||||
EQ::versions::ClientVersion
|
EQ::versions::ClientVersion
|
||||||
@@ -56,187 +54,177 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o
|
|||||||
character_limit = 8;
|
character_limit = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string character_list_query = fmt::format(
|
auto characters = CharacterDataRepository::GetWhere(
|
||||||
SQL(
|
database,
|
||||||
SELECT
|
fmt::format(
|
||||||
`id`,
|
"`account_id` = {} AND `deleted_at` IS NULL ORDER BY `name` LIMIT {}",
|
||||||
`name`,
|
|
||||||
`gender`,
|
|
||||||
`race`,
|
|
||||||
`class`,
|
|
||||||
`level`,
|
|
||||||
`deity`,
|
|
||||||
`last_login`,
|
|
||||||
`time_played`,
|
|
||||||
`hair_color`,
|
|
||||||
`beard_color`,
|
|
||||||
`eye_color_1`,
|
|
||||||
`eye_color_2`,
|
|
||||||
`hair_style`,
|
|
||||||
`beard`,
|
|
||||||
`face`,
|
|
||||||
`drakkin_heritage`,
|
|
||||||
`drakkin_tattoo`,
|
|
||||||
`drakkin_details`,
|
|
||||||
`zone_id`
|
|
||||||
FROM
|
|
||||||
`character_data`
|
|
||||||
WHERE
|
|
||||||
`account_id` = {}
|
|
||||||
AND
|
|
||||||
`deleted_at` IS NULL
|
|
||||||
ORDER BY `name`
|
|
||||||
LIMIT {}
|
|
||||||
),
|
|
||||||
account_id,
|
account_id,
|
||||||
character_limit
|
character_limit
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
auto results = database.QueryDatabase(character_list_query);
|
size_t character_count = characters.size();
|
||||||
size_t character_count = results.RowCount();
|
if (characters.empty()) {
|
||||||
if (character_count == 0) {
|
|
||||||
*out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
|
*out_app = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
|
||||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer;
|
auto *cs = (CharacterSelect_Struct *) (*out_app)->pBuffer;
|
||||||
cs->CharCount = 0;
|
cs->CharCount = 0;
|
||||||
cs->TotalChars = character_limit;
|
cs->TotalChars = character_limit;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> character_ids;
|
||||||
|
for (auto &e: characters) {
|
||||||
|
character_ids.push_back(e.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& inventories = InventoryRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`character_id` IN ({}) AND `slot_id` BETWEEN {} AND {}",
|
||||||
|
Strings::Join(character_ids, ","),
|
||||||
|
EQ::invslot::slotHead,
|
||||||
|
EQ::invslot::slotFeet
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto& character_binds = CharacterBindRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`id` IN ({}) ORDER BY id, slot",
|
||||||
|
Strings::Join(character_ids, ",")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const auto& character_materials = CharacterMaterialRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`id` IN ({}) ORDER BY id, slot",
|
||||||
|
Strings::Join(character_ids, ",")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count);
|
size_t packet_size = sizeof(CharacterSelect_Struct) + (sizeof(CharacterSelectEntry_Struct) * character_count);
|
||||||
*out_app = new EQApplicationPacket(OP_SendCharInfo, packet_size);
|
*out_app = new EQApplicationPacket(OP_SendCharInfo, packet_size);
|
||||||
|
|
||||||
unsigned char *buff_ptr = (*out_app)->pBuffer;
|
unsigned char *buff_ptr = (*out_app)->pBuffer;
|
||||||
CharacterSelect_Struct *cs = (CharacterSelect_Struct *) buff_ptr;
|
auto *cs = (CharacterSelect_Struct *) buff_ptr;
|
||||||
|
|
||||||
cs->CharCount = character_count;
|
cs->CharCount = character_count;
|
||||||
cs->TotalChars = character_limit;
|
cs->TotalChars = character_limit;
|
||||||
|
|
||||||
buff_ptr += sizeof(CharacterSelect_Struct);
|
buff_ptr += sizeof(CharacterSelect_Struct);
|
||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
for (auto &e: characters) {
|
||||||
CharacterSelectEntry_Struct *p_character_select_entry_struct = (CharacterSelectEntry_Struct *) buff_ptr;
|
auto *cse = (CharacterSelectEntry_Struct *) buff_ptr;
|
||||||
PlayerProfile_Struct pp;
|
PlayerProfile_Struct pp;
|
||||||
EQ::InventoryProfile inventory_profile;
|
EQ::InventoryProfile inv;
|
||||||
|
|
||||||
pp.SetPlayerProfileVersion(EQ::versions::ConvertClientVersionToMobVersion(client_version));
|
pp.SetPlayerProfileVersion(EQ::versions::ConvertClientVersionToMobVersion(client_version));
|
||||||
inventory_profile.SetInventoryVersion(client_version);
|
inv.SetInventoryVersion(client_version);
|
||||||
inventory_profile.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support
|
inv.SetGMInventory(true); // charsel can not interact with items..but, no harm in setting to full expansion support
|
||||||
|
|
||||||
uint32 character_id = Strings::ToUnsignedInt(row[0]);
|
uint32 character_id = e.id;
|
||||||
uint8 has_home = 0;
|
uint8 has_home = 0;
|
||||||
uint8 has_bind = 0;
|
uint8 has_bind = 0;
|
||||||
|
|
||||||
memset(&pp, 0, sizeof(PlayerProfile_Struct));
|
memset(&pp, 0, sizeof(PlayerProfile_Struct));
|
||||||
memset(p_character_select_entry_struct->Name, 0, sizeof(p_character_select_entry_struct->Name));
|
memset(cse->Name, 0, sizeof(cse->Name));
|
||||||
strcpy(p_character_select_entry_struct->Name, row[1]);
|
strcpy(cse->Name, e.name.c_str());
|
||||||
p_character_select_entry_struct->Class = (uint8) Strings::ToUnsignedInt(row[4]);
|
cse->Class = e.class_;
|
||||||
p_character_select_entry_struct->Race = (uint32) Strings::ToUnsignedInt(row[3]);
|
cse->Race = e.race;
|
||||||
p_character_select_entry_struct->Level = (uint8) Strings::ToUnsignedInt(row[5]);
|
cse->Level = e.level;
|
||||||
p_character_select_entry_struct->ShroudClass = p_character_select_entry_struct->Class;
|
cse->ShroudClass = cse->Class;
|
||||||
p_character_select_entry_struct->ShroudRace = p_character_select_entry_struct->Race;
|
cse->ShroudRace = cse->Race;
|
||||||
p_character_select_entry_struct->Zone = (uint16) Strings::ToUnsignedInt(row[19]);
|
cse->Zone = e.zone_id;
|
||||||
p_character_select_entry_struct->Instance = 0;
|
cse->Instance = 0;
|
||||||
p_character_select_entry_struct->Gender = (uint8) Strings::ToUnsignedInt(row[2]);
|
cse->Gender = e.gender;
|
||||||
p_character_select_entry_struct->Face = (uint8) Strings::ToUnsignedInt(row[15]);
|
cse->Face = e.face;
|
||||||
|
|
||||||
for (uint32 material_slot = 0; material_slot < EQ::textures::materialCount; material_slot++) {
|
for (auto &s: cse->Equip) {
|
||||||
p_character_select_entry_struct->Equip[material_slot].Material = 0;
|
s.Material = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].Unknown1 = 0;
|
s.Unknown1 = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].EliteModel = 0;
|
s.EliteModel = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].HerosForgeModel = 0;
|
s.HerosForgeModel = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].Unknown2 = 0;
|
s.Unknown2 = 0;
|
||||||
p_character_select_entry_struct->Equip[material_slot].Color = 0;
|
s.Color = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
p_character_select_entry_struct->Unknown15 = 0xFF;
|
cse->Unknown15 = 0xFF;
|
||||||
p_character_select_entry_struct->Unknown19 = 0xFF;
|
cse->Unknown19 = 0xFF;
|
||||||
p_character_select_entry_struct->DrakkinTattoo = (uint32) Strings::ToInt(row[17]);
|
cse->DrakkinTattoo = e.drakkin_tattoo;
|
||||||
p_character_select_entry_struct->DrakkinDetails = (uint32) Strings::ToInt(row[18]);
|
cse->DrakkinDetails = e.drakkin_details;
|
||||||
p_character_select_entry_struct->Deity = (uint32) Strings::ToInt(row[6]);
|
cse->Deity = e.deity;
|
||||||
p_character_select_entry_struct->PrimaryIDFile = 0; // Processed Below
|
cse->PrimaryIDFile = 0; // Processed Below
|
||||||
p_character_select_entry_struct->SecondaryIDFile = 0; // Processed Below
|
cse->SecondaryIDFile = 0; // Processed Below
|
||||||
p_character_select_entry_struct->HairColor = (uint8) Strings::ToInt(row[9]);
|
cse->HairColor = e.hair_color;
|
||||||
p_character_select_entry_struct->BeardColor = (uint8) Strings::ToInt(row[10]);
|
cse->BeardColor = e.beard_color;
|
||||||
p_character_select_entry_struct->EyeColor1 = (uint8) Strings::ToInt(row[11]);
|
cse->EyeColor1 = e.eye_color_1;
|
||||||
p_character_select_entry_struct->EyeColor2 = (uint8) Strings::ToInt(row[12]);
|
cse->EyeColor2 = e.eye_color_2;
|
||||||
p_character_select_entry_struct->HairStyle = (uint8) Strings::ToInt(row[13]);
|
cse->HairStyle = e.hair_style;
|
||||||
p_character_select_entry_struct->Beard = (uint8) Strings::ToInt(row[14]);
|
cse->Beard = e.beard;
|
||||||
p_character_select_entry_struct->GoHome = 0; // Processed Below
|
cse->GoHome = 0; // Processed Below
|
||||||
p_character_select_entry_struct->Tutorial = 0; // Processed Below
|
cse->Tutorial = 0; // Processed Below
|
||||||
p_character_select_entry_struct->DrakkinHeritage = (uint32) Strings::ToInt(row[16]);
|
cse->DrakkinHeritage = e.drakkin_heritage;
|
||||||
p_character_select_entry_struct->Unknown1 = 0;
|
cse->Unknown1 = 0;
|
||||||
p_character_select_entry_struct->Enabled = 1;
|
cse->Enabled = 1;
|
||||||
p_character_select_entry_struct->LastLogin = (uint32) Strings::ToInt(row[7]); // RoF2 value: 1212696584
|
cse->LastLogin = e.last_login; // RoF2 value: 1212696584
|
||||||
p_character_select_entry_struct->Unknown2 = 0;
|
cse->Unknown2 = 0;
|
||||||
|
|
||||||
if (RuleB(World, EnableReturnHomeButton)) {
|
if (RuleB(World, EnableReturnHomeButton)) {
|
||||||
int now = time(nullptr);
|
int now = time(nullptr);
|
||||||
if ((now - Strings::ToInt(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome))
|
if (now - e.last_login >= RuleI(World, MinOfflineTimeToReturnHome)) {
|
||||||
p_character_select_entry_struct->GoHome = 1;
|
cse->GoHome = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RuleB(World, EnableTutorialButton) && (p_character_select_entry_struct->Level <= RuleI(World, MaxLevelForTutorial)))
|
if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) {
|
||||||
p_character_select_entry_struct->Tutorial = 1;
|
cse->Tutorial = 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
// binds
|
||||||
* Bind
|
int bind_count = 0;
|
||||||
*/
|
for (auto &bind : character_binds) {
|
||||||
character_list_query = fmt::format(
|
if (bind.id != e.id) {
|
||||||
SQL(
|
continue;
|
||||||
SELECT
|
}
|
||||||
`zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`
|
|
||||||
FROM
|
if (bind.slot == 4) {
|
||||||
`character_bind`
|
|
||||||
WHERE
|
|
||||||
`id` = {}
|
|
||||||
LIMIT 5
|
|
||||||
),
|
|
||||||
character_id
|
|
||||||
);
|
|
||||||
auto results_bind = database.QueryDatabase(character_list_query);
|
|
||||||
auto bind_count = results_bind.RowCount();
|
|
||||||
for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) {
|
|
||||||
if (row_b[6] && Strings::ToInt(row_b[6]) == 4) {
|
|
||||||
has_home = 1;
|
has_home = 1;
|
||||||
// If our bind count is less than 5, we need to actually make use of this data so lets parse it
|
pp.binds[4].zone_id = bind.zone_id;
|
||||||
if (bind_count < 5) {
|
pp.binds[4].instance_id = bind.instance_id;
|
||||||
pp.binds[4].zone_id = Strings::ToInt(row_b[0]);
|
pp.binds[4].x = bind.x;
|
||||||
pp.binds[4].instance_id = Strings::ToInt(row_b[1]);
|
pp.binds[4].y = bind.y;
|
||||||
pp.binds[4].x = Strings::ToFloat(row_b[2]);
|
pp.binds[4].z = bind.z;
|
||||||
pp.binds[4].y = Strings::ToFloat(row_b[3]);
|
pp.binds[4].heading = bind.heading;
|
||||||
pp.binds[4].z = Strings::ToFloat(row_b[4]);
|
|
||||||
pp.binds[4].heading = Strings::ToFloat(row_b[5]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (row_b[6] && Strings::ToInt(row_b[6]) == 0)
|
if (bind.slot == 0) {
|
||||||
has_bind = 1;
|
has_bind = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_home == 0 || has_bind == 0) {
|
bind_count++;
|
||||||
std::string character_list_query = fmt::format(
|
}
|
||||||
SQL(
|
|
||||||
SELECT
|
|
||||||
`zone_id`, `bind_id`, `x`, `y`, `z`, `heading`
|
|
||||||
FROM
|
|
||||||
`start_zones`
|
|
||||||
WHERE
|
|
||||||
`player_class` = {}
|
|
||||||
AND
|
|
||||||
`player_deity` = {}
|
|
||||||
AND
|
|
||||||
`player_race` = {} {}
|
|
||||||
),
|
|
||||||
p_character_select_entry_struct->Class,
|
|
||||||
p_character_select_entry_struct->Deity,
|
|
||||||
p_character_select_entry_struct->Race,
|
|
||||||
ContentFilterCriteria::apply().c_str()
|
|
||||||
);
|
|
||||||
auto results_bind = content_db.QueryDatabase(character_list_query);
|
|
||||||
for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) {
|
|
||||||
/* If a bind_id is specified, make them start there */
|
|
||||||
if (Strings::ToInt(row_d[1]) != 0) {
|
|
||||||
pp.binds[4].zone_id = (uint32) Strings::ToInt(row_d[1]);
|
|
||||||
|
|
||||||
|
if (has_home == 0 || has_bind == 0) {
|
||||||
|
const auto &start_zones = StartZonesRepository::GetWhere(
|
||||||
|
content_db,
|
||||||
|
fmt::format(
|
||||||
|
"`player_class` = {} AND `player_deity` = {} AND `player_race` = {} {}",
|
||||||
|
cse->Class,
|
||||||
|
cse->Deity,
|
||||||
|
cse->Race,
|
||||||
|
ContentFilterCriteria::apply().c_str()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!start_zones.empty()) {
|
||||||
|
pp.binds[4].zone_id = start_zones[0].zone_id;
|
||||||
|
pp.binds[4].x = start_zones[0].x;
|
||||||
|
pp.binds[4].y = start_zones[0].y;
|
||||||
|
pp.binds[4].z = start_zones[0].z;
|
||||||
|
pp.binds[4].heading = start_zones[0].heading;
|
||||||
|
|
||||||
|
if (start_zones[0].bind_id != 0) {
|
||||||
|
pp.binds[4].zone_id = start_zones[0].bind_id;
|
||||||
auto z = GetZone(pp.binds[4].zone_id);
|
auto z = GetZone(pp.binds[4].zone_id);
|
||||||
if (z) {
|
if (z) {
|
||||||
pp.binds[4].x = z->safe_x;
|
pp.binds[4].x = z->safe_x;
|
||||||
@@ -244,168 +232,151 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, EQApplicationPacket **o
|
|||||||
pp.binds[4].z = z->safe_z;
|
pp.binds[4].z = z->safe_z;
|
||||||
pp.binds[4].heading = z->safe_heading;
|
pp.binds[4].heading = z->safe_heading;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
/* Otherwise, use the zone and coordinates given */
|
pp.binds[4].zone_id = start_zones[0].zone_id;
|
||||||
else {
|
pp.binds[4].x = start_zones[0].x;
|
||||||
pp.binds[4].zone_id = (uint32) Strings::ToInt(row_d[0]);
|
pp.binds[4].y = start_zones[0].y;
|
||||||
float x = Strings::ToFloat(row_d[2]);
|
pp.binds[4].z = start_zones[0].z;
|
||||||
float y = Strings::ToFloat(row_d[3]);
|
pp.binds[4].heading = start_zones[0].heading;
|
||||||
float z = Strings::ToFloat(row_d[4]);
|
|
||||||
float heading = Strings::ToFloat(row_d[5]);
|
if (pp.binds[4].x == 0 && pp.binds[4].y == 0 && pp.binds[4].z == 0 && pp.binds[4].heading == 0) {
|
||||||
if (x == 0 && y == 0 && z == 0 && heading == 0) {
|
|
||||||
auto zone = GetZone(pp.binds[4].zone_id);
|
auto zone = GetZone(pp.binds[4].zone_id);
|
||||||
if (zone) {
|
if (zone) {
|
||||||
x = zone->safe_x;
|
pp.binds[4].x = zone->safe_x;
|
||||||
y = zone->safe_y;
|
pp.binds[4].y = zone->safe_y;
|
||||||
z = zone->safe_z;
|
pp.binds[4].z = zone->safe_z;
|
||||||
heading = zone->safe_heading;
|
pp.binds[4].heading = zone->safe_heading;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pp.binds[4].x = x;
|
|
||||||
pp.binds[4].y = y;
|
|
||||||
pp.binds[4].z = z;
|
|
||||||
pp.binds[4].heading = heading;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
LogError("No start zone found for class [{}] deity [{}] race [{}]", cse->Class, cse->Deity, cse->Race);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pp.binds[0] = pp.binds[4];
|
pp.binds[0] = pp.binds[4];
|
||||||
/* If no home bind set, set it */
|
|
||||||
|
// If we don't have home set, set it
|
||||||
if (has_home == 0) {
|
if (has_home == 0) {
|
||||||
std::string query = fmt::format(
|
auto bind = CharacterBindRepository::NewEntity();
|
||||||
SQL(
|
bind.id = character_id;
|
||||||
REPLACE INTO
|
bind.zone_id = pp.binds[4].zone_id;
|
||||||
`character_bind`
|
bind.instance_id = 0;
|
||||||
(`id`, `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`)
|
bind.x = pp.binds[4].x;
|
||||||
VALUES ({}, {}, {}, {}, {}, {}, {}, {})
|
bind.y = pp.binds[4].y;
|
||||||
),
|
bind.z = pp.binds[4].z;
|
||||||
character_id,
|
bind.heading = pp.binds[4].heading;
|
||||||
pp.binds[4].zone_id,
|
bind.slot = 4;
|
||||||
0,
|
CharacterBindRepository::ReplaceOne(*this, bind);
|
||||||
pp.binds[4].x,
|
|
||||||
pp.binds[4].y,
|
|
||||||
pp.binds[4].z,
|
|
||||||
pp.binds[4].heading,
|
|
||||||
4
|
|
||||||
);
|
|
||||||
auto results_bset = QueryDatabase(query);
|
|
||||||
}
|
}
|
||||||
/* If no regular bind set, set it */
|
|
||||||
|
// If we don't have regular bind set, set it
|
||||||
if (has_bind == 0) {
|
if (has_bind == 0) {
|
||||||
std::string query = fmt::format(
|
auto bind = CharacterBindRepository::NewEntity();
|
||||||
SQL(
|
bind.id = character_id;
|
||||||
REPLACE INTO
|
bind.zone_id = pp.binds[0].zone_id;
|
||||||
`character_bind`
|
bind.instance_id = 0;
|
||||||
(`id`, `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`)
|
bind.x = pp.binds[0].x;
|
||||||
VALUES ({}, {}, {}, {}, {}, {}, {}, {})
|
bind.y = pp.binds[0].y;
|
||||||
),
|
bind.z = pp.binds[0].z;
|
||||||
character_id,
|
bind.heading = pp.binds[0].heading;
|
||||||
pp.binds[0].zone_id,
|
bind.slot = 0;
|
||||||
0,
|
CharacterBindRepository::ReplaceOne(*this, bind);
|
||||||
pp.binds[0].x,
|
|
||||||
pp.binds[0].y,
|
|
||||||
pp.binds[0].z,
|
|
||||||
pp.binds[0].heading,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
auto results_bset = QueryDatabase(query);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* If our bind count is less than 5, then we have null data that needs to be filled in. */
|
|
||||||
|
// If our bind count is less than 5, then we have null data that needs to be filled in
|
||||||
if (bind_count < 5) {
|
if (bind_count < 5) {
|
||||||
// we know that home and main bind must be valid here, so we don't check those
|
// we know that home and main bind must be valid here, so we don't check those
|
||||||
// we also use home to fill in the null data like live does.
|
// we also use home to fill in the null data like live does.
|
||||||
|
|
||||||
|
std::vector<CharacterBindRepository::CharacterBind> binds;
|
||||||
|
|
||||||
for (int i = 1; i < 4; i++) {
|
for (int i = 1; i < 4; i++) {
|
||||||
if (pp.binds[i].zone_id != 0) // we assume 0 is the only invalid one ...
|
if (pp.binds[i].zone_id != 0) { // we assume 0 is the only invalid one ...
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string query = fmt::format(
|
|
||||||
SQL(
|
|
||||||
REPLACE INTO
|
|
||||||
`character_bind`
|
|
||||||
(`id`, `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `slot`)
|
|
||||||
VALUES ({}, {}, {}, {}, {}, {}, {}, {})
|
|
||||||
),
|
|
||||||
character_id,
|
|
||||||
pp.binds[4].zone_id,
|
|
||||||
0,
|
|
||||||
pp.binds[4].x,
|
|
||||||
pp.binds[4].y,
|
|
||||||
pp.binds[4].z,
|
|
||||||
pp.binds[4].heading,
|
|
||||||
i
|
|
||||||
);
|
|
||||||
auto results_bset = QueryDatabase(query);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
character_list_query = fmt::format(
|
auto bind = CharacterBindRepository::NewEntity();
|
||||||
SQL(
|
|
||||||
SELECT
|
bind.slot = i;
|
||||||
`slot`, `red`, `green`, `blue`, `use_tint`, `color`
|
bind.id = character_id;
|
||||||
FROM
|
bind.zone_id = pp.binds[4].zone_id;
|
||||||
`character_material`
|
bind.instance_id = 0;
|
||||||
WHERE
|
bind.x = pp.binds[4].x;
|
||||||
`id` = {}
|
bind.y = pp.binds[4].y;
|
||||||
),
|
bind.z = pp.binds[4].z;
|
||||||
character_id
|
bind.heading = pp.binds[4].heading;
|
||||||
);
|
binds.emplace_back(bind);
|
||||||
auto results_b = database.QueryDatabase(character_list_query);
|
|
||||||
uint8 slot = 0;
|
|
||||||
for (auto row_b = results_b.begin(); row_b != results_b.end(); ++row_b) {
|
|
||||||
slot = Strings::ToInt(row_b[0]);
|
|
||||||
pp.item_tint.Slot[slot].Red = Strings::ToInt(row_b[1]);
|
|
||||||
pp.item_tint.Slot[slot].Green = Strings::ToInt(row_b[2]);
|
|
||||||
pp.item_tint.Slot[slot].Blue = Strings::ToInt(row_b[3]);
|
|
||||||
pp.item_tint.Slot[slot].UseTint = Strings::ToInt(row_b[4]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetCharSelInventory(account_id, p_character_select_entry_struct->Name, &inventory_profile)) {
|
CharacterBindRepository::ReplaceMany(*this, binds);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &cm : character_materials) {
|
||||||
|
pp.item_tint.Slot[cm.slot].Red = cm.red;
|
||||||
|
pp.item_tint.Slot[cm.slot].Green = cm.green;
|
||||||
|
pp.item_tint.Slot[cm.slot].Blue = cm.blue;
|
||||||
|
pp.item_tint.Slot[cm.slot].UseTint = cm.use_tint;
|
||||||
|
pp.item_tint.Slot[cm.slot].Color = cm.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetCharSelInventory(inventories, e, &inv)) {
|
||||||
const EQ::ItemData *item = nullptr;
|
const EQ::ItemData *item = nullptr;
|
||||||
const EQ::ItemInstance *inst = nullptr;
|
const EQ::ItemInstance *inst = nullptr;
|
||||||
int16 inventory_slot = 0;
|
int16 inventory_slot = 0;
|
||||||
|
|
||||||
for (uint32 matslot = EQ::textures::textureBegin; matslot < EQ::textures::materialCount; matslot++) {
|
for (uint32 matslot = EQ::textures::textureBegin; matslot < EQ::textures::materialCount; matslot++) {
|
||||||
inventory_slot = EQ::InventoryProfile::CalcSlotFromMaterial(matslot);
|
inventory_slot = EQ::InventoryProfile::CalcSlotFromMaterial(matslot);
|
||||||
if (inventory_slot == INVALID_INDEX) { continue; }
|
if (inventory_slot == INVALID_INDEX) { continue; }
|
||||||
inst = inventory_profile.GetItem(inventory_slot);
|
inst = inv.GetItem(inventory_slot);
|
||||||
if (inst == nullptr)
|
if (inst == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
item = inst->GetItem();
|
item = inst->GetItem();
|
||||||
if (item == nullptr)
|
if (item == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (matslot > 6) {
|
if (matslot > 6) {
|
||||||
uint32 item_id_file = 0;
|
uint32 item_id_file = 0;
|
||||||
// Weapon Models
|
// Weapon Models
|
||||||
if (inst->GetOrnamentationIDFile() != 0) {
|
if (inst->GetOrnamentationIDFile() != 0) {
|
||||||
item_id_file = inst->GetOrnamentationIDFile();
|
item_id_file = inst->GetOrnamentationIDFile();
|
||||||
p_character_select_entry_struct->Equip[matslot].Material = item_id_file;
|
cse->Equip[matslot].Material = item_id_file;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
if (strlen(item->IDFile) > 2) {
|
if (strlen(item->IDFile) > 2) {
|
||||||
item_id_file = Strings::ToInt(&item->IDFile[2]);
|
item_id_file = Strings::ToInt(&item->IDFile[2]);
|
||||||
p_character_select_entry_struct->Equip[matslot].Material = item_id_file;
|
cse->Equip[matslot].Material = item_id_file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matslot == EQ::textures::weaponPrimary) {
|
if (matslot == EQ::textures::weaponPrimary) {
|
||||||
p_character_select_entry_struct->PrimaryIDFile = item_id_file;
|
cse->PrimaryIDFile = item_id_file;
|
||||||
} else {
|
|
||||||
p_character_select_entry_struct->SecondaryIDFile = item_id_file;
|
|
||||||
}
|
}
|
||||||
} else {
|
else {
|
||||||
|
cse->SecondaryIDFile = item_id_file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
// Armor Materials/Models
|
// Armor Materials/Models
|
||||||
uint32 color = (
|
uint32 color = (
|
||||||
pp.item_tint.Slot[matslot].UseTint ?
|
pp.item_tint.Slot[matslot].UseTint ?
|
||||||
pp.item_tint.Slot[matslot].Color :
|
pp.item_tint.Slot[matslot].Color :
|
||||||
inst->GetColor()
|
inst->GetColor()
|
||||||
);
|
);
|
||||||
p_character_select_entry_struct->Equip[matslot].Material = item->Material;
|
cse->Equip[matslot].Material = item->Material;
|
||||||
p_character_select_entry_struct->Equip[matslot].EliteModel = item->EliteMaterial;
|
cse->Equip[matslot].EliteModel = item->EliteMaterial;
|
||||||
p_character_select_entry_struct->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot);
|
cse->Equip[matslot].HerosForgeModel = inst->GetOrnamentHeroModel(matslot);
|
||||||
p_character_select_entry_struct->Equip[matslot].Color = color;
|
cse->Equip[matslot].Color = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
printf("Error loading inventory for %s\n", p_character_select_entry_struct->Name);
|
else {
|
||||||
|
printf("Error loading inventory for %s\n", cse->Name);
|
||||||
}
|
}
|
||||||
buff_ptr += sizeof(CharacterSelectEntry_Struct);
|
buff_ptr += sizeof(CharacterSelectEntry_Struct);
|
||||||
}
|
}
|
||||||
@@ -855,34 +826,21 @@ bool WorldDatabase::LoadCharacterCreateCombos()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is a slightly modified version of SharedDatabase::GetInventory(...) for character select use-only
|
bool WorldDatabase::GetCharSelInventory(
|
||||||
bool WorldDatabase::GetCharSelInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv)
|
const std::vector<InventoryRepository::Inventory> &inventories,
|
||||||
|
const CharacterDataRepository::CharacterData &character,
|
||||||
|
EQ::InventoryProfile *inv
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (!account_id || !name || !inv) {
|
if (inventories.empty() || !character.id || !inv) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32 character_id = GetCharacterID(name);
|
for (const auto& e : inventories) {
|
||||||
|
if (e.character_id != character.id) {
|
||||||
if (!character_id) {
|
continue;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& l = InventoryRepository::GetWhere(
|
|
||||||
*this,
|
|
||||||
fmt::format(
|
|
||||||
"`character_id` = {} AND `slot_id` BETWEEN {} AND {}",
|
|
||||||
character_id,
|
|
||||||
EQ::invslot::slotHead,
|
|
||||||
EQ::invslot::slotFeet
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (l.empty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& e : l) {
|
|
||||||
switch (e.slot_id) {
|
switch (e.slot_id) {
|
||||||
case EQ::invslot::slotFace:
|
case EQ::invslot::slotFace:
|
||||||
case EQ::invslot::slotEar2:
|
case EQ::invslot::slotEar2:
|
||||||
|
|||||||
+7
-1
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "../common/shareddb.h"
|
#include "../common/shareddb.h"
|
||||||
#include "../common/eq_packet.h"
|
#include "../common/eq_packet.h"
|
||||||
|
#include "../common/repositories/inventory_repository.h"
|
||||||
|
#include "../common/repositories/character_data_repository.h"
|
||||||
|
|
||||||
struct PlayerProfile_Struct;
|
struct PlayerProfile_Struct;
|
||||||
struct CharCreate_Struct;
|
struct CharCreate_Struct;
|
||||||
@@ -43,7 +45,11 @@ private:
|
|||||||
void SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc);
|
void SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc);
|
||||||
void SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc);
|
void SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc);
|
||||||
|
|
||||||
bool GetCharSelInventory(uint32 account_id, char* name, EQ::InventoryProfile* inv);
|
bool GetCharSelInventory(
|
||||||
|
const std::vector<InventoryRepository::Inventory> &inventories,
|
||||||
|
const CharacterDataRepository::CharacterData &character,
|
||||||
|
EQ::InventoryProfile *inv
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern WorldDatabase database;
|
extern WorldDatabase database;
|
||||||
|
|||||||
+66
-11
@@ -36,11 +36,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "shared_task_manager.h"
|
#include "shared_task_manager.h"
|
||||||
#include "dynamic_zone_manager.h"
|
#include "dynamic_zone_manager.h"
|
||||||
#include "ucs.h"
|
#include "ucs.h"
|
||||||
|
#include "clientlist.h"
|
||||||
|
|
||||||
extern uint32 numzones;
|
extern uint32 numzones;
|
||||||
extern EQ::Random emu_random;
|
extern EQ::Random emu_random;
|
||||||
extern WebInterfaceList web_interface;
|
extern WebInterfaceList web_interface;
|
||||||
extern SharedTaskManager shared_task_manager;
|
extern SharedTaskManager shared_task_manager;
|
||||||
|
extern ClientList client_list;
|
||||||
volatile bool UCSServerAvailable_ = false;
|
volatile bool UCSServerAvailable_ = false;
|
||||||
void CatchSignal(int sig_num);
|
void CatchSignal(int sig_num);
|
||||||
|
|
||||||
@@ -515,19 +517,27 @@ void ZSList::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_mins
|
|||||||
SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer);
|
SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZSList::SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message) {
|
void ZSList::SendEmoteMessageRaw(
|
||||||
if (!message)
|
const char *to,
|
||||||
|
uint32 to_guilddbid,
|
||||||
|
int16 to_minstatus,
|
||||||
|
uint32 type,
|
||||||
|
const char *message
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!message) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
auto pack = new ServerPacket;
|
auto pack = new ServerPacket;
|
||||||
|
|
||||||
pack->opcode = ServerOP_EmoteMessage;
|
pack->opcode = ServerOP_EmoteMessage;
|
||||||
pack->size = sizeof(ServerEmoteMessage_Struct) + strlen(message) + 1;
|
pack->size = sizeof(ServerEmoteMessage_Struct) + strlen(message) + 1;
|
||||||
pack->pBuffer = new uchar[pack->size];
|
pack->pBuffer = new uchar[pack->size];
|
||||||
memset(pack->pBuffer, 0, pack->size);
|
memset(pack->pBuffer, 0, pack->size);
|
||||||
ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*)pack->pBuffer;
|
ServerEmoteMessage_Struct *sem = (ServerEmoteMessage_Struct *) pack->pBuffer;
|
||||||
|
|
||||||
if (to) {
|
if (to) {
|
||||||
strcpy((char *)sem->to, to);
|
strcpy((char *) sem->to, to);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sem->to[0] = 0;
|
sem->to[0] = 0;
|
||||||
@@ -537,21 +547,36 @@ void ZSList::SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_m
|
|||||||
sem->minstatus = to_minstatus;
|
sem->minstatus = to_minstatus;
|
||||||
sem->type = type;
|
sem->type = type;
|
||||||
strcpy(&sem->message[0], message);
|
strcpy(&sem->message[0], message);
|
||||||
char tempto[64] = { 0 };
|
char tempto[64] = {0};
|
||||||
if (to)
|
if (to) {
|
||||||
strn0cpy(tempto, to, 64);
|
strn0cpy(tempto, to, 64);
|
||||||
|
}
|
||||||
|
|
||||||
if (tempto[0] == 0) {
|
if (tempto[0] == 0) {
|
||||||
|
if (to_guilddbid > 0) {
|
||||||
|
SendPacketToZonesWithGuild(to_guilddbid, pack);
|
||||||
|
}
|
||||||
|
else if (to_minstatus > 0) {
|
||||||
|
SendPacketToZonesWithGMs(pack);
|
||||||
|
} else {
|
||||||
SendPacket(pack);
|
SendPacket(pack);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
ZoneServer* zs = FindByName(to);
|
ZoneServer *zs = FindByName(to);
|
||||||
|
if (zs) {
|
||||||
if (zs != 0)
|
|
||||||
zs->SendPacket(pack);
|
zs->SendPacket(pack);
|
||||||
else
|
}
|
||||||
|
else if (to_guilddbid > 0) {
|
||||||
|
SendPacketToZonesWithGuild(to_guilddbid, pack);
|
||||||
|
}
|
||||||
|
else if (to_minstatus > 0) {
|
||||||
|
SendPacketToZonesWithGMs(pack);
|
||||||
|
}
|
||||||
|
else {
|
||||||
SendPacket(pack);
|
SendPacket(pack);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
delete pack;
|
delete pack;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,6 +896,33 @@ bool ZSList::SendPacketToBootedZones(ServerPacket* pack)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ZSList::SendPacketToZonesWithGuild(uint32 guild_id, ServerPacket* pack)
|
||||||
|
{
|
||||||
|
auto servers = client_list.GetGuildZoneServers(guild_id);
|
||||||
|
for (auto const& z : zone_server_list) {
|
||||||
|
for (auto const& server_id : servers) {
|
||||||
|
if (z->GetID() == server_id && z->GetZoneID() > 0) {
|
||||||
|
z->SendPacket(pack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZSList::SendPacketToZonesWithGMs(ServerPacket* pack)
|
||||||
|
{
|
||||||
|
for (auto const &z: zone_server_list) {
|
||||||
|
for (auto const &server_id: client_list.GetZoneServersWithGMs()) {
|
||||||
|
if (z->GetID() == server_id && z->GetZoneID() > 0) {
|
||||||
|
z->SendPacket(pack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ZSList::SendServerReload(ServerReload::Type type, uchar *packet)
|
void ZSList::SendServerReload(ServerReload::Type type, uchar *packet)
|
||||||
{
|
{
|
||||||
static auto pack = ServerPacket(ServerOP_ServerReloadRequest, sizeof(ServerReload::Request));
|
static auto pack = ServerPacket(ServerOP_ServerReloadRequest, sizeof(ServerReload::Request));
|
||||||
@@ -894,7 +946,10 @@ void ZSList::SendServerReload(ServerReload::Type type, uchar *packet)
|
|||||||
ServerReload::Type::Commands,
|
ServerReload::Type::Commands,
|
||||||
ServerReload::Type::PerlExportSettings,
|
ServerReload::Type::PerlExportSettings,
|
||||||
ServerReload::Type::DataBucketsCache,
|
ServerReload::Type::DataBucketsCache,
|
||||||
ServerReload::Type::WorldRepop
|
ServerReload::Type::Quests,
|
||||||
|
ServerReload::Type::QuestsTimerReset,
|
||||||
|
ServerReload::Type::WorldRepop,
|
||||||
|
ServerReload::Type::WorldWithRespawn
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set requires_zone_booted flag before executing reload logic
|
// Set requires_zone_booted flag before executing reload logic
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ public:
|
|||||||
bool SendPacket(ServerPacket *pack);
|
bool SendPacket(ServerPacket *pack);
|
||||||
bool SendPacket(uint32 zoneid, ServerPacket *pack);
|
bool SendPacket(uint32 zoneid, ServerPacket *pack);
|
||||||
bool SendPacket(uint32 zoneid, uint16 instanceid, ServerPacket *pack);
|
bool SendPacket(uint32 zoneid, uint16 instanceid, ServerPacket *pack);
|
||||||
|
bool SendPacketToZonesWithGuild(uint32 guild_id, ServerPacket *pack);
|
||||||
|
bool SendPacketToZonesWithGMs(ServerPacket *pack);
|
||||||
bool SendPacketToBootedZones(ServerPacket* pack);
|
bool SendPacketToBootedZones(ServerPacket* pack);
|
||||||
bool SetLockedZone(uint16 iZoneID, bool iLock);
|
bool SetLockedZone(uint16 iZoneID, bool iLock);
|
||||||
|
|
||||||
|
|||||||
+16
-7
@@ -571,8 +571,14 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (scm->guilddbid > 0) {
|
||||||
|
zoneserver_list.SendPacketToZonesWithGuild(scm->guilddbid, pack);
|
||||||
|
} else if (scm->chan_num == ChatChannel_GMSAY) {
|
||||||
|
zoneserver_list.SendPacketToZonesWithGMs(pack);
|
||||||
|
} else {
|
||||||
zoneserver_list.SendPacket(pack);
|
zoneserver_list.SendPacket(pack);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -729,13 +735,15 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
zs = zoneserver_list.FindByID(s->zone_server_id);
|
zs = zoneserver_list.FindByID(s->zone_server_id);
|
||||||
} else if (s->zone_id) {
|
} else if (s->zone_id) {
|
||||||
zs = zoneserver_list.FindByName(ZoneName(s->zone_id));
|
zs = zoneserver_list.FindByName(ZoneName(s->zone_id));
|
||||||
|
} else if (s->instance_id) {
|
||||||
|
zs = zoneserver_list.FindByInstanceID(s->instance_id);
|
||||||
} else {
|
} else {
|
||||||
zoneserver_list.SendEmoteMessage(
|
zoneserver_list.SendEmoteMessage(
|
||||||
s->admin_name,
|
s->admin_name,
|
||||||
0,
|
0,
|
||||||
AccountStatus::Player,
|
AccountStatus::Player,
|
||||||
Chat::White,
|
Chat::White,
|
||||||
"Error: SOP_ZoneShutdown: neither ID nor name specified"
|
"Error: SOP_ZoneShutdown: Zone ID, Instance ID, nor Zone Short Name specified"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1480,6 +1488,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
case ServerOP_DzSwapMembers:
|
case ServerOP_DzSwapMembers:
|
||||||
case ServerOP_DzRemoveAllMembers:
|
case ServerOP_DzRemoveAllMembers:
|
||||||
case ServerOP_DzGetMemberStatuses:
|
case ServerOP_DzGetMemberStatuses:
|
||||||
|
case ServerOP_DzGetBulkMemberStatuses:
|
||||||
case ServerOP_DzSetSecondsRemaining:
|
case ServerOP_DzSetSecondsRemaining:
|
||||||
case ServerOP_DzSetCompass:
|
case ServerOP_DzSetCompass:
|
||||||
case ServerOP_DzSetSafeReturn:
|
case ServerOP_DzSetSafeReturn:
|
||||||
@@ -1509,7 +1518,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
|
|
||||||
guild->tribute.timer.Disable();
|
guild->tribute.timer.Disable();
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(data->guild_id, pack);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1552,7 +1561,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
guild_mgr.UpdateDbGuildFavor(data->guild_id, data->favor);
|
guild_mgr.UpdateDbGuildFavor(data->guild_id, data->favor);
|
||||||
guild_mgr.UpdateDbTributeTimeRemaining(data->guild_id, data->time_remaining);
|
guild_mgr.UpdateDbTributeTimeRemaining(data->guild_id, data->time_remaining);
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToZonesWithGuild(data->guild_id, pack);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1586,7 +1595,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
data->time_remaining = in->time_remaining;
|
data->time_remaining = in->time_remaining;
|
||||||
strn0cpy(data->player_name, in->player_name, sizeof(data->player_name));
|
strn0cpy(data->player_name, in->player_name, sizeof(data->player_name));
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(out);
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, out);
|
||||||
safe_delete(out);
|
safe_delete(out);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1609,7 +1618,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
out->tribute_id_2_tier = guild->tribute.id_2_tier;
|
out->tribute_id_2_tier = guild->tribute.id_2_tier;
|
||||||
out->time_remaining = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id);
|
out->time_remaining = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id);
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(sp);
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, sp);
|
||||||
safe_delete(sp);
|
safe_delete(sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1629,7 +1638,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
out->tribute_timer = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id);
|
out->tribute_timer = guild_mgr.GetGuildTributeTimeRemaining(in->guild_id);
|
||||||
out->trophy_timer = 0;
|
out->trophy_timer = 0;
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(sp);
|
zoneserver_list.SendPacketToZonesWithGuild(in->guild_id, sp);
|
||||||
safe_delete(sp);
|
safe_delete(sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1653,7 +1662,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
out->member_time = in->member_time;
|
out->member_time = in->member_time;
|
||||||
strn0cpy(out->player_name, in->player_name, sizeof(out->player_name));
|
strn0cpy(out->player_name, in->player_name, sizeof(out->player_name));
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(sp);
|
zoneserver_list.SendPacketToZonesWithGuild(out->guild_id, sp);
|
||||||
safe_delete(sp)
|
safe_delete(sp)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ public:
|
|||||||
|
|
||||||
inline const char* GetZoneName() const { return zone_name; }
|
inline const char* GetZoneName() const { return zone_name; }
|
||||||
inline const char* GetZoneLongName() const { return long_name; }
|
inline const char* GetZoneLongName() const { return long_name; }
|
||||||
|
inline std::string GetCurrentVersion() const { return CURRENT_VERSION; }
|
||||||
|
inline std::string GetCompileDate() const { return COMPILE_DATE; }
|
||||||
const char* GetCompileTime() const{ return compiled; }
|
const char* GetCompileTime() const{ return compiled; }
|
||||||
void SetCompile(char* in_compile){ strcpy(compiled,in_compile); }
|
void SetCompile(char* in_compile){ strcpy(compiled,in_compile); }
|
||||||
inline uint32 GetZoneID() const { return zone_server_zone_id; }
|
inline uint32 GetZoneID() const { return zone_server_zone_id; }
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ SET(zone_sources
|
|||||||
zone_event_scheduler.cpp
|
zone_event_scheduler.cpp
|
||||||
zone_npc_factions.cpp
|
zone_npc_factions.cpp
|
||||||
zone_reload.cpp
|
zone_reload.cpp
|
||||||
|
zone_save_state.cpp
|
||||||
zoning.cpp
|
zoning.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -292,6 +293,7 @@ SET(zone_headers
|
|||||||
zonedump.h
|
zonedump.h
|
||||||
zone_cli.h
|
zone_cli.h
|
||||||
zone_reload.h
|
zone_reload.h
|
||||||
|
zone_save_state.h
|
||||||
zone_cli.cpp)
|
zone_cli.cpp)
|
||||||
|
|
||||||
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
|
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
|
||||||
|
|||||||
@@ -2507,6 +2507,12 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_resumed_from_zone_suspend && !IsQueuedForCorpse()) {
|
||||||
|
LogInfo("NPC [{}] is resumed from zone suspend, cannot kill until zone resume is complete.", GetCleanName());
|
||||||
|
SetHP(0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsMultiQuestEnabled()) {
|
if (IsMultiQuestEnabled()) {
|
||||||
for (auto &i: m_hand_in.items) {
|
for (auto &i: m_hand_in.items) {
|
||||||
if (i.is_multiquest_item && i.item->GetItem()->NoDrop != 0) {
|
if (i.is_multiquest_item && i.item->GetItem()->NoDrop != 0) {
|
||||||
@@ -2789,6 +2795,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
|||||||
const uint16 entity_id = GetID();
|
const uint16 entity_id = GetID();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
(
|
||||||
!HasOwner() &&
|
!HasOwner() &&
|
||||||
!IsMerc() &&
|
!IsMerc() &&
|
||||||
!GetSwarmInfo() &&
|
!GetSwarmInfo() &&
|
||||||
@@ -2814,6 +2821,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
|||||||
killer_mob && is_ldon_treasure
|
killer_mob && is_ldon_treasure
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|| IsQueuedForCorpse()
|
||||||
) {
|
) {
|
||||||
if (killer) {
|
if (killer) {
|
||||||
if (killer->GetOwner() != 0 && killer->GetOwner()->IsClient()) {
|
if (killer->GetOwner() != 0 && killer->GetOwner()->IsClient()) {
|
||||||
@@ -3038,9 +3047,15 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
|||||||
if (!other)
|
if (!other)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (other->IsDestroying())
|
||||||
|
return;
|
||||||
|
|
||||||
if (other == this)
|
if (other == this)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (other->IsClient() && (other->CastToClient()->IsZoning() || other->CastToClient()->Connected() == false))
|
||||||
|
return;
|
||||||
|
|
||||||
if (other->IsTrap())
|
if (other->IsTrap())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -690,12 +690,16 @@ void Aura::ProcessSpawns()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!e.second->IsOfClientBot()) {
|
if (!e.second->IsClient()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto c = e.second->CastToClient();
|
auto c = e.second->CastToClient();
|
||||||
|
|
||||||
|
if (!c) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
bool spawned = spawned_for.find(c->GetID()) != spawned_for.end();
|
bool spawned = spawned_for.find(c->GetID()) != spawned_for.end();
|
||||||
if (ShouldISpawnFor(c)) {
|
if (ShouldISpawnFor(c)) {
|
||||||
if (!spawned) {
|
if (!spawned) {
|
||||||
|
|||||||
+70
-280
@@ -281,31 +281,42 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_ammo_item) {
|
if (is_ammo_item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto recommended_level = is_augment ? recommended_level_override : inst->GetItemRecommendedLevel(true);
|
const auto recommended_level = is_augment ? recommended_level_override : inst->GetItemRecommendedLevel(true);
|
||||||
|
const bool meets_recommended = (IsNPC() && !RuleB(Items, NPCUseRecommendedLevels)) || current_level >= recommended_level;
|
||||||
|
|
||||||
if (IsNPC() || current_level >= recommended_level) {
|
auto CalcItemBonus = [&](int statValue) -> int {
|
||||||
b->HP += item->HP;
|
return meets_recommended ? statValue : CalcRecommendedLevelBonus(current_level, recommended_level, statValue);
|
||||||
b->Mana += item->Mana;
|
};
|
||||||
b->Endurance += item->Endur;
|
|
||||||
|
|
||||||
b->AC += item->AC;
|
auto CalcCappedItemBonus = [&](int currentStat, int bonus, int cap) -> int {
|
||||||
b->ATK += item->Attack;
|
int calc_stat = currentStat + CalcItemBonus(bonus);
|
||||||
|
return IsOfClientBotMerc() ? std::min(cap, calc_stat) : calc_stat;
|
||||||
|
};
|
||||||
|
|
||||||
b->STR += (item->AStr + item->HeroicStr);
|
b->HP += CalcItemBonus(item->HP);
|
||||||
b->STA += (item->ASta + item->HeroicSta);
|
b->Mana += CalcItemBonus(item->Mana);
|
||||||
b->DEX += (item->ADex + item->HeroicDex);
|
b->Endurance += CalcItemBonus(item->Endur);
|
||||||
b->AGI += (item->AAgi + item->HeroicAgi);
|
b->AC += CalcItemBonus(item->AC);
|
||||||
b->INT += (item->AInt + item->HeroicInt);
|
|
||||||
b->WIS += (item->AWis + item->HeroicWis);
|
|
||||||
b->CHA += (item->ACha + item->HeroicCha);
|
|
||||||
|
|
||||||
b->MR += (item->MR + item->HeroicMR);
|
b->STR += CalcItemBonus(item->AStr + item->HeroicStr);
|
||||||
b->FR += (item->FR + item->HeroicFR);
|
b->STA += CalcItemBonus(item->ASta + item->HeroicSta);
|
||||||
b->CR += (item->CR + item->HeroicCR);
|
b->DEX += CalcItemBonus(item->ADex + item->HeroicDex);
|
||||||
b->PR += (item->PR + item->HeroicPR);
|
b->AGI += CalcItemBonus(item->AAgi + item->HeroicAgi);
|
||||||
b->DR += (item->DR + item->HeroicDR);
|
b->INT += CalcItemBonus(item->AInt + item->HeroicInt);
|
||||||
b->Corrup += (item->SVCorruption + item->HeroicSVCorrup);
|
b->WIS += CalcItemBonus(item->AWis + item->HeroicWis);
|
||||||
|
b->CHA += CalcItemBonus(item->ACha + item->HeroicCha);
|
||||||
|
|
||||||
|
b->HeroicSTR += CalcItemBonus(item->HeroicStr);
|
||||||
|
b->HeroicSTA += CalcItemBonus(item->HeroicSta);
|
||||||
|
b->HeroicDEX += CalcItemBonus(item->HeroicDex);
|
||||||
|
b->HeroicAGI += CalcItemBonus(item->HeroicAgi);
|
||||||
|
b->HeroicINT += CalcItemBonus(item->HeroicInt);
|
||||||
|
b->HeroicWIS += CalcItemBonus(item->HeroicWis);
|
||||||
|
b->HeroicCHA += CalcItemBonus(item->HeroicCha);
|
||||||
|
|
||||||
b->STRCapMod += item->HeroicStr;
|
b->STRCapMod += item->HeroicStr;
|
||||||
b->STACapMod += item->HeroicSta;
|
b->STACapMod += item->HeroicSta;
|
||||||
@@ -315,251 +326,58 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
|
|||||||
b->WISCapMod += item->HeroicWis;
|
b->WISCapMod += item->HeroicWis;
|
||||||
b->CHACapMod += item->HeroicCha;
|
b->CHACapMod += item->HeroicCha;
|
||||||
|
|
||||||
|
b->MR += CalcItemBonus(item->MR + item->HeroicMR);
|
||||||
|
b->FR += CalcItemBonus(item->FR + item->HeroicFR);
|
||||||
|
b->CR += CalcItemBonus(item->CR + item->HeroicCR);
|
||||||
|
b->PR += CalcItemBonus(item->PR + item->HeroicPR);
|
||||||
|
b->DR += CalcItemBonus(item->DR + item->HeroicDR);
|
||||||
|
b->Corrup += CalcItemBonus(item->SVCorruption + item->HeroicSVCorrup);
|
||||||
|
|
||||||
|
b->HeroicMR += CalcItemBonus(item->HeroicMR);
|
||||||
|
b->HeroicFR += CalcItemBonus(item->HeroicFR);
|
||||||
|
b->HeroicCR += CalcItemBonus(item->HeroicCR);
|
||||||
|
b->HeroicPR += CalcItemBonus(item->HeroicPR);
|
||||||
|
b->HeroicDR += CalcItemBonus(item->HeroicDR);
|
||||||
|
b->HeroicCorrup += CalcItemBonus(item->HeroicSVCorrup);
|
||||||
|
|
||||||
b->MRCapMod += item->HeroicMR;
|
b->MRCapMod += item->HeroicMR;
|
||||||
b->CRCapMod += item->HeroicFR;
|
b->FRCapMod += item->HeroicFR;
|
||||||
b->FRCapMod += item->HeroicCR;
|
b->CRCapMod += item->HeroicCR;
|
||||||
b->PRCapMod += item->HeroicPR;
|
b->PRCapMod += item->HeroicPR;
|
||||||
b->DRCapMod += item->HeroicDR;
|
b->DRCapMod += item->HeroicDR;
|
||||||
b->CorrupCapMod += item->HeroicSVCorrup;
|
b->CorrupCapMod += item->HeroicSVCorrup;
|
||||||
|
|
||||||
b->HeroicSTR += item->HeroicStr;
|
b->HPRegen += CalcItemBonus(item->Regen);
|
||||||
b->HeroicSTA += item->HeroicSta;
|
b->ManaRegen += CalcItemBonus(item->ManaRegen);
|
||||||
b->HeroicDEX += item->HeroicDex;
|
b->EnduranceRegen += CalcItemBonus(item->EnduranceRegen);
|
||||||
b->HeroicAGI += item->HeroicAgi;
|
|
||||||
b->HeroicINT += item->HeroicInt;
|
|
||||||
b->HeroicWIS += item->HeroicWis;
|
|
||||||
b->HeroicCHA += item->HeroicCha;
|
|
||||||
|
|
||||||
b->HeroicMR += item->HeroicMR;
|
// These have rule-configured caps.
|
||||||
b->HeroicFR += item->HeroicFR;
|
b->ATK = CalcCappedItemBonus(b->ATK, item->Attack, RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap);
|
||||||
b->HeroicCR += item->HeroicCR;
|
b->DamageShield = CalcCappedItemBonus(b->DamageShield, item->DamageShield, RuleI(Character, ItemDamageShieldCap));
|
||||||
b->HeroicPR += item->HeroicPR;
|
b->SpellShield = CalcCappedItemBonus(b->SpellShield, item->SpellShield, RuleI(Character, ItemSpellShieldingCap));
|
||||||
b->HeroicDR += item->HeroicDR;
|
b->MeleeMitigation = CalcCappedItemBonus(b->MeleeMitigation, item->Shielding, RuleI(Character, ItemShieldingCap));
|
||||||
b->HeroicCorrup += item->HeroicSVCorrup;
|
b->StunResist = CalcCappedItemBonus(b->StunResist, item->StunResist, RuleI(Character, ItemStunResistCap));
|
||||||
} else {
|
b->StrikeThrough = CalcCappedItemBonus(b->StrikeThrough, item->StrikeThrough, RuleI(Character, ItemStrikethroughCap));
|
||||||
b->HP += CalcRecommendedLevelBonus(current_level, recommended_level, item->HP);
|
b->AvoidMeleeChance = CalcCappedItemBonus(b->AvoidMeleeChance, item->Avoidance, RuleI(Character, ItemAvoidanceCap));
|
||||||
b->Mana += CalcRecommendedLevelBonus(current_level, recommended_level, item->Mana);
|
b->HitChance = CalcCappedItemBonus(b->HitChance, item->Accuracy, RuleI(Character, ItemAccuracyCap));
|
||||||
b->Endurance += CalcRecommendedLevelBonus(current_level, recommended_level, item->Endur);
|
b->ProcChance = CalcCappedItemBonus(b->ProcChance, item->CombatEffects, RuleI(Character, ItemCombatEffectsCap));
|
||||||
|
b->DoTShielding = CalcCappedItemBonus(b->DoTShielding, item->DotShielding, RuleI(Character, ItemDoTShieldingCap));
|
||||||
b->AC += CalcRecommendedLevelBonus(current_level, recommended_level, item->AC);
|
b->HealAmt = CalcCappedItemBonus(b->HealAmt, item->HealAmt, RuleI(Character, ItemHealAmtCap));
|
||||||
b->ATK += CalcRecommendedLevelBonus(current_level, recommended_level, item->Attack);
|
b->SpellDmg = CalcCappedItemBonus(b->SpellDmg, item->SpellDmg, RuleI(Character, ItemSpellDmgCap));
|
||||||
|
b->Clairvoyance = CalcCappedItemBonus(b->Clairvoyance, item->Clairvoyance, RuleI(Character, ItemClairvoyanceCap));
|
||||||
b->STR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AStr + item->HeroicStr));
|
b->DSMitigation = CalcCappedItemBonus(b->DSMitigation, item->DSMitigation, RuleI(Character, ItemDSMitigationCap));
|
||||||
b->STA += CalcRecommendedLevelBonus(current_level, recommended_level, (item->ASta + item->HeroicSta));
|
|
||||||
b->DEX += CalcRecommendedLevelBonus(current_level, recommended_level, (item->ADex + item->HeroicDex));
|
|
||||||
b->AGI += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AAgi + item->HeroicAgi));
|
|
||||||
b->INT += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AInt + item->HeroicInt));
|
|
||||||
b->WIS += CalcRecommendedLevelBonus(current_level, recommended_level, (item->AWis + item->HeroicWis));
|
|
||||||
b->CHA += CalcRecommendedLevelBonus(current_level, recommended_level, (item->ACha + item->HeroicCha));
|
|
||||||
|
|
||||||
b->MR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->MR + item->HeroicMR));
|
|
||||||
b->FR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->FR + item->HeroicFR));
|
|
||||||
b->CR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->CR + item->HeroicCR));
|
|
||||||
b->PR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->PR + item->HeroicPR));
|
|
||||||
b->DR += CalcRecommendedLevelBonus(current_level, recommended_level, (item->DR + item->HeroicDR));
|
|
||||||
b->Corrup += CalcRecommendedLevelBonus(current_level, recommended_level, (item->SVCorruption + item->HeroicSVCorrup));
|
|
||||||
|
|
||||||
b->STRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicStr);
|
|
||||||
b->STACapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicSta);
|
|
||||||
b->DEXCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicDex);
|
|
||||||
b->AGICapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicAgi);
|
|
||||||
b->INTCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicInt);
|
|
||||||
b->WISCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicWis);
|
|
||||||
b->CHACapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicCha);
|
|
||||||
|
|
||||||
b->MRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicMR);
|
|
||||||
b->CRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicFR);
|
|
||||||
b->FRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicCR);
|
|
||||||
b->PRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicPR);
|
|
||||||
b->DRCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicDR);
|
|
||||||
b->CorrupCapMod += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicSVCorrup);
|
|
||||||
|
|
||||||
b->HeroicSTR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicStr);
|
|
||||||
b->HeroicSTA += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicSta);
|
|
||||||
b->HeroicDEX += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicDex);
|
|
||||||
b->HeroicAGI += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicAgi);
|
|
||||||
b->HeroicINT += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicInt);
|
|
||||||
b->HeroicWIS += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicWis);
|
|
||||||
b->HeroicCHA += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicCha);
|
|
||||||
|
|
||||||
b->HeroicMR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicMR);
|
|
||||||
b->HeroicFR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicFR);
|
|
||||||
b->HeroicCR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicCR);
|
|
||||||
b->HeroicPR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicPR);
|
|
||||||
b->HeroicDR += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicDR);
|
|
||||||
b->HeroicCorrup += CalcRecommendedLevelBonus(current_level, recommended_level, item->HeroicSVCorrup);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b->haste < item->Haste) {
|
if (b->haste < item->Haste) {
|
||||||
b->haste = item->Haste;
|
b->haste = item->Haste;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item->Regen != 0) {
|
if (item->ExtraDmgAmt != 0 && item->ExtraDmgSkill <= EQ::skills::HIGHEST_SKILL) {
|
||||||
b->HPRegen += item->Regen;
|
if (item->ExtraDmgSkill == ALL_SKILLS) {
|
||||||
|
for (const auto &skill_id: EQ::skills::GetExtraDamageSkills()) {
|
||||||
|
b->SkillDamageAmount[skill_id] = CalcCappedItemBonus(b->SkillDamageAmount[skill_id], item->ExtraDmgAmt, RuleI(Character, ItemExtraDmgCap));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item->ManaRegen != 0) {
|
|
||||||
b->ManaRegen += item->ManaRegen;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->EnduranceRegen != 0) {
|
|
||||||
b->EnduranceRegen += item->EnduranceRegen;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->Attack != 0) {
|
|
||||||
unsigned int cap = RuleI(Character, ItemATKCap);
|
|
||||||
cap += itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap;
|
|
||||||
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->ATK + item->Attack) > cap
|
|
||||||
) {
|
|
||||||
b->ATK = RuleI(Character, ItemATKCap);
|
|
||||||
} else {
|
} else {
|
||||||
b->ATK += item->Attack;
|
b->SkillDamageAmount[item->ExtraDmgSkill] = CalcCappedItemBonus(b->SkillDamageAmount[item->ExtraDmgSkill], item->ExtraDmgAmt, RuleI(Character, ItemExtraDmgCap));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->DamageShield != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->DamageShield + item->DamageShield) > RuleI(Character, ItemDamageShieldCap)
|
|
||||||
) {
|
|
||||||
b->DamageShield = RuleI(Character, ItemDamageShieldCap);
|
|
||||||
} else {
|
|
||||||
b->DamageShield += item->DamageShield;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->SpellShield != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->SpellShield + item->SpellShield) > RuleI(Character, ItemSpellShieldingCap)
|
|
||||||
) {
|
|
||||||
b->SpellShield = RuleI(Character, ItemSpellShieldingCap);
|
|
||||||
} else {
|
|
||||||
b->SpellShield += item->SpellShield;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->Shielding != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->MeleeMitigation + item->Shielding) > RuleI(Character, ItemShieldingCap)
|
|
||||||
) {
|
|
||||||
b->MeleeMitigation = RuleI(Character, ItemShieldingCap);
|
|
||||||
} else {
|
|
||||||
b->MeleeMitigation += item->Shielding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->StunResist != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->StunResist + item->StunResist) > RuleI(Character, ItemStunResistCap)
|
|
||||||
) {
|
|
||||||
b->StunResist = RuleI(Character, ItemStunResistCap);
|
|
||||||
} else {
|
|
||||||
b->StunResist += item->StunResist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->StrikeThrough != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->StrikeThrough + item->StrikeThrough) > RuleI(Character, ItemStrikethroughCap)
|
|
||||||
) {
|
|
||||||
b->StrikeThrough = RuleI(Character, ItemStrikethroughCap);
|
|
||||||
} else {
|
|
||||||
b->StrikeThrough += item->StrikeThrough;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->Avoidance != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->AvoidMeleeChance + item->Avoidance) > RuleI(Character, ItemAvoidanceCap)
|
|
||||||
) {
|
|
||||||
b->AvoidMeleeChance = RuleI(Character, ItemAvoidanceCap);
|
|
||||||
} else {
|
|
||||||
b->AvoidMeleeChance += item->Avoidance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->Accuracy != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->HitChance + item->Accuracy) > RuleI(Character, ItemAccuracyCap)
|
|
||||||
) {
|
|
||||||
b->HitChance = RuleI(Character, ItemAccuracyCap);
|
|
||||||
} else {
|
|
||||||
b->HitChance += item->Accuracy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->CombatEffects != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->ProcChance + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap)
|
|
||||||
) {
|
|
||||||
b->ProcChance = RuleI(Character, ItemCombatEffectsCap);
|
|
||||||
} else {
|
|
||||||
b->ProcChance += item->CombatEffects;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->DotShielding != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->DoTShielding + item->DotShielding) > RuleI(Character, ItemDoTShieldingCap)
|
|
||||||
) {
|
|
||||||
b->DoTShielding = RuleI(Character, ItemDoTShieldingCap);
|
|
||||||
} else {
|
|
||||||
b->DoTShielding += item->DotShielding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->HealAmt != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->HealAmt + item->HealAmt) > RuleI(Character, ItemHealAmtCap)
|
|
||||||
) {
|
|
||||||
b->HealAmt = RuleI(Character, ItemHealAmtCap);
|
|
||||||
} else {
|
|
||||||
b->HealAmt += item->HealAmt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->SpellDmg != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->SpellDmg + item->SpellDmg) > RuleI(Character, ItemSpellDmgCap)
|
|
||||||
) {
|
|
||||||
b->SpellDmg = RuleI(Character, ItemSpellDmgCap);
|
|
||||||
} else {
|
|
||||||
b->SpellDmg += item->SpellDmg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->Clairvoyance != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->Clairvoyance + item->Clairvoyance) > RuleI(Character, ItemClairvoyanceCap)
|
|
||||||
) {
|
|
||||||
b->Clairvoyance = RuleI(Character, ItemClairvoyanceCap);
|
|
||||||
} else {
|
|
||||||
b->Clairvoyance += item->Clairvoyance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item->DSMitigation != 0) {
|
|
||||||
if (
|
|
||||||
IsOfClientBotMerc() &&
|
|
||||||
(b->DSMitigation + item->DSMitigation) > RuleI(Character, ItemDSMitigationCap)
|
|
||||||
) {
|
|
||||||
b->DSMitigation = RuleI(Character, ItemDSMitigationCap);
|
|
||||||
} else {
|
|
||||||
b->DSMitigation += item->DSMitigation;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,32 +496,6 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item->ExtraDmgAmt != 0 && item->ExtraDmgSkill <= EQ::skills::HIGHEST_SKILL) {
|
|
||||||
if (item->ExtraDmgSkill == ALL_SKILLS) {
|
|
||||||
for (const auto& skill_id : EQ::skills::GetExtraDamageSkills()) {
|
|
||||||
if (
|
|
||||||
!IsNPC() &&
|
|
||||||
RuleI(Character, ItemExtraDmgCap) >= 0 &&
|
|
||||||
(b->SkillDamageAmount[skill_id] + item->ExtraDmgAmt) > RuleI(Character, ItemExtraDmgCap)
|
|
||||||
) {
|
|
||||||
b->SkillDamageAmount[skill_id] = RuleI(Character, ItemExtraDmgCap);
|
|
||||||
} else {
|
|
||||||
b->SkillDamageAmount[skill_id] += item->ExtraDmgAmt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (
|
|
||||||
!IsNPC() &&
|
|
||||||
RuleI(Character, ItemExtraDmgCap) >= 0 &&
|
|
||||||
(b->SkillDamageAmount[item->ExtraDmgSkill] + item->ExtraDmgAmt) > RuleI(Character, ItemExtraDmgCap)
|
|
||||||
) {
|
|
||||||
b->SkillDamageAmount[item->ExtraDmgSkill] = RuleI(Character, ItemExtraDmgCap);
|
|
||||||
} else {
|
|
||||||
b->SkillDamageAmount[item->ExtraDmgSkill] += item->ExtraDmgAmt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_augment) {
|
if (!is_augment) {
|
||||||
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
||||||
const auto* augment = inst->GetAugment(i);
|
const auto* augment = inst->GetAugment(i);
|
||||||
@@ -714,10 +506,8 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
|
|||||||
AddItemBonuses(augment, b, true, false, recommended_level);
|
AddItemBonuses(augment, b, true, false, recommended_level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Mob::AdditiveWornBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_augment) {
|
void Mob::AdditiveWornBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_augment) {
|
||||||
/*
|
/*
|
||||||
Powerful Non-live like option allows developers to add worn effects on items that
|
Powerful Non-live like option allows developers to add worn effects on items that
|
||||||
|
|||||||
+389
-219
@@ -2256,7 +2256,7 @@ void Bot::AI_Process()
|
|||||||
SetAttackingFlag(false);
|
SetAttackingFlag(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
float tar_distance = DistanceSquared(m_Position, tar->GetPosition());
|
float tar_distance = DistanceSquaredNoZ(m_Position, tar->GetPosition());
|
||||||
|
|
||||||
// TARGET VALIDATION
|
// TARGET VALIDATION
|
||||||
if (!IsValidTarget(bot_owner, leash_owner, lo_distance, leash_distance, tar, tar_distance)) {
|
if (!IsValidTarget(bot_owner, leash_owner, lo_distance, leash_distance, tar, tar_distance)) {
|
||||||
@@ -2287,7 +2287,7 @@ void Bot::AI_Process()
|
|||||||
// COMBAT RANGE CALCS
|
// COMBAT RANGE CALCS
|
||||||
bool front_mob = InFrontMob(tar, GetX(), GetY());
|
bool front_mob = InFrontMob(tar, GetX(), GetY());
|
||||||
bool behind_mob = BehindMob(tar, GetX(), GetY());
|
bool behind_mob = BehindMob(tar, GetX(), GetY());
|
||||||
uint8 stop_melee_level = GetStopMeleeLevel();
|
bool stop_melee_level = GetLevel() >= GetStopMeleeLevel();
|
||||||
tar_distance = sqrt(tar_distance); // sqrt this for future calculations
|
tar_distance = sqrt(tar_distance); // sqrt this for future calculations
|
||||||
// Item variables
|
// Item variables
|
||||||
const EQ::ItemInstance* p_item = GetBotItem(EQ::invslot::slotPrimary);
|
const EQ::ItemInstance* p_item = GetBotItem(EQ::invslot::slotPrimary);
|
||||||
@@ -2296,7 +2296,6 @@ void Bot::AI_Process()
|
|||||||
CombatRangeInput input = {
|
CombatRangeInput input = {
|
||||||
.target = tar,
|
.target = tar,
|
||||||
.target_distance = tar_distance,
|
.target_distance = tar_distance,
|
||||||
.behind_mob = behind_mob,
|
|
||||||
.stop_melee_level = stop_melee_level,
|
.stop_melee_level = stop_melee_level,
|
||||||
.p_item = p_item,
|
.p_item = p_item,
|
||||||
.s_item = s_item
|
.s_item = s_item
|
||||||
@@ -2315,7 +2314,7 @@ void Bot::AI_Process()
|
|||||||
if (PULLING_BOT || RETURNING_BOT) {
|
if (PULLING_BOT || RETURNING_BOT) {
|
||||||
if (!TargetValidation(tar)) { return; }
|
if (!TargetValidation(tar)) { return; }
|
||||||
|
|
||||||
if (!DoLosChecks(tar)) {
|
if (RuleB(Bots, BotsRequireLoS) && !HasLoS()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2368,6 +2367,16 @@ void Bot::AI_Process()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
HasTargetReflection() &&
|
||||||
|
!IsTaunting() &&
|
||||||
|
!tar->IsFleeing() &&
|
||||||
|
!tar->IsFeared() &&
|
||||||
|
TryEvade(tar)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// ENGAGED AT COMBAT RANGE
|
// ENGAGED AT COMBAT RANGE
|
||||||
|
|
||||||
// We can fight
|
// We can fight
|
||||||
@@ -2433,8 +2442,12 @@ void Bot::AI_Process()
|
|||||||
|
|
||||||
ranged_timer.Start();
|
ranged_timer.Start();
|
||||||
}
|
}
|
||||||
else if (!IsBotRanged() && GetLevel() < stop_melee_level) {
|
else if (!IsBotRanged() && !stop_melee_level) {
|
||||||
if (!GetMaxMeleeRange() || !RuleB(Bots, DisableSpecialAbilitiesAtMaxMelee)) {
|
if (
|
||||||
|
IsTaunting() ||
|
||||||
|
!GetMaxMeleeRange() ||
|
||||||
|
!RuleB(Bots, DisableSpecialAbilitiesAtMaxMelee)
|
||||||
|
) {
|
||||||
DoClassAttacks(tar);
|
DoClassAttacks(tar);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3065,7 +3078,7 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) {
|
|||||||
|
|
||||||
// For races with a fixed size
|
// For races with a fixed size
|
||||||
if (GetRace() == Race::LavaDragon || GetRace() == Race::Wurm || GetRace() == Race::GhostDragon) {
|
if (GetRace() == Race::LavaDragon || GetRace() == Race::Wurm || GetRace() == Race::GhostDragon) {
|
||||||
// size_mod = 60.0f;
|
size_mod = 60.0f;
|
||||||
}
|
}
|
||||||
else if (size_mod < 6.0f) {
|
else if (size_mod < 6.0f) {
|
||||||
size_mod = 8.0f;
|
size_mod = 8.0f;
|
||||||
@@ -3110,92 +3123,39 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) {
|
|||||||
size_mod = (size_mod / 7.0f);
|
size_mod = (size_mod / 7.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
o.melee_distance_max = size_mod;
|
o.melee_distance_max = sqrt(size_mod);
|
||||||
|
|
||||||
if (!RuleB(Bots, UseFlatNormalMeleeRange)) {
|
|
||||||
|
|
||||||
bool is_two_hander = input.p_item && input.p_item->GetItem()->IsType2HWeapon();
|
bool is_two_hander = input.p_item && input.p_item->GetItem()->IsType2HWeapon();
|
||||||
bool is_shield = input.s_item && input.s_item->GetItem()->IsTypeShield();
|
bool is_shield = input.s_item && input.s_item->GetItem()->IsTypeShield();
|
||||||
bool is_backstab_weapon = input.p_item && input.p_item->GetItemBackstabDamage();
|
bool is_backstab_weapon = input.p_item && input.p_item->GetItemBackstabDamage();
|
||||||
|
|
||||||
switch (GetClass()) {
|
if (IsTaunting()) { // Taunting bots
|
||||||
case Class::Warrior:
|
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, LowerTauntingMeleeDistanceMultiplier);
|
||||||
case Class::Paladin:
|
o.melee_distance = o.melee_distance_max * RuleR(Bots, UpperTauntingMeleeDistanceMultiplier);
|
||||||
case Class::ShadowKnight:
|
|
||||||
o.melee_distance = (
|
|
||||||
is_two_hander ? o.melee_distance_max * 0.45f
|
|
||||||
: is_shield ? o.melee_distance_max * 0.35f
|
|
||||||
: o.melee_distance_max * 0.40f
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Class::Necromancer:
|
|
||||||
case Class::Wizard:
|
|
||||||
case Class::Magician:
|
|
||||||
case Class::Enchanter:
|
|
||||||
o.melee_distance = (
|
|
||||||
is_two_hander ? o.melee_distance_max * 0.95f
|
|
||||||
: o.melee_distance_max * 0.75f
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Class::Rogue:
|
|
||||||
o.melee_distance = (
|
|
||||||
input.behind_mob && is_backstab_weapon
|
|
||||||
? o.melee_distance_max * 0.35f
|
|
||||||
: o.melee_distance_max * 0.50f
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
o.melee_distance = (
|
|
||||||
is_two_hander ? o.melee_distance_max * 0.70f
|
|
||||||
: o.melee_distance_max * 0.50f
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
else if (IsBotRanged()) { // Archers/Throwers
|
||||||
o.melee_distance = sqrt(o.melee_distance);
|
|
||||||
o.melee_distance_max = sqrt(o.melee_distance_max);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
o.melee_distance_max = sqrt(o.melee_distance_max);
|
|
||||||
o.melee_distance = o.melee_distance_max * RuleR(Bots, NormalMeleeRangeDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (o.melee_distance > RuleR(Bots, MaxDistanceForMelee)) {
|
|
||||||
o.melee_distance = RuleR(Bots, MaxDistanceForMelee);
|
|
||||||
}
|
|
||||||
|
|
||||||
o.melee_distance_min = o.melee_distance * RuleR(Bots, PercentMinMeleeDistance);
|
|
||||||
|
|
||||||
if (IsTaunting()) {
|
|
||||||
o.melee_distance_min = o.melee_distance * RuleR(Bots, PercentTauntMinMeleeDistance);
|
|
||||||
o.melee_distance = o.melee_distance * RuleR(Bots, TauntNormalMeleeRangeDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_stop_melee_level = GetLevel() >= input.stop_melee_level;
|
|
||||||
|
|
||||||
if (!IsTaunting() && !IsBotRanged() && !is_stop_melee_level && GetMaxMeleeRange()) {
|
|
||||||
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, PercentMinMaxMeleeRangeDistance);
|
|
||||||
o.melee_distance = o.melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_stop_melee_level && !IsBotRanged()) {
|
|
||||||
float desired_range = GetBotDistanceRanged();
|
|
||||||
o.melee_distance_min = std::max(o.melee_distance, (desired_range / 2));
|
|
||||||
o.melee_distance = std::max((o.melee_distance + 1), desired_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsBotRanged()) {
|
|
||||||
float min_distance = RuleI(Combat, MinRangedAttackDist);
|
float min_distance = RuleI(Combat, MinRangedAttackDist);
|
||||||
float max_distance = GetBotRangedValue();
|
float max_distance = GetBotRangedValue();
|
||||||
float desired_range = GetBotDistanceRanged();
|
float desired_range = GetBotDistanceRanged();
|
||||||
max_distance = (max_distance == 0 ? desired_range : max_distance); // stay ranged if set to ranged even if items/ammo aren't correct
|
|
||||||
|
max_distance = (max_distance == 0 ? desired_range : max_distance); // stay ranged even if items/ammo aren't correct
|
||||||
o.melee_distance_min = std::max(min_distance, (desired_range / 2));
|
o.melee_distance_min = std::max(min_distance, (desired_range / 2));
|
||||||
o.melee_distance = std::min(max_distance, desired_range);
|
o.melee_distance = std::min(max_distance, desired_range);
|
||||||
}
|
}
|
||||||
|
else if (input.stop_melee_level) { // Casters
|
||||||
|
float desired_range = GetBotDistanceRanged();
|
||||||
|
|
||||||
|
o.melee_distance_min = std::max(o.melee_distance_max, (desired_range / 2));
|
||||||
|
o.melee_distance = std::max((o.melee_distance_max * 1.25f), desired_range);
|
||||||
|
}
|
||||||
|
else if (GetMaxMeleeRange()) { // Melee bots set to max melee range
|
||||||
|
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, LowerMaxMeleeRangeDistanceMultiplier);
|
||||||
|
o.melee_distance = o.melee_distance_max * RuleR(Bots, UpperMaxMeleeRangeDistanceMultiplier);
|
||||||
|
}
|
||||||
|
else { // Regular melee
|
||||||
|
o.melee_distance_min = o.melee_distance_max * RuleR(Bots, LowerMeleeDistanceMultiplier);
|
||||||
|
o.melee_distance = o.melee_distance_max * RuleR(Bots, UpperMeleeDistanceMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
o.at_combat_range = (input.target_distance <= o.melee_distance);
|
o.at_combat_range = (input.target_distance <= o.melee_distance);
|
||||||
|
|
||||||
@@ -3215,14 +3175,17 @@ bool Bot::IsValidTarget(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetHasLoS(DoLosChecks(tar));
|
||||||
|
|
||||||
bool invalid_target_state = false;
|
bool invalid_target_state = false;
|
||||||
|
|
||||||
if (HOLDING ||
|
if (HOLDING ||
|
||||||
!tar->IsNPC() ||
|
!tar->IsNPC() ||
|
||||||
(tar->IsMezzed() && !HasBotAttackFlag(tar)) ||
|
(tar->IsMezzed() && !HasBotAttackFlag(tar)) ||
|
||||||
(!Charmed() && tar->GetUltimateOwner()->IsOfClientBotMerc()) ||
|
(!Charmed() && tar->GetUltimateOwner()->IsOfClientBotMerc()) ||
|
||||||
lo_distance > leash_distance ||
|
lo_distance > leash_distance ||
|
||||||
tar_distance > leash_distance ||
|
tar_distance > leash_distance ||
|
||||||
(!GetAttackingFlag() && !CheckLosCheat(tar) && !leash_owner->CheckLosCheat(tar)) ||
|
(!GetAttackingFlag() && !HasLoS()) ||
|
||||||
!IsAttackAllowed(tar)
|
!IsAttackAllowed(tar)
|
||||||
) {
|
) {
|
||||||
invalid_target_state = true;
|
invalid_target_state = true;
|
||||||
@@ -3615,9 +3578,18 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) {
|
|||||||
void Bot::Depop() {
|
void Bot::Depop() {
|
||||||
WipeHateList();
|
WipeHateList();
|
||||||
entity_list.RemoveFromHateLists(this);
|
entity_list.RemoveFromHateLists(this);
|
||||||
|
RemoveAllAuras();
|
||||||
|
|
||||||
if (HasPet())
|
Mob* bot_pet = GetPet();
|
||||||
GetPet()->Depop();
|
|
||||||
|
if (bot_pet) {
|
||||||
|
if (bot_pet->Charmed()) {
|
||||||
|
bot_pet->BuffFadeByEffect(SE_Charm);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bot_pet->Depop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_botOwner = nullptr;
|
_botOwner = nullptr;
|
||||||
_botOwnerCharacterID = 0;
|
_botOwnerCharacterID = 0;
|
||||||
@@ -3728,7 +3700,7 @@ bool Bot::Spawn(Client* botCharacterOwner) {
|
|||||||
SetVerifiedRaid(true);
|
SetVerifiedRaid(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (auto group = entity_list.GetGroupByMob(this)) {
|
else if (auto group = entity_list.GetGroupByMobName(GetName())) {
|
||||||
// Safety Check to confirm we have a valid group
|
// Safety Check to confirm we have a valid group
|
||||||
auto owner = GetBotOwner();
|
auto owner = GetBotOwner();
|
||||||
if (owner && !group->IsGroupMember(owner->GetCleanName())) {
|
if (owner && !group->IsGroupMember(owner->GetCleanName())) {
|
||||||
@@ -3740,13 +3712,6 @@ bool Bot::Spawn(Client* botCharacterOwner) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MapSpellTypeLevels();
|
|
||||||
|
|
||||||
if (RuleB(Bots, RunSpellTypeChecksOnSpawn)) {
|
|
||||||
OwnerMessage("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline.");
|
|
||||||
CheckBotSpells(); //This runs through a serious of checks and outputs any spells that are set to the wrong spell type in the database
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsBotRanged()) {
|
if (IsBotRanged()) {
|
||||||
ChangeBotRangedWeapons(true);
|
ChangeBotRangedWeapons(true);
|
||||||
}
|
}
|
||||||
@@ -9571,7 +9536,14 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!BotHasEnoughMana(spell_id)) {
|
if (
|
||||||
|
!BotHasEnoughMana(spell_id) &&
|
||||||
|
(
|
||||||
|
!RuleB(Bots, FinishBuffing) ||
|
||||||
|
IsEngaged() ||
|
||||||
|
IsBotBuffSpellType(spell_type)
|
||||||
|
)
|
||||||
|
) {
|
||||||
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spell_id));
|
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spell_id));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -9766,7 +9738,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
&&
|
&&
|
||||||
tar->CanBuffStack(spell_id, GetLevel(), true) < 0
|
tar->CanBuffStack(spell_id, GetLevel(), false) < 0
|
||||||
) {
|
) {
|
||||||
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName());
|
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName());
|
||||||
return false;
|
return false;
|
||||||
@@ -9830,6 +9802,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8 bot_class = GetClass();
|
uint8 bot_class = GetClass();
|
||||||
|
auto spell = spells[spell_id];
|
||||||
|
|
||||||
switch (spell_type) {
|
switch (spell_type) {
|
||||||
case BotSpellTypes::Buff:
|
case BotSpellTypes::Buff:
|
||||||
@@ -9853,7 +9826,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) {
|
|||||||
case BotSpellTypes::SendHome:
|
case BotSpellTypes::SendHome:
|
||||||
if (
|
if (
|
||||||
tar == this &&
|
tar == this &&
|
||||||
spells[spell_id].target_type == ST_TargetsTarget
|
spell.target_type == ST_TargetsTarget
|
||||||
) {
|
) {
|
||||||
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id));
|
LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id));
|
||||||
return false;
|
return false;
|
||||||
@@ -9883,7 +9856,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
spells[spell_id].target_type == ST_Pet &&
|
(spell.target_type == ST_Pet || spell.target_type == ST_SummonedPet) &&
|
||||||
(
|
(
|
||||||
!tar->IsPet() ||
|
!tar->IsPet() ||
|
||||||
(
|
(
|
||||||
@@ -10626,6 +10599,8 @@ BotSpell Bot::GetSpellByHealType(uint16 spell_type, Mob* tar) {
|
|||||||
return GetBestBotSpellForHealOverTime(this, tar, spell_type);
|
return GetBestBotSpellForHealOverTime(this, tar, spell_type);
|
||||||
case BotSpellTypes::GroupHoTHeals:
|
case BotSpellTypes::GroupHoTHeals:
|
||||||
return GetBestBotSpellForGroupHealOverTime(this, tar, spell_type);
|
return GetBestBotSpellForGroupHealOverTime(this, tar, spell_type);
|
||||||
|
default:
|
||||||
|
return BotSpell(); // Return an empty BotSpell if no valid spell type is found
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10677,7 +10652,7 @@ int Bot::GetDefaultSetting(uint16 setting_category, uint16 setting_type, uint8 s
|
|||||||
case BotSettingCategories::SpellTypeAnnounceCast:
|
case BotSettingCategories::SpellTypeAnnounceCast:
|
||||||
return GetDefaultSpellTypeAnnounceCast(setting_type, stance);
|
return GetDefaultSpellTypeAnnounceCast(setting_type, stance);
|
||||||
default:
|
default:
|
||||||
break;
|
return 0; // Default return value for unrecognized categories
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -10716,7 +10691,7 @@ int Bot::GetSetting(uint16 setting_category, uint16 setting_type) {
|
|||||||
case BotSettingCategories::SpellTypeAnnounceCast:
|
case BotSettingCategories::SpellTypeAnnounceCast:
|
||||||
return GetSpellTypeAnnounceCast(setting_type);
|
return GetSpellTypeAnnounceCast(setting_type);
|
||||||
default:
|
default:
|
||||||
break;
|
return 0; // Default return value for unrecognized categories
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11443,7 +11418,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DoLosChecks(tar)) {
|
if (!HasLoS() && !DoLosChecks(tar)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11606,18 +11581,265 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsBotSpellTypeDetrimental(spell_type) && !IsDetrimentalSpell(spell_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsBotSpellTypeBeneficial(spell_type) && !IsBeneficialSpell(spell_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto spell = spells[spell_id];
|
||||||
|
std::string teleport_zone = spell.teleport_zone;
|
||||||
|
|
||||||
switch (spell_type) {
|
switch (spell_type) {
|
||||||
|
case BotSpellTypes::Nuke:
|
||||||
|
if (IsAnyNukeOrStunSpell(spell_id) && !IsStunSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::RegularHeal:
|
||||||
|
case BotSpellTypes::PetRegularHeals:
|
||||||
|
if (
|
||||||
|
IsAnyHealSpell(spell_id) &&
|
||||||
|
!IsVeryFastHealSpell(spell_id) &&
|
||||||
|
!IsFastHealSpell(spell_id) &&
|
||||||
|
!IsCompleteHealSpell(spell_id) &&
|
||||||
|
!IsHealOverTimeSpell(spell_id) &&
|
||||||
|
!IsBuffSpell(spell_id)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Root:
|
||||||
|
case BotSpellTypes::AERoot:
|
||||||
|
if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_Root)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
case BotSpellTypes::Buff:
|
case BotSpellTypes::Buff:
|
||||||
|
case BotSpellTypes::PreCombatBuff:
|
||||||
case BotSpellTypes::PetBuffs:
|
case BotSpellTypes::PetBuffs:
|
||||||
if (
|
if (
|
||||||
IsResistanceOnlySpell(spell_id) ||
|
IsBuffSpell(spell_id) &&
|
||||||
IsDamageShieldOnlySpell(spell_id) ||
|
IsBeneficialSpell(spell_id) &&
|
||||||
IsDamageShieldAndResistSpell(spell_id)
|
!IsBardSong(spell_id) &&
|
||||||
|
!IsResistanceOnlySpell(spell_id) &&
|
||||||
|
!IsDamageShieldOnlySpell(spell_id) &&
|
||||||
|
!IsDamageShieldAndResistSpell(spell_id)
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
spell_type != BotSpellTypes::PetBuffs &&
|
||||||
|
(spell.target_type == ST_Pet || spell.target_type == ST_SummonedPet)
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Escape:
|
||||||
|
if (IsEscapeSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Pet:
|
||||||
|
if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Lifetap:
|
||||||
|
case BotSpellTypes::AELifetap:
|
||||||
|
if (IsDetrimentalSpell(spell_id) && IsLifetapSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Snare:
|
||||||
|
case BotSpellTypes::AESnare:
|
||||||
|
if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::DOT:
|
||||||
|
case BotSpellTypes::AEDoT:
|
||||||
|
if (
|
||||||
|
IsDetrimentalSpell(spell_id) &&
|
||||||
|
(IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id))
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Dispel:
|
||||||
|
case BotSpellTypes::AEDispel:
|
||||||
|
if (IsDispelSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::InCombatBuff:
|
||||||
|
if (
|
||||||
|
!IsBardSong(spell_id) &&
|
||||||
|
(
|
||||||
|
(IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) ||
|
||||||
|
(spell_type == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id))
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Mez:
|
||||||
|
case BotSpellTypes::AEMez:
|
||||||
|
if (IsMesmerizeSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Charm:
|
||||||
|
if (IsCharmSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Slow:
|
||||||
|
case BotSpellTypes::AESlow:
|
||||||
|
if (IsDetrimentalSpell(spell_id) && IsSlowSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Debuff:
|
||||||
|
case BotSpellTypes::AEDebuff:
|
||||||
|
if (
|
||||||
|
IsDebuffSpell(spell_id) &&
|
||||||
|
!IsHateReduxSpell(spell_id) &&
|
||||||
|
!IsHateSpell(spell_id)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Cure:
|
||||||
|
case BotSpellTypes::GroupCures:
|
||||||
|
case BotSpellTypes::PetCures:
|
||||||
|
if (IsCureSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Resurrect:
|
||||||
|
if (IsEffectInSpell(spell_id, SE_Revive)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::HateRedux:
|
||||||
|
if (IsHateReduxSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::InCombatBuffSong:
|
||||||
|
case BotSpellTypes::OutOfCombatBuffSong:
|
||||||
|
case BotSpellTypes::PreCombatBuffSong:
|
||||||
|
if (
|
||||||
|
IsBuffSpell(spell_id) &&
|
||||||
|
IsBeneficialSpell(spell_id) &&
|
||||||
|
IsBardSong(spell_id)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Fear:
|
||||||
|
case BotSpellTypes::AEFear:
|
||||||
|
if (IsFearSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Stun:
|
||||||
|
case BotSpellTypes::AEStun:
|
||||||
|
if (IsDetrimentalSpell(spell_id) && IsStunSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::HateLine:
|
||||||
|
case BotSpellTypes::AEHateLine:
|
||||||
|
if (IsDetrimentalSpell(spell_id) && IsHateSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::CompleteHeal:
|
||||||
|
case BotSpellTypes::GroupCompleteHeals:
|
||||||
|
case BotSpellTypes::PetCompleteHeals:
|
||||||
|
if (IsCompleteHealSpell(spell_id) || IsGroupCompleteHealSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::FastHeals:
|
||||||
|
case BotSpellTypes::PetFastHeals:
|
||||||
|
if (IsFastHealSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::VeryFastHeals:
|
||||||
|
case BotSpellTypes::PetVeryFastHeals:
|
||||||
|
if (IsVeryFastHealSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::GroupHeals:
|
||||||
|
if (IsRegularGroupHealSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::HoTHeals:
|
||||||
|
case BotSpellTypes::GroupHoTHeals:
|
||||||
|
case BotSpellTypes::PetHoTHeals:
|
||||||
|
if (IsHealOverTimeSpell(spell_id) || IsGroupHealOverTimeSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::AENukes:
|
||||||
|
if (
|
||||||
|
IsDetrimentalSpell(spell_id) &&
|
||||||
|
!IsAERainSpell(spell_id) &&
|
||||||
|
!IsPBAENukeSpell(spell_id) &&
|
||||||
|
!IsStunSpell(spell_id)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::AERains:
|
||||||
|
if (IsAERainNukeSpell(spell_id) && !IsStunSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::PBAENuke:
|
||||||
|
if (IsPBAENukeSpell(spell_id) && !IsStunSpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
case BotSpellTypes::ResistBuffs:
|
case BotSpellTypes::ResistBuffs:
|
||||||
case BotSpellTypes::PetResistBuffs:
|
case BotSpellTypes::PetResistBuffs:
|
||||||
if (IsResistanceBuffSpell(spell_id)) {
|
if (IsResistanceBuffSpell(spell_id)) {
|
||||||
@@ -11627,44 +11849,7 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) {
|
|||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::DamageShields:
|
case BotSpellTypes::DamageShields:
|
||||||
case BotSpellTypes::PetDamageShields:
|
case BotSpellTypes::PetDamageShields:
|
||||||
if (IsEffectInSpell(spell_id, SE_DamageShield)) {
|
if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_DamageShield)) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
case BotSpellTypes::PBAENuke:
|
|
||||||
if (
|
|
||||||
IsPBAENukeSpell(spell_id) &&
|
|
||||||
!IsStunSpell(spell_id)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
case BotSpellTypes::AERains:
|
|
||||||
if (
|
|
||||||
IsAERainNukeSpell(spell_id) &&
|
|
||||||
!IsStunSpell(spell_id)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case BotSpellTypes::AEStun:
|
|
||||||
case BotSpellTypes::Stun:
|
|
||||||
if (IsStunSpell(spell_id)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
case BotSpellTypes::AENukes:
|
|
||||||
case BotSpellTypes::Nuke:
|
|
||||||
if (!IsStunSpell(spell_id)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
case BotSpellTypes::Lull:
|
|
||||||
if (IsHarmonySpell(spell_id)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11672,13 +11857,18 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) {
|
|||||||
case BotSpellTypes::Teleport:
|
case BotSpellTypes::Teleport:
|
||||||
if (
|
if (
|
||||||
IsBeneficialSpell(spell_id) &&
|
IsBeneficialSpell(spell_id) &&
|
||||||
(
|
(IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))
|
||||||
IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate)
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
case BotSpellTypes::Lull:
|
||||||
|
case BotSpellTypes::AELull:
|
||||||
|
if (IsHarmonySpell(spell_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::Succor:
|
case BotSpellTypes::Succor:
|
||||||
if (
|
if (
|
||||||
@@ -11702,25 +11892,21 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::Levitate:
|
case BotSpellTypes::Levitate:
|
||||||
if (
|
if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Levitate)) {
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_Levitate)
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::Rune:
|
case BotSpellTypes::Rune:
|
||||||
if (
|
if (IsBeneficialSpell(spell_id) &&
|
||||||
IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) ||
|
(IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune))
|
||||||
IsEffectInSpell(spell_id, SE_Rune)
|
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::WaterBreathing:
|
case BotSpellTypes::WaterBreathing:
|
||||||
if (IsEffectInSpell(spell_id, SE_WaterBreathing)) {
|
if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11728,29 +11914,22 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) {
|
|||||||
case BotSpellTypes::Size:
|
case BotSpellTypes::Size:
|
||||||
if (
|
if (
|
||||||
IsBeneficialSpell(spell_id) &&
|
IsBeneficialSpell(spell_id) &&
|
||||||
(
|
(IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))
|
||||||
IsEffectInSpell(spell_id, SE_ModelSize) ||
|
|
||||||
IsEffectInSpell(spell_id, SE_ChangeHeight)
|
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::Invisibility:
|
case BotSpellTypes::Invisibility:
|
||||||
if (
|
if (IsBeneficialSpell(spell_id) &&
|
||||||
IsEffectInSpell(spell_id, SE_SeeInvis) ||
|
(IsEffectInSpell(spell_id, SE_SeeInvis) ||IsInvisibleSpell(spell_id))
|
||||||
IsInvisibleSpell(spell_id)
|
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
case BotSpellTypes::MovementSpeed:
|
case BotSpellTypes::MovementSpeed:
|
||||||
if (
|
if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) {
|
||||||
IsBeneficialSpell(spell_id) &&
|
|
||||||
IsEffectInSpell(spell_id, SE_MovementSpeed)
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11758,7 +11937,13 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) {
|
|||||||
case BotSpellTypes::SendHome:
|
case BotSpellTypes::SendHome:
|
||||||
if (
|
if (
|
||||||
IsBeneficialSpell(spell_id) &&
|
IsBeneficialSpell(spell_id) &&
|
||||||
IsEffectInSpell(spell_id, SE_GateToHomeCity)
|
(
|
||||||
|
IsEffectInSpell(spell_id, SE_GateToHomeCity) ||
|
||||||
|
(
|
||||||
|
teleport_zone.compare("") &&
|
||||||
|
(IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))
|
||||||
|
)
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -11846,23 +12031,26 @@ void Bot::DoCombatPositioning(
|
|||||||
bool behind_mob,
|
bool behind_mob,
|
||||||
bool front_mob
|
bool front_mob
|
||||||
) {
|
) {
|
||||||
if (HasTargetReflection()) {
|
if (!tar->IsFeared()) {
|
||||||
if (!IsTaunting() && !tar->IsFeared() && !tar->IsStunned()) {
|
bool is_too_close = tar_distance < melee_distance_min;
|
||||||
if (TryEvade(tar)) {
|
bool los_adjust = !HasRequiredLoSForPositioning(tar);
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (tar->IsRooted() && !IsTaunting()) { // Move non-taunters out of range - Above already checks if bot is targeted, otherwise they would stay
|
|
||||||
if (tar_distance <= melee_distance_max) {
|
|
||||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 2), GetBehindMob(), false)) {
|
|
||||||
RunToGoalWithJitter(Goal);
|
|
||||||
|
|
||||||
|
if (tar->IsRooted() && !IsTaunting()) { // Move non-taunting melee out of range
|
||||||
|
bool rooted_adjust = tar_distance <= melee_distance_max && HasTargetReflection();
|
||||||
|
|
||||||
|
if (rooted_adjust) {
|
||||||
|
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 1.25f), GetBehindMob(), !GetBehindMob())) {
|
||||||
|
RunToGoalWithJitter(Goal);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (tar_distance < melee_distance_min || (!front_mob && IsTaunting())) { // Back up any bots that are too close
|
else {
|
||||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), (IsTaunting() || !GetBehindMob()))) {
|
if (IsTaunting()) { // Taunting adjustments
|
||||||
|
bool taunting_adjust = (!front_mob || is_too_close || los_adjust);
|
||||||
|
|
||||||
|
if (taunting_adjust) {
|
||||||
|
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, true)) {
|
||||||
RunToGoalWithJitter(Goal);
|
RunToGoalWithJitter(Goal);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -11870,41 +12058,10 @@ void Bot::DoCombatPositioning(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!tar->IsFeared()) {
|
if (tar->IsEnraged() && !stop_melee_level && !IsBotRanged()) { // Move non-taunting melee bots behind target during enrage
|
||||||
if (IsTaunting()) { // Taunting adjustments
|
bool enraged_adjust = !behind_mob || is_too_close || los_adjust;
|
||||||
Mob* mob_tar = tar->GetTarget();
|
|
||||||
|
|
||||||
if (!mob_tar) {
|
if (enraged_adjust) {
|
||||||
DoFaceCheckNoJitter(tar);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RuleB(Bots, TauntingBotsFollowTopHate)) { // If enabled, taunting bots will stick to top hate
|
|
||||||
if (Distance(m_Position, mob_tar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate)) {
|
|
||||||
Goal = mob_tar->GetPosition();
|
|
||||||
RunToGoalWithJitter(Goal);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // Otherwise, stick to any other bots that are taunting
|
|
||||||
if (mob_tar->IsBot() && mob_tar->CastToBot()->IsTaunting() && (Distance(m_Position, mob_tar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate))) {
|
|
||||||
Goal = mob_tar->GetPosition();
|
|
||||||
RunToGoalWithJitter(Goal);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (tar_distance < melee_distance_min || (GetBehindMob() && !behind_mob) || (IsTaunting() && !front_mob) || !HasRequiredLoSForPositioning(tar)) { // Regular adjustment
|
|
||||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), (IsTaunting() || !GetBehindMob()))) {
|
|
||||||
RunToGoalWithJitter(Goal);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (tar->IsEnraged() && !IsTaunting() && !stop_melee_level && !behind_mob) { // Move non-taunting melee bots behind target during enrage
|
|
||||||
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) {
|
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) {
|
||||||
RunToGoalWithJitter(Goal);
|
RunToGoalWithJitter(Goal);
|
||||||
|
|
||||||
@@ -11912,11 +12069,26 @@ void Bot::DoCombatPositioning(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else { // Regular adjustments
|
||||||
|
bool regular_adjust =
|
||||||
|
is_too_close ||
|
||||||
|
los_adjust ||
|
||||||
|
(!GetBehindMob() && !front_mob) ||
|
||||||
|
(GetBehindMob() && !behind_mob);
|
||||||
|
|
||||||
|
if (regular_adjust) {
|
||||||
|
if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), !GetBehindMob())) {
|
||||||
|
RunToGoalWithJitter(Goal);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DoFaceCheckNoJitter(tar);
|
DoFaceCheckNoJitter(tar);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only, bool front_only, bool bypass_los) {
|
bool Bot::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only, bool front_only, bool bypass_los) {
|
||||||
@@ -12013,7 +12185,11 @@ bool Bot::RequiresLoSForPositioning() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
|
for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) {
|
||||||
if (IsBotSpellTypeDetrimental(i) && !GetSpellTypeHold(i)) {
|
if (IsHealBotSpellType(i) || i == BotSpellTypes::PBAENuke) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GetSpellTypeHold(i)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12026,7 +12202,7 @@ bool Bot::HasRequiredLoSForPositioning(Mob* tar) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RequiresLoSForPositioning() && !DoLosChecks(tar)) {
|
if (RequiresLoSForPositioning() && !HasLoS()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12041,10 +12217,6 @@ bool Bot::HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob*
|
|||||||
for (auto& close_mob : caster->m_close_mobs) {
|
for (auto& close_mob : caster->m_close_mobs) {
|
||||||
Mob* m = close_mob.second;
|
Mob* m = close_mob.second;
|
||||||
|
|
||||||
if (tar == m) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (spell_type) {
|
switch (spell_type) {
|
||||||
case BotSpellTypes::AELull:
|
case BotSpellTypes::AELull:
|
||||||
if (m->GetSpecialAbility(SpecialAbility::PacifyImmunity)) {
|
if (m->GetSpecialAbility(SpecialAbility::PacifyImmunity)) {
|
||||||
@@ -12136,8 +12308,6 @@ bool Bot::HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob*
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetHasLoS(true);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-8
@@ -235,8 +235,7 @@ static std::map<uint16, std::string> botSubType_names = {
|
|||||||
struct CombatRangeInput {
|
struct CombatRangeInput {
|
||||||
Mob* target;
|
Mob* target;
|
||||||
float target_distance;
|
float target_distance;
|
||||||
bool behind_mob;
|
bool stop_melee_level;
|
||||||
uint8 stop_melee_level;
|
|
||||||
const EQ::ItemInstance* p_item;
|
const EQ::ItemInstance* p_item;
|
||||||
const EQ::ItemInstance* s_item;
|
const EQ::ItemInstance* s_item;
|
||||||
};
|
};
|
||||||
@@ -684,12 +683,9 @@ public:
|
|||||||
void SetSitManaPct(uint8 value) { _SitManaPct = value; }
|
void SetSitManaPct(uint8 value) { _SitManaPct = value; }
|
||||||
|
|
||||||
// Spell lists
|
// Spell lists
|
||||||
void CheckBotSpells();
|
|
||||||
void MapSpellTypeLevels();
|
|
||||||
const std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>>& GetCommandedSpellTypesMinLevels() { return commanded_spells_min_level; }
|
|
||||||
std::list<BotSpellTypeOrder> GetSpellTypesPrioritized(uint8 priority_type);
|
std::list<BotSpellTypeOrder> GetSpellTypesPrioritized(uint8 priority_type);
|
||||||
static uint16 GetParentSpellType(uint16 spell_type);
|
static uint16 GetParentSpellType(uint16 spell_type);
|
||||||
bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id);
|
static bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id);
|
||||||
inline uint16 GetCastedSpellType() const { return _castedSpellType; }
|
inline uint16 GetCastedSpellType() const { return _castedSpellType; }
|
||||||
void SetCastedSpellType(uint16 spell_type);
|
void SetCastedSpellType(uint16 spell_type);
|
||||||
bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id);
|
bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id);
|
||||||
@@ -1153,8 +1149,6 @@ protected:
|
|||||||
std::vector<BotSpells> AIBot_spells_enforced;
|
std::vector<BotSpells> AIBot_spells_enforced;
|
||||||
std::unordered_map<uint16, std::vector<BotSpells_wIndex>> AIBot_spells_by_type;
|
std::unordered_map<uint16, std::vector<BotSpells_wIndex>> AIBot_spells_by_type;
|
||||||
|
|
||||||
std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>> commanded_spells_min_level;
|
|
||||||
|
|
||||||
std::vector<BotTimer> bot_timers;
|
std::vector<BotTimer> bot_timers;
|
||||||
std::vector<BotBlockedBuffs> bot_blocked_buffs;
|
std::vector<BotBlockedBuffs> bot_blocked_buffs;
|
||||||
|
|
||||||
|
|||||||
+10
-2
@@ -57,6 +57,7 @@
|
|||||||
#include "water_map.h"
|
#include "water_map.h"
|
||||||
#include "worldserver.h"
|
#include "worldserver.h"
|
||||||
#include "mob.h"
|
#include "mob.h"
|
||||||
|
#include "bot_database.h"
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
@@ -219,6 +220,13 @@ int bot_command_init(void)
|
|||||||
std::vector<std::pair<std::string, uint8>> injected_bot_command_settings;
|
std::vector<std::pair<std::string, uint8>> injected_bot_command_settings;
|
||||||
std::vector<std::string> orphaned_bot_command_settings;
|
std::vector<std::string> orphaned_bot_command_settings;
|
||||||
|
|
||||||
|
if (RuleB(Bots, RunSpellTypeChecksOnBoot)) {
|
||||||
|
LogBotSpellTypeChecks("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline.");
|
||||||
|
database.botdb.CheckBotSpells();
|
||||||
|
}
|
||||||
|
|
||||||
|
database.botdb.MapCommandedSpellTypeMinLevels();
|
||||||
|
|
||||||
for (auto bcs_iter : bot_command_settings) {
|
for (auto bcs_iter : bot_command_settings) {
|
||||||
|
|
||||||
auto bcl_iter = bot_command_list.find(bcs_iter.first);
|
auto bcl_iter = bot_command_list.find(bcs_iter.first);
|
||||||
@@ -796,10 +804,10 @@ void helper_send_usage_required_bots(Client *bot_owner, uint16 spell_type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& spell_map = bot->GetCommandedSpellTypesMinLevels();
|
auto spell_map = database.botdb.GetCommandedSpellTypesMinLevels();
|
||||||
|
|
||||||
if (spell_map.empty()) {
|
if (spell_map.empty()) {
|
||||||
bot_owner->Message(Chat::Yellow, "No bots are capable of casting this spell type");
|
bot_owner->Message(Chat::Yellow, "No bots are capable of casting this spell type.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ void bot_command_attack(Client *c, const Seperator *sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->DoLosChecks(target_mob)) {
|
if (RuleB(Bots, BotsRequireLoS) && !c->DoLosChecks(target_mob)) {
|
||||||
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -525,6 +525,9 @@ void bot_command_cast(Client* c, const Seperator* sep)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool requires_los = !(IsAnyHealSpell(spell_id) && !IsPBAESpell(spell_id));
|
||||||
|
bot_iter->SetHasLoS(requires_los ? bot_iter->DoLosChecks(new_tar) : true);
|
||||||
|
|
||||||
if (!bot_iter->AttemptAACastSpell(tar, spell_id, rank)) {
|
if (!bot_iter->AttemptAACastSpell(tar, spell_id, rank)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -543,6 +546,9 @@ void bot_command_cast(Client* c, const Seperator* sep)
|
|||||||
tar = bot_iter;
|
tar = bot_iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool los_required = bot_iter != tar && !IsAnyHealSpell(chosen_spell_id) && !IsPBAESpell(chosen_spell_id);
|
||||||
|
bot_iter->SetHasLoS(los_required ? bot_iter->DoLosChecks(new_tar) : true);
|
||||||
|
|
||||||
if (bot_iter->AttemptForcedCastSpell(tar, chosen_spell_id)) {
|
if (bot_iter->AttemptForcedCastSpell(tar, chosen_spell_id)) {
|
||||||
if (!first_found) {
|
if (!first_found) {
|
||||||
first_found = bot_iter;
|
first_found = bot_iter;
|
||||||
@@ -556,6 +562,7 @@ void bot_command_cast(Client* c, const Seperator* sep)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bot_iter->SetCommandedSpell(true);
|
bot_iter->SetCommandedSpell(true);
|
||||||
|
bot_iter->SetHasLoS(BotSpellTypeRequiresLoS(spell_type) ? bot_iter->DoLosChecks(new_tar) : true);
|
||||||
|
|
||||||
if (bot_iter->AICastSpell(new_tar, 100, spell_type, sub_target_type, sub_type)) {
|
if (bot_iter->AICastSpell(new_tar, 100, spell_type, sub_target_type, sub_type)) {
|
||||||
if (!first_found) {
|
if (!first_found) {
|
||||||
|
|||||||
@@ -197,7 +197,11 @@ void bot_command_depart(Client* c, const Seperator* sep)
|
|||||||
|
|
||||||
bot_iter->SetCommandedSpell(true);
|
bot_iter->SetCommandedSpell(true);
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(itr->SpellId, bot_iter->HasLoS())) {
|
if (!IsValidSpell(itr->SpellId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(BotSpellTypes::Teleport, itr->SpellId) && !bot_iter->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ void bot_command_precombat(Client* c, const Seperator* sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->DoLosChecks(c->GetTarget())) {
|
if (RuleB(Bots, BotsRequireLoS) && !c->DoLosChecks(c->GetTarget())) {
|
||||||
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ void bot_command_pull(Client *c, const Seperator *sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!c->DoLosChecks(target_mob)) {
|
if (RuleB(Bots, BotsRequireLoS) && !c->DoLosChecks(target_mob)) {
|
||||||
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
c->Message(Chat::Red, "You must have Line of Sight to use this command.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include "../common/repositories/bot_pet_buffs_repository.h"
|
#include "../common/repositories/bot_pet_buffs_repository.h"
|
||||||
#include "../common/repositories/bot_pet_inventories_repository.h"
|
#include "../common/repositories/bot_pet_inventories_repository.h"
|
||||||
#include "../common/repositories/bot_spell_casting_chances_repository.h"
|
#include "../common/repositories/bot_spell_casting_chances_repository.h"
|
||||||
|
#include "../common/repositories/bot_spells_entries_repository.h"
|
||||||
#include "../common/repositories/bot_settings_repository.h"
|
#include "../common/repositories/bot_settings_repository.h"
|
||||||
#include "../common/repositories/bot_stances_repository.h"
|
#include "../common/repositories/bot_stances_repository.h"
|
||||||
#include "../common/repositories/bot_timers_repository.h"
|
#include "../common/repositories/bot_timers_repository.h"
|
||||||
@@ -2521,3 +2522,208 @@ bool BotDatabase::DeleteBotBlockedBuffs(const uint32 bot_id)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BotDatabase::CheckBotSpells() {
|
||||||
|
auto spell_list = BotSpellsEntriesRepository::All(content_db);
|
||||||
|
uint16 spell_id;
|
||||||
|
SPDat_Spell_Struct spell;
|
||||||
|
|
||||||
|
for (const auto& s : spell_list) {
|
||||||
|
if (!IsValidSpell(s.spell_id)) {
|
||||||
|
LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
spell = spells[s.spell_id];
|
||||||
|
spell_id = spell.id;
|
||||||
|
|
||||||
|
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] >= 255) {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.minlevel) {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the min level is currently set to {}.",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id,
|
||||||
|
s.minlevel
|
||||||
|
);
|
||||||
|
|
||||||
|
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]",
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
spell_id,
|
||||||
|
s.npc_spells_id,
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
s.minlevel,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] < s.minlevel) {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] could be used starting at level {} for a {} [#{}] instead of the current min level of {}.",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id,
|
||||||
|
s.minlevel
|
||||||
|
);
|
||||||
|
|
||||||
|
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]",
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
spell_id,
|
||||||
|
s.npc_spells_id,
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
s.minlevel,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.maxlevel) {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the max level is currently set to {}.",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)],
|
||||||
|
GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX),
|
||||||
|
s.npc_spells_id,
|
||||||
|
s.maxlevel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 correct_type = GetCorrectBotSpellType(s.type, spell_id);
|
||||||
|
|
||||||
|
if (RuleB(Bots, UseParentSpellTypeForChecks)) {
|
||||||
|
uint16 parent_type = Bot::GetParentSpellType(correct_type);
|
||||||
|
|
||||||
|
if (s.type == parent_type || s.type == correct_type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (correct_type != parent_type) {
|
||||||
|
correct_type = parent_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (IsPetBotSpellType(s.type)) {
|
||||||
|
correct_type = GetPetBotSpellType(correct_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsPetBotSpellType(correct_type) && (spell.target_type != ST_Pet && spell.target_type != ST_SummonedPet)) {
|
||||||
|
correct_type = Bot::GetParentSpellType(correct_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (correct_type == s.type) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (correct_type == UINT16_MAX) {
|
||||||
|
LogBotSpellTypeChecks(
|
||||||
|
"{} [#{}] is incorrect. It is currently set as {} [#{}] but the correct type is unknown.",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
Bot::GetSpellTypeNameByID(s.type),
|
||||||
|
s.type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]",
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
Bot::GetSpellTypeNameByID(s.type),
|
||||||
|
s.type,
|
||||||
|
Bot::GetSpellTypeNameByID(correct_type),
|
||||||
|
correct_type
|
||||||
|
);
|
||||||
|
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spell_id` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]",
|
||||||
|
correct_type,
|
||||||
|
spell_id,
|
||||||
|
GetSpellName(spell_id),
|
||||||
|
spell_id,
|
||||||
|
Bot::GetSpellTypeNameByID(s.type),
|
||||||
|
s.type,
|
||||||
|
Bot::GetSpellTypeNameByID(correct_type),
|
||||||
|
correct_type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BotDatabase::MapCommandedSpellTypeMinLevels() {
|
||||||
|
commanded_spell_type_min_levels.clear();
|
||||||
|
|
||||||
|
auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START });
|
||||||
|
auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END });
|
||||||
|
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
if (!Bot::IsValidBotSpellType(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int x = Class::Warrior; x <= Class::Berserker; ++x) {
|
||||||
|
commanded_spell_type_min_levels[i][x] = {UINT8_MAX, "" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto spell_list = BotSpellsEntriesRepository::All(content_db);
|
||||||
|
|
||||||
|
for (const auto& s : spell_list) {
|
||||||
|
if (!IsValidSpell(s.spell_id)) {
|
||||||
|
LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto spell = spells[s.spell_id];
|
||||||
|
|
||||||
|
if (spell.target_type == ST_Self) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t bot_class = s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!EQ::ValueWithin(bot_class, Class::Warrior, Class::Berserker) ||
|
||||||
|
!Bot::IsValidBotSpellType(s.type)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = start; i <= end; ++i) {
|
||||||
|
if (s.minlevel > commanded_spell_type_min_levels[i][bot_class].min_level) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
i > BotSpellTypes::PARENT_TYPE_END &&
|
||||||
|
i != s.type &&
|
||||||
|
Bot::GetParentSpellType(i) != s.type
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Bot::IsValidSpellTypeBySpellID(i, s.spell_id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.minlevel < commanded_spell_type_min_levels[i][bot_class].min_level) {
|
||||||
|
commanded_spell_type_min_levels[i][bot_class].min_level = s.minlevel;
|
||||||
|
commanded_spell_type_min_levels[i][bot_class].description = StringFormat(
|
||||||
|
"%s [#%u] - Level %u",
|
||||||
|
GetClassIDName(bot_class),
|
||||||
|
bot_class,
|
||||||
|
s.minlevel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+9
-1
@@ -24,7 +24,7 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "bot_structs.h"
|
||||||
|
|
||||||
class Bot;
|
class Bot;
|
||||||
class Client;
|
class Client;
|
||||||
@@ -130,6 +130,10 @@ public:
|
|||||||
bool SaveBotSettings(Mob* m);
|
bool SaveBotSettings(Mob* m);
|
||||||
bool DeleteBotSettings(const uint32 bot_id);
|
bool DeleteBotSettings(const uint32 bot_id);
|
||||||
|
|
||||||
|
void CheckBotSpells();
|
||||||
|
void MapCommandedSpellTypeMinLevels();
|
||||||
|
std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>> GetCommandedSpellTypesMinLevels() { return commanded_spell_type_min_levels; }
|
||||||
|
|
||||||
/* Bot group functions */
|
/* Bot group functions */
|
||||||
bool LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 group_id, std::list<uint32>& group_list);
|
bool LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 group_id, std::list<uint32>& group_list);
|
||||||
|
|
||||||
@@ -211,6 +215,10 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::string query;
|
std::string query;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::map<int32_t, std::map<int32_t, BotSpellTypesByClass>> commanded_spell_type_min_levels;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+87
-247
@@ -41,7 +41,10 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chance < 100 && zone->random.Int(0, 100) > chance) {
|
if (
|
||||||
|
!IsCommandedSpell() &&
|
||||||
|
zone->random.Int(0, 100) > chance
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,10 +64,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ
|
|||||||
bot_spell.SpellIndex = 0;
|
bot_spell.SpellIndex = 0;
|
||||||
bot_spell.ManaCost = 0;
|
bot_spell.ManaCost = 0;
|
||||||
|
|
||||||
if (BotSpellTypeRequiresLoS(spell_type) && tar != this) {
|
if (!BotSpellTypeRequiresLoS(spell_type) || tar == this) {
|
||||||
SetHasLoS(DoLosChecks(tar));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SetHasLoS(true);
|
SetHasLoS(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,8 +218,11 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ
|
|||||||
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, (IsAEBotSpellType(spell_type) || sub_target_type == CommandedSubTypes::AETarget), sub_target_type, sub_type);
|
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, (IsAEBotSpellType(spell_type) || sub_target_type == CommandedSubTypes::AETarget), sub_target_type, sub_type);
|
||||||
|
|
||||||
for (const auto& s : bot_spell_list) {
|
for (const auto& s : bot_spell_list) {
|
||||||
|
if (!IsValidSpell(s.SpellId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) {
|
if (BotRequiresLoSToCast(spell_type, s.SpellId) && !HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +276,11 @@ bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
|
|||||||
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, IsAEBotSpellType(spell_type));
|
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, IsAEBotSpellType(spell_type));
|
||||||
|
|
||||||
for (const auto& s : bot_spell_list) {
|
for (const auto& s : bot_spell_list) {
|
||||||
if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) {
|
if (!IsValidSpell(s.SpellId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, s.SpellId) && !HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +288,7 @@ bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
|
|||||||
Mob* add_mob = GetFirstIncomingMobToMez(this, s.SpellId, spell_type, IsAEBotSpellType(spell_type));
|
Mob* add_mob = GetFirstIncomingMobToMez(this, s.SpellId, spell_type, IsAEBotSpellType(spell_type));
|
||||||
|
|
||||||
if (!add_mob) {
|
if (!add_mob) {
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
tar = add_mob;
|
tar = add_mob;
|
||||||
@@ -327,7 +334,11 @@ bool Bot::BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe
|
|||||||
|
|
||||||
bot_spell = GetBestBotSpellForCure(this, tar, spell_type);
|
bot_spell = GetBestBotSpellForCure(this, tar, spell_type);
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell.SpellId) && !HasLoS()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,7 +408,11 @@ bool Bot::BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
|
|||||||
bot_spell = GetFirstBotSpellBySpellType(this, spell_type);
|
bot_spell = GetFirstBotSpellBySpellType(this, spell_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell.SpellId) && !HasLoS()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,6 +433,10 @@ bool Bot::BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type) {
|
bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type) {
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell.SpellId) && !HasLoS()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (spell_type == BotSpellTypes::Stun || spell_type == BotSpellTypes::AEStun) {
|
if (spell_type == BotSpellTypes::Stun || spell_type == BotSpellTypes::AEStun) {
|
||||||
uint8 stun_chance = (tar->IsCasting() ? RuleI(Bots, StunCastChanceIfCasting) : RuleI(Bots, StunCastChanceNormal));
|
uint8 stun_chance = (tar->IsCasting() ? RuleI(Bots, StunCastChanceIfCasting) : RuleI(Bots, StunCastChanceNormal));
|
||||||
|
|
||||||
@@ -435,24 +454,24 @@ bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe
|
|||||||
bot_spell = GetBestBotSpellForStunByTargetType(this, ST_TargetOptional, spell_type, IsAEBotSpellType(spell_type), tar);
|
bot_spell = GetBestBotSpellForStunByTargetType(this, ST_TargetOptional, spell_type, IsAEBotSpellType(spell_type), tar);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||||
bot_spell = GetBestBotSpellForNukeByBodyType(this, tar->GetBodyType(), spell_type, IsAEBotSpellType(spell_type), tar);
|
bot_spell = GetBestBotSpellForNukeByBodyType(this, tar->GetBodyType(), spell_type, IsAEBotSpellType(spell_type), tar);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS()) && spell_type == BotSpellTypes::Nuke && bot_class == Class::Wizard) {
|
if (spell_type == BotSpellTypes::Nuke && bot_class == Class::Wizard && !IsValidSpell(bot_spell.SpellId)) {
|
||||||
bot_spell = GetBestBotWizardNukeSpellByTargetResists(this, tar, spell_type);
|
bot_spell = GetBestBotWizardNukeSpellByTargetResists(this, tar, spell_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) {
|
if (!IsValidSpell(bot_spell.SpellId)) {
|
||||||
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, IsAEBotSpellType(spell_type));
|
std::vector<BotSpell_wPriority> bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, IsAEBotSpellType(spell_type));
|
||||||
|
|
||||||
for (const auto& s : bot_spell_list) {
|
for (const auto& s : bot_spell_list) {
|
||||||
if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) {
|
if (!IsValidSpell(s.SpellId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -562,7 +581,9 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
|
|||||||
// Allow bots to cast buff spells even if they are out of mana
|
// Allow bots to cast buff spells even if they are out of mana
|
||||||
if (
|
if (
|
||||||
RuleB(Bots, FinishBuffing) &&
|
RuleB(Bots, FinishBuffing) &&
|
||||||
manaCost > hasMana && AIBot_spells[i].type == BotSpellTypes::Buff
|
manaCost > hasMana &&
|
||||||
|
!IsEngaged() &&
|
||||||
|
IsBotBuffSpellType(AIBot_spells[i].type)
|
||||||
) {
|
) {
|
||||||
SetMana(manaCost);
|
SetMana(manaCost);
|
||||||
}
|
}
|
||||||
@@ -908,7 +929,11 @@ std::list<BotSpell> Bot::GetBotSpellsForSpellEffect(Bot* caster, uint16 spell_ty
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -946,7 +971,11 @@ std::list<BotSpell> Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* caster, ui
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -987,7 +1016,11 @@ std::list<BotSpell> Bot::GetBotSpellsBySpellType(Bot* caster, uint16 spell_type)
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1016,7 +1049,11 @@ std::vector<BotSpell_wPriority> Bot::GetPrioritizedBotSpellsBySpellType(Bot* cas
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1103,7 +1140,11 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* caster, uint16 spell_type) {
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1135,7 +1176,6 @@ BotSpell Bot::GetBestBotSpellForVeryFastHeal(Bot* caster, Mob* tar, uint16 spell
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
||||||
|
|
||||||
for (auto bot_spell_list_itr : bot_spell_list) {
|
for (auto bot_spell_list_itr : bot_spell_list) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (
|
if (
|
||||||
IsVeryFastHealSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
IsVeryFastHealSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
||||||
result.SpellId = bot_spell_list_itr.SpellId;
|
result.SpellId = bot_spell_list_itr.SpellId;
|
||||||
@@ -1161,7 +1201,6 @@ BotSpell Bot::GetBestBotSpellForFastHeal(Bot* caster, Mob* tar, uint16 spell_typ
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
||||||
|
|
||||||
for (auto bot_spell_list_itr : bot_spell_list) {
|
for (auto bot_spell_list_itr : bot_spell_list) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsFastHealSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
if (IsFastHealSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
||||||
result.SpellId = bot_spell_list_itr.SpellId;
|
result.SpellId = bot_spell_list_itr.SpellId;
|
||||||
result.SpellIndex = bot_spell_list_itr.SpellIndex;
|
result.SpellIndex = bot_spell_list_itr.SpellIndex;
|
||||||
@@ -1186,7 +1225,6 @@ BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* caster, Mob* tar, uint16 spell
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_HealOverTime);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_HealOverTime);
|
||||||
|
|
||||||
for (auto bot_spell_list_itr : bot_spell_list) {
|
for (auto bot_spell_list_itr : bot_spell_list) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsHealOverTimeSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
if (IsHealOverTimeSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) {
|
||||||
result.SpellId = bot_spell_list_itr.SpellId;
|
result.SpellId = bot_spell_list_itr.SpellId;
|
||||||
result.SpellIndex = bot_spell_list_itr.SpellIndex;
|
result.SpellIndex = bot_spell_list_itr.SpellIndex;
|
||||||
@@ -1243,7 +1281,6 @@ BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* caster, Mob* tar, u
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsRegularSingleTargetHealSpell(bot_spell_list_itr->SpellId) && caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type)) {
|
if (IsRegularSingleTargetHealSpell(bot_spell_list_itr->SpellId) && caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type)) {
|
||||||
result.SpellId = bot_spell_list_itr->SpellId;
|
result.SpellId = bot_spell_list_itr->SpellId;
|
||||||
result.SpellIndex = bot_spell_list_itr->SpellIndex;
|
result.SpellIndex = bot_spell_list_itr->SpellIndex;
|
||||||
@@ -1268,7 +1305,6 @@ BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* caster, Mob* tar, uint16
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsRegularSingleTargetHealSpell(bot_spell_list_itr->SpellId) && caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type)) {
|
if (IsRegularSingleTargetHealSpell(bot_spell_list_itr->SpellId) && caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type)) {
|
||||||
result.SpellId = bot_spell_list_itr->SpellId;
|
result.SpellId = bot_spell_list_itr->SpellId;
|
||||||
result.SpellIndex = bot_spell_list_itr->SpellIndex;
|
result.SpellIndex = bot_spell_list_itr->SpellIndex;
|
||||||
@@ -1298,7 +1334,6 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* caster, Mob* tar, uint16 spell_ty
|
|||||||
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsRegularGroupHealSpell(bot_spell_list_itr->SpellId)) {
|
if (IsRegularGroupHealSpell(bot_spell_list_itr->SpellId)) {
|
||||||
uint16 spell_id = bot_spell_list_itr->SpellId;
|
uint16 spell_id = bot_spell_list_itr->SpellId;
|
||||||
|
|
||||||
@@ -1337,7 +1372,6 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* caster, Mob* tar, uint16
|
|||||||
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) {
|
if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) {
|
||||||
uint16 spell_id = bot_spell_list_itr->SpellId;
|
uint16 spell_id = bot_spell_list_itr->SpellId;
|
||||||
|
|
||||||
@@ -1376,7 +1410,6 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* caster, Mob* tar, uint16
|
|||||||
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
int required_count = caster->GetSpellTypeAEOrGroupTargetCount(spell_type);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsGroupCompleteHealSpell(bot_spell_list_itr->SpellId)) {
|
if (IsGroupCompleteHealSpell(bot_spell_list_itr->SpellId)) {
|
||||||
uint16 spell_id = bot_spell_list_itr->SpellId;
|
uint16 spell_id = bot_spell_list_itr->SpellId;
|
||||||
|
|
||||||
@@ -1410,7 +1443,6 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* caster, uint16 spell_type) {
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Mez);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Mez);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (
|
if (
|
||||||
IsMesmerizeSpell(bot_spell_list_itr->SpellId) &&
|
IsMesmerizeSpell(bot_spell_list_itr->SpellId) &&
|
||||||
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)
|
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)
|
||||||
@@ -1433,11 +1465,10 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
|
|||||||
if (caster && caster->GetOwner()) {
|
if (caster && caster->GetOwner()) {
|
||||||
int spell_range = caster->GetActSpellRange(spell_id, spells[spell_id].range);
|
int spell_range = caster->GetActSpellRange(spell_id, spells[spell_id].range);
|
||||||
int spell_ae_range = caster->GetAOERange(spell_id);
|
int spell_ae_range = caster->GetAOERange(spell_id);
|
||||||
int buff_count = 0;
|
bool is_pbae_spell = IsPBAESpell(spell_id);
|
||||||
NPC* npc = nullptr;
|
NPC* npc = nullptr;
|
||||||
|
|
||||||
for (auto& close_mob : caster->m_close_mobs) {
|
for (auto& close_mob : caster->m_close_mobs) {
|
||||||
buff_count = 0;
|
|
||||||
npc = close_mob.second->CastToNPC();
|
npc = close_mob.second->CastToNPC();
|
||||||
|
|
||||||
if (!npc) {
|
if (!npc) {
|
||||||
@@ -1448,30 +1479,30 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_pbae_spell) {
|
||||||
|
if (spell_ae_range < Distance(caster->GetPosition(), npc->GetPosition())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (spell_range < Distance(caster->GetPosition(), npc->GetPosition())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (AE) {
|
if (AE) {
|
||||||
int target_count = 0;
|
int target_count = 0;
|
||||||
|
|
||||||
for (auto& close_mob : caster->m_close_mobs) {
|
for (auto& close_mob : caster->m_close_mobs) {
|
||||||
Mob* m = close_mob.second;
|
Mob* m = close_mob.second;
|
||||||
|
|
||||||
if (npc == m) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!caster->IsValidMezTarget(caster->GetOwner(), m, spell_id)) {
|
if (!caster->IsValidMezTarget(caster->GetOwner(), m, spell_id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsPBAESpell(spell_id)) {
|
if (spell_ae_range < Distance(npc->GetPosition(), m->GetPosition())) {
|
||||||
if (spell_ae_range < Distance(caster->GetPosition(), m->GetPosition())) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (spell_range < Distance(m->GetPosition(), npc->GetPosition())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (caster->CastChecks(spell_id, m, spell_type, true, true)) {
|
if (caster->CastChecks(spell_id, m, spell_type, true, true)) {
|
||||||
++target_count;
|
++target_count;
|
||||||
@@ -1486,11 +1517,6 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zone->random.Int(1, 100) < RuleI(Bots, AEMezChance)) {
|
|
||||||
caster->SetSpellTypeRecastTimer(spell_type, RuleI(Bots, MezFailDelay));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = npc;
|
result = npc;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1502,18 +1528,10 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_typ
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zone->random.Int(1, 100) < RuleI(Bots, MezChance)) {
|
|
||||||
caster->SetSpellTypeRecastTimer(spell_type, RuleI(Bots, MezAEFailDelay));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = npc;
|
result = npc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
caster->SetHasLoS(true);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1534,7 +1552,6 @@ BotSpell Bot::GetBestBotMagicianPetSpell(Bot* caster, uint16 spell_type) {
|
|||||||
std::string pet_type = GetBotMagicianPetType(caster);
|
std::string pet_type = GetBotMagicianPetType(caster);
|
||||||
|
|
||||||
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (
|
if (
|
||||||
IsSummonPetSpell(bot_spell_list_itr->SpellId) &&
|
IsSummonPetSpell(bot_spell_list_itr->SpellId) &&
|
||||||
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId) &&
|
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId) &&
|
||||||
@@ -1716,7 +1733,6 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* caster, SpellTargetType ta
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffectAndTargetType(caster, spell_type, SE_CurrentHP, target_type);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffectAndTargetType(caster, spell_type, SE_CurrentHP, target_type);
|
||||||
|
|
||||||
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsPureNukeSpell(bot_spell_list_itr->SpellId) || IsDamageSpell(bot_spell_list_itr->SpellId)) {
|
if (IsPureNukeSpell(bot_spell_list_itr->SpellId) || IsDamageSpell(bot_spell_list_itr->SpellId)) {
|
||||||
if (!AE && IsAnyAESpell(bot_spell_list_itr->SpellId) && !IsGroupSpell(bot_spell_list_itr->SpellId)) {
|
if (!AE && IsAnyAESpell(bot_spell_list_itr->SpellId) && !IsGroupSpell(bot_spell_list_itr->SpellId)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1729,7 +1745,6 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* caster, SpellTargetType ta
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
caster->IsCommandedSpell() ||
|
caster->IsCommandedSpell() ||
|
||||||
!AE ||
|
!AE ||
|
||||||
@@ -1766,7 +1781,6 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* caster, SpellTargetType ta
|
|||||||
|
|
||||||
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr)
|
for(std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr)
|
||||||
{
|
{
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (IsStunSpell(bot_spell_list_itr->SpellId)) {
|
if (IsStunSpell(bot_spell_list_itr->SpellId)) {
|
||||||
if (!AE && IsAnyAESpell(bot_spell_list_itr->SpellId) && !IsGroupSpell(bot_spell_list_itr->SpellId)) {
|
if (!AE && IsAnyAESpell(bot_spell_list_itr->SpellId) && !IsGroupSpell(bot_spell_list_itr->SpellId)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -1830,7 +1844,6 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* caster, Mob* target,
|
|||||||
bool spell_selected = false;
|
bool spell_selected = false;
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (!caster->IsValidSpellRange(bot_spell_list_itr->SpellId, target)) {
|
if (!caster->IsValidSpellRange(bot_spell_list_itr->SpellId, target)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1887,8 +1900,6 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* caster, Mob* target,
|
|||||||
|
|
||||||
if (!spell_selected) {
|
if (!spell_selected) {
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
|
|
||||||
if (caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)) {
|
if (caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)) {
|
||||||
if (caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type)) {
|
if (caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type)) {
|
||||||
spell_selected = true;
|
spell_selected = true;
|
||||||
@@ -1926,7 +1937,11 @@ BotSpell Bot::GetDebuffBotSpell(Bot* caster, Mob *tar, uint16 spell_type) {
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1972,7 +1987,11 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* caster, Mob *tar, uint16 spell
|
|||||||
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
const std::vector<BotSpells_wIndex>& bot_spell_list = caster->BotGetSpellsByType(spell_type);
|
||||||
|
|
||||||
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
for (int i = bot_spell_list.size() - 1; i >= 0; i--) {
|
||||||
if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) {
|
if (!IsValidSpell(bot_spell_list[i].spellid)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BotRequiresLoSToCast(spell_type, bot_spell_list[i].spellid) && !caster->HasLoS()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2091,7 +2110,6 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spell_type)
|
|||||||
case BotSpellTypes::AERains:
|
case BotSpellTypes::AERains:
|
||||||
case BotSpellTypes::AEStun:
|
case BotSpellTypes::AEStun:
|
||||||
case BotSpellTypes::AESnare:
|
case BotSpellTypes::AESnare:
|
||||||
case BotSpellTypes::AEMez:
|
|
||||||
case BotSpellTypes::AESlow:
|
case BotSpellTypes::AESlow:
|
||||||
case BotSpellTypes::AEDebuff:
|
case BotSpellTypes::AEDebuff:
|
||||||
case BotSpellTypes::AEFear:
|
case BotSpellTypes::AEFear:
|
||||||
@@ -2157,6 +2175,8 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spell_type)
|
|||||||
case BotSpellTypes::PetVeryFastHeals:
|
case BotSpellTypes::PetVeryFastHeals:
|
||||||
case BotSpellTypes::PetHoTHeals:
|
case BotSpellTypes::PetHoTHeals:
|
||||||
return RuleI(Bots, PercentChanceToCastHeal);
|
return RuleI(Bots, PercentChanceToCastHeal);
|
||||||
|
case BotSpellTypes::AEMez:
|
||||||
|
return RuleI(Bots, PercentChanceToCastAEMez);
|
||||||
default:
|
default:
|
||||||
return RuleI(Bots, PercentChanceToCastOtherType);
|
return RuleI(Bots, PercentChanceToCastOtherType);
|
||||||
}
|
}
|
||||||
@@ -2821,7 +2841,6 @@ BotSpell Bot::GetBestBotSpellForRez(Bot* caster, Mob* target, uint16 spell_type)
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Revive);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Revive);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (
|
if (
|
||||||
IsResurrectSpell(bot_spell_list_itr->SpellId) &&
|
IsResurrectSpell(bot_spell_list_itr->SpellId) &&
|
||||||
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)
|
caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)
|
||||||
@@ -2849,7 +2868,6 @@ BotSpell Bot::GetBestBotSpellForCharm(Bot* caster, Mob* target, uint16 spell_typ
|
|||||||
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Charm);
|
std::list<BotSpell> bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Charm);
|
||||||
|
|
||||||
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
for (std::list<BotSpell>::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
|
||||||
if (
|
if (
|
||||||
IsCharmSpell(bot_spell_list_itr->SpellId) &&
|
IsCharmSpell(bot_spell_list_itr->SpellId) &&
|
||||||
caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type)
|
caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type)
|
||||||
@@ -2865,181 +2883,3 @@ BotSpell Bot::GetBestBotSpellForCharm(Bot* caster, Mob* target, uint16 spell_typ
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Bot::CheckBotSpells() {
|
|
||||||
auto spell_list = BotSpellsEntriesRepository::All(content_db);
|
|
||||||
uint16 spell_id;
|
|
||||||
SPDat_Spell_Struct spell;
|
|
||||||
uint16 correct_type;
|
|
||||||
uint16 parent_type;
|
|
||||||
|
|
||||||
for (const auto& s : spell_list) {
|
|
||||||
if (!IsValidSpell(s.spell_id)) {
|
|
||||||
LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
spell = spells[s.spell_id];
|
|
||||||
spell_id = spell.id;
|
|
||||||
|
|
||||||
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] >= 255) {
|
|
||||||
LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.minlevel) {
|
|
||||||
LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the min level is currently set to {}."
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX)
|
|
||||||
, s.npc_spells_id
|
|
||||||
, s.minlevel
|
|
||||||
);
|
|
||||||
|
|
||||||
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]"
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, spell_id
|
|
||||||
, s.npc_spells_id
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, s.minlevel
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX)
|
|
||||||
, s.npc_spells_id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] < s.minlevel) {
|
|
||||||
LogBotSpellTypeChecks("{} [#{}] could be used starting at level {} for a {} [#{}] instead of the current min level of {}."
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX)
|
|
||||||
, s.npc_spells_id
|
|
||||||
, s.minlevel
|
|
||||||
);
|
|
||||||
|
|
||||||
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]"
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, spell_id
|
|
||||||
, s.npc_spells_id
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, s.minlevel
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX)
|
|
||||||
, s.npc_spells_id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.maxlevel) {
|
|
||||||
LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the max level is currently set to {}."
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)]
|
|
||||||
, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX)
|
|
||||||
, s.npc_spells_id
|
|
||||||
, s.maxlevel
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
correct_type = GetCorrectBotSpellType(s.type, spell_id);
|
|
||||||
parent_type = GetParentSpellType(correct_type);
|
|
||||||
|
|
||||||
if (RuleB(Bots, UseParentSpellTypeForChecks)) {
|
|
||||||
if (s.type == parent_type || s.type == correct_type) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (IsPetBotSpellType(s.type)) {
|
|
||||||
correct_type = GetPetBotSpellType(correct_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (correct_type == s.type) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (correct_type == UINT16_MAX) {
|
|
||||||
LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] but the correct type is unknown."
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, GetSpellTypeNameByID(s.type)
|
|
||||||
, s.type
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]"
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, GetSpellTypeNameByID(s.type)
|
|
||||||
, s.type
|
|
||||||
, GetSpellTypeNameByID(correct_type)
|
|
||||||
, correct_type
|
|
||||||
);
|
|
||||||
LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spell_id` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]"
|
|
||||||
, correct_type
|
|
||||||
, spell_id
|
|
||||||
, GetSpellName(spell_id)
|
|
||||||
, spell_id
|
|
||||||
, GetSpellTypeNameByID(s.type)
|
|
||||||
, s.type
|
|
||||||
, GetSpellTypeNameByID(correct_type)
|
|
||||||
, correct_type
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bot::MapSpellTypeLevels() {
|
|
||||||
commanded_spells_min_level.clear();
|
|
||||||
|
|
||||||
auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START });
|
|
||||||
auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END });
|
|
||||||
|
|
||||||
for (int i = start; i <= end; ++i) {
|
|
||||||
if (!Bot::IsValidBotSpellType(i)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int x = Class::Warrior; x <= Class::Berserker; ++x) {
|
|
||||||
commanded_spells_min_level[i][x] = { UINT8_MAX, "" };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto spell_list = BotSpellsEntriesRepository::All(content_db);
|
|
||||||
|
|
||||||
for (const auto& s : spell_list) {
|
|
||||||
if (!IsValidSpell(s.spell_id)) {
|
|
||||||
LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t spell_type = s.type;
|
|
||||||
int32_t bot_class = s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX;
|
|
||||||
uint8_t min_level = s.minlevel;
|
|
||||||
|
|
||||||
if (
|
|
||||||
!EQ::ValueWithin(bot_class, Class::Warrior, Class::Berserker) ||
|
|
||||||
!Bot::IsValidBotSpellType(spell_type)
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& spell_info = commanded_spells_min_level[spell_type][bot_class];
|
|
||||||
|
|
||||||
if (min_level < spell_info.min_level) {
|
|
||||||
spell_info.min_level = min_level;
|
|
||||||
spell_info.description = StringFormat(
|
|
||||||
"%s [#%u]: Level %u",
|
|
||||||
GetClassIDName(bot_class),
|
|
||||||
bot_class,
|
|
||||||
min_level
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#include "../../zone.h"
|
||||||
|
|
||||||
|
inline void RunTest(const std::string &test_name, const std::string &expected, const std::string &actual)
|
||||||
|
{
|
||||||
|
if (expected == actual) {
|
||||||
|
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||||
|
} else {
|
||||||
|
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||||
|
std::cerr << " 📌 Expected: " << expected << "\n";
|
||||||
|
std::cerr << " ❌ Got: " << actual << "\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void RunTest(const std::string &test_name, bool expected, bool actual)
|
||||||
|
{
|
||||||
|
if (expected == actual) {
|
||||||
|
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||||
|
std::cerr << " 📌 Expected: " << (expected ? "true" : "false") << "\n";
|
||||||
|
std::cerr << " ❌ Got: " << (actual ? "true" : "false") << "\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void RunTest(const std::string &test_name, int expected, int actual)
|
||||||
|
{
|
||||||
|
if (expected == actual) {
|
||||||
|
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||||
|
std::cerr << " 📌 Expected: " << expected << "\n";
|
||||||
|
std::cerr << " ❌ Got: " << actual << "\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern Zone *zone;
|
||||||
|
|
||||||
|
inline void SetupZone(std::string zone_short_name, uint32 instance_id = 0) {
|
||||||
|
LogSys.SilenceConsoleLogging();
|
||||||
|
|
||||||
|
LogSys.log_settings[Logs::ZoneState].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||||
|
LogSys.log_settings[Logs::Info].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||||
|
LogSys.log_settings[Logs::Spawns].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||||
|
|
||||||
|
// boot shell zone for testing
|
||||||
|
Zone::Bootup(ZoneID(zone_short_name), 0, false);
|
||||||
|
zone->StopShutdownTimer();
|
||||||
|
entity_list.Process();
|
||||||
|
entity_list.MobProcess();
|
||||||
|
|
||||||
|
LogSys.EnableConsoleLogging();
|
||||||
|
}
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
#include "../../common/http/httplib.h"
|
||||||
|
#include "../../common/eqemu_logsys.h"
|
||||||
|
#include "../../common/platform.h"
|
||||||
|
#include "../../zone.h"
|
||||||
|
#include "../../client.h"
|
||||||
|
#include "../../common/net/eqstream.h"
|
||||||
|
|
||||||
|
extern Zone *zone;
|
||||||
|
|
||||||
|
void ZoneCLI::TestDataBuckets(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||||
|
{
|
||||||
|
if (cmd[{"-h", "--help"}]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 break_length = 50;
|
||||||
|
int failed_count = 0;
|
||||||
|
|
||||||
|
LogSys.SilenceConsoleLogging();
|
||||||
|
|
||||||
|
// boot shell zone for testing
|
||||||
|
Zone::Bootup(ZoneID("qrg"), 0, false);
|
||||||
|
zone->StopShutdownTimer();
|
||||||
|
|
||||||
|
entity_list.Process();
|
||||||
|
entity_list.MobProcess();
|
||||||
|
|
||||||
|
LogSys.EnableConsoleLogging();
|
||||||
|
|
||||||
|
std::cout << "===========================================\n";
|
||||||
|
std::cout << "⚙\uFE0F> Running DataBuckets Tests...\n";
|
||||||
|
std::cout << "===========================================\n\n";
|
||||||
|
|
||||||
|
Client *client = new Client();
|
||||||
|
|
||||||
|
// Basic Key-Value Set/Get
|
||||||
|
client->DeleteBucket("basic_key");
|
||||||
|
client->SetBucket("basic_key", "simple_value");
|
||||||
|
std::string value = client->GetBucket("basic_key");
|
||||||
|
RunTest("Basic Key-Value Set/Get", "simple_value", value);
|
||||||
|
|
||||||
|
// Overwriting a Key
|
||||||
|
client->SetBucket("basic_key", "new_value");
|
||||||
|
value = client->GetBucket("basic_key");
|
||||||
|
RunTest("Overwriting a Key", "new_value", value);
|
||||||
|
|
||||||
|
// Deleting a Key
|
||||||
|
client->DeleteBucket("basic_key");
|
||||||
|
value = client->GetBucket("basic_key");
|
||||||
|
RunTest("Deleting a Key", "", value);
|
||||||
|
|
||||||
|
// Setting a Key with an Expiration
|
||||||
|
client->SetBucket("expiring_key", "expires_soon", "S1");
|
||||||
|
value = client->GetBucket("expiring_key");
|
||||||
|
RunTest("Setting a Key with an Expiration", "expires_soon", value);
|
||||||
|
|
||||||
|
// Ensure Expired Key is Deleted
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||||
|
value = client->GetBucket("expiring_key");
|
||||||
|
RunTest("Ensure Expired Key is Deleted", "", value);
|
||||||
|
|
||||||
|
// Cache Read/Write Consistency
|
||||||
|
client->SetBucket("cache_key", "cached_value");
|
||||||
|
value = client->GetBucket("cache_key");
|
||||||
|
RunTest("Cache Read/Write Consistency", "cached_value", value);
|
||||||
|
|
||||||
|
// Cache Clears on Key Deletion
|
||||||
|
client->DeleteBucket("cache_key");
|
||||||
|
value = client->GetBucket("cache_key");
|
||||||
|
RunTest("Cache Clears on Key Deletion", "", value);
|
||||||
|
|
||||||
|
// Setting a Full JSON String
|
||||||
|
client->SetBucket("json_key", R"({"key1":"value1","key2":"value2"})");
|
||||||
|
value = client->GetBucket("json_key");
|
||||||
|
RunTest("Setting a Full JSON String", R"({"key1":"value1","key2":"value2"})", value);
|
||||||
|
|
||||||
|
// Overwriting JSON with a Simple String
|
||||||
|
client->SetBucket("json_key", "string_value");
|
||||||
|
value = client->GetBucket("json_key");
|
||||||
|
RunTest("Overwriting JSON with a Simple String", "string_value", value);
|
||||||
|
|
||||||
|
// Deleting Non-Existent Key
|
||||||
|
client->DeleteBucket("non_existent_key");
|
||||||
|
value = client->GetBucket("non_existent_key");
|
||||||
|
RunTest("Deleting Non-Existent Key", "", value);
|
||||||
|
|
||||||
|
// Basic Key-Value Storage**
|
||||||
|
client->DeleteBucket("simple_key"); // Reset
|
||||||
|
client->SetBucket("simple_key", "simple_value");
|
||||||
|
value = client->GetBucket("simple_key");
|
||||||
|
RunTest("Basic Key-Value Set/Get", "simple_value", value);
|
||||||
|
|
||||||
|
// Nested Key Storage**
|
||||||
|
client->DeleteBucket("nested");
|
||||||
|
client->SetBucket("nested.test1", "value1");
|
||||||
|
client->SetBucket("nested.test2", "value2");
|
||||||
|
value = client->GetBucket("nested");
|
||||||
|
RunTest("Nested Key Set/Get", R"({"test1":"value1","test2":"value2"})", value);
|
||||||
|
|
||||||
|
// Prevent Overwriting Objects**
|
||||||
|
client->DeleteBucket("nested");
|
||||||
|
client->SetBucket("nested.test1.a", "value1");
|
||||||
|
client->SetBucket("nested.test2.a", "value2");
|
||||||
|
client->SetBucket("nested.test2", "new_value"); // Should be **rejected**
|
||||||
|
value = client->GetBucket("nested");
|
||||||
|
RunTest("Prevent Overwriting Objects", R"({"test1":{"a":"value1"},"test2":{"a":"value2"}})", value);
|
||||||
|
|
||||||
|
// Deleting a Specific Nested Key**
|
||||||
|
client->DeleteBucket("nested");
|
||||||
|
client->SetBucket("nested.test1", "value1");
|
||||||
|
client->SetBucket("nested.test2", "value2");
|
||||||
|
client->DeleteBucket("nested.test1");
|
||||||
|
value = client->GetBucket("nested");
|
||||||
|
RunTest("Delete Nested Key", R"({"test2":"value2"})", value);
|
||||||
|
|
||||||
|
// Deleting the Entire Parent Key**
|
||||||
|
client->DeleteBucket("nested");
|
||||||
|
value = client->GetBucket("nested");
|
||||||
|
RunTest("Delete Parent Key", "", value);
|
||||||
|
|
||||||
|
// Expiration is Ignored for Nested Keys**
|
||||||
|
client->DeleteBucket("exp_test");
|
||||||
|
client->SetBucket("exp_test.nested", "data", "S20"); // Expiration ignored
|
||||||
|
value = client->GetBucket("exp_test");
|
||||||
|
RunTest("Expiration Ignored for Nested Keys", R"({"nested":"data"})", value);
|
||||||
|
|
||||||
|
// Cache Behavior**
|
||||||
|
client->DeleteBucket("cache_test");
|
||||||
|
client->SetBucket("cache_test", "cache_value");
|
||||||
|
value = client->GetBucket("cache_test");
|
||||||
|
RunTest("Cache Read/Write Consistency", "cache_value", value);
|
||||||
|
|
||||||
|
// Ensure Deleting Parent Key Clears Cache**
|
||||||
|
client->DeleteBucket("cache_test");
|
||||||
|
value = client->GetBucket("cache_test");
|
||||||
|
RunTest("Cache Clears on Parent Delete", "", value);
|
||||||
|
|
||||||
|
// Setting an Entire JSON Object**
|
||||||
|
client->DeleteBucket("full_json");
|
||||||
|
client->SetBucket("full_json", R"({"key1":"value1","key2":{"subkey":"subvalue"}})");
|
||||||
|
value = client->GetBucket("full_json");
|
||||||
|
RunTest("Set and Retrieve Full JSON Structure", R"({"key1":"value1","key2":{"subkey":"subvalue"}})", value);
|
||||||
|
|
||||||
|
// Partial Nested Key Deletion within JSON**
|
||||||
|
client->DeleteBucket("full_json");
|
||||||
|
client->SetBucket("full_json", R"({"key1":"value1","key2":{"subkey":"subvalue"}})");
|
||||||
|
client->DeleteBucket("full_json.key2");
|
||||||
|
value = client->GetBucket("full_json");
|
||||||
|
RunTest("Delete Nested Key within JSON", R"({"key1":"value1"})", value);
|
||||||
|
|
||||||
|
// Ensure Object Protection on Overwrite Attempt**
|
||||||
|
client->DeleteBucket("complex");
|
||||||
|
client->SetBucket("complex.nested.obj1", "data1");
|
||||||
|
client->SetBucket("complex.nested.obj2", "data2");
|
||||||
|
client->SetBucket("complex.nested", "overwrite_attempt"); // Should be rejected
|
||||||
|
value = client->GetBucket("complex");
|
||||||
|
RunTest("Ensure Object Protection on Overwrite Attempt", R"({"nested":{"obj1":"data1","obj2":"data2"}})", value);
|
||||||
|
|
||||||
|
// Deleting Non-Existent Key Doesn't Break Existing Data**
|
||||||
|
client->DeleteBucket("complex");
|
||||||
|
client->SetBucket("complex.nested.obj1", "data1");
|
||||||
|
client->SetBucket("complex.nested.obj2", "data2");
|
||||||
|
client->DeleteBucket("does_not_exist"); // Should do nothing
|
||||||
|
value = client->GetBucket("complex");
|
||||||
|
RunTest("Deleting Non-Existent Key Doesn't Break Existing Data", R"({"nested":{"obj1":"data1","obj2":"data2"}})", value);
|
||||||
|
|
||||||
|
// Get nested key value one level up **
|
||||||
|
client->DeleteBucket("complex");
|
||||||
|
client->SetBucket("complex.nested.obj1", "data1");
|
||||||
|
client->SetBucket("complex.nested.obj2", "data2");
|
||||||
|
value = client->GetBucket("complex.nested");
|
||||||
|
RunTest("Get nested key value", R"({"obj1":"data1","obj2":"data2"})", value);
|
||||||
|
|
||||||
|
// Get nested key value deep **
|
||||||
|
client->DeleteBucket("complex");
|
||||||
|
client->SetBucket("complex.nested.obj1", "data1");
|
||||||
|
client->SetBucket("complex.nested.obj2", "data2");
|
||||||
|
value = client->GetBucket("complex.nested.obj2");
|
||||||
|
RunTest("Get nested key value deep", R"(data2)", value);
|
||||||
|
|
||||||
|
// Retrieve Nested Key from Plain String**
|
||||||
|
client->DeleteBucket("plain_string");
|
||||||
|
client->SetBucket("plain_string", "some_value");
|
||||||
|
value = client->GetBucket("plain_string.nested");
|
||||||
|
RunTest("Retrieve Nested Key from Plain String", "", value);
|
||||||
|
|
||||||
|
// Store and Retrieve JSON Array**
|
||||||
|
client->DeleteBucket("json_array");
|
||||||
|
client->SetBucket("json_array", R"(["item1", "item2"])");
|
||||||
|
value = client->GetBucket("json_array");
|
||||||
|
RunTest("Store and Retrieve JSON Array", R"(["item1", "item2"])", value);
|
||||||
|
|
||||||
|
// // Prevent Overwriting Array with Object**
|
||||||
|
// client->DeleteBucket("json_array");
|
||||||
|
// client->SetBucket("json_array", R"(["item1", "item2"])");
|
||||||
|
// client->SetBucket("json_array.item", "new_value"); // Should be rejected
|
||||||
|
// value = client->GetBucket("json_array");
|
||||||
|
// RunTest("Prevent Overwriting Array with Object", R"(["item1", "item2"])", value);
|
||||||
|
|
||||||
|
// Retrieve Non-Existent Nested Key**
|
||||||
|
client->DeleteBucket("nested_partial");
|
||||||
|
client->SetBucket("nested_partial.level1", R"({"exists": "yes"})");
|
||||||
|
value = client->GetBucket("nested_partial.level1.non_existent");
|
||||||
|
RunTest("Retrieve Non-Existent Nested Key", "", value);
|
||||||
|
|
||||||
|
// Overwriting Parent Key Deletes Children**
|
||||||
|
client->DeleteBucket("nested_override");
|
||||||
|
client->SetBucket("nested_override.child", "data");
|
||||||
|
client->SetBucket("nested_override", "new_parent_value"); // Should remove `child`
|
||||||
|
value = client->GetBucket("nested_override");
|
||||||
|
RunTest("Overwriting Parent Key Deletes Children", "new_parent_value", value);
|
||||||
|
|
||||||
|
// Store and Retrieve Empty JSON Object**
|
||||||
|
client->DeleteBucket("empty_json");
|
||||||
|
client->SetBucket("empty_json", R"({})");
|
||||||
|
value = client->GetBucket("empty_json");
|
||||||
|
RunTest("Store and Retrieve Empty JSON Object", R"({})", value);
|
||||||
|
|
||||||
|
// Store and Retrieve JSON String**
|
||||||
|
client->DeleteBucket("json_string");
|
||||||
|
client->SetBucket("json_string", R"("this is a string")");
|
||||||
|
value = client->GetBucket("json_string");
|
||||||
|
RunTest("Store and Retrieve JSON String", R"("this is a string")", value);
|
||||||
|
|
||||||
|
// Deeply Nested Key Retrieval**
|
||||||
|
client->DeleteBucket("deep_nested");
|
||||||
|
client->SetBucket("deep_nested.level1.level2.level3.level4.level5", "final_value");
|
||||||
|
value = client->GetBucket("deep_nested.level1.level2.level3.level4.level5");
|
||||||
|
RunTest("Deeply Nested Key Retrieval", "final_value", value);
|
||||||
|
|
||||||
|
// Setting a Key with an Expiration
|
||||||
|
client->SetBucket("nested_expire.test.test", "shouldnt_expire", "S1");
|
||||||
|
value = client->GetBucket("nested_expire");
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||||
|
RunTest("Setting a nested key with an expiration protection test", R"({"test":{"test":"shouldnt_expire"}})", value);
|
||||||
|
|
||||||
|
// Delete Deep Nested Key Keeps Parent**
|
||||||
|
// client->DeleteBucket("deep_nested");
|
||||||
|
// client->SetBucket("deep_nested.level1.level2.level3", R"({"key": "value"})");
|
||||||
|
// client->DeleteBucket("deep_nested.level1.level2.level3.key");
|
||||||
|
// value = client->GetBucket("deep_nested.level1.level2.level3");
|
||||||
|
// RunTest("Delete Deep Nested Key Keeps Parent", "{}", value);
|
||||||
|
|
||||||
|
std::cout << "\n===========================================\n";
|
||||||
|
std::cout << "✅ All DataBucket Tests Completed!\n";
|
||||||
|
std::cout << "===========================================\n";
|
||||||
|
}
|
||||||
@@ -1,27 +1,73 @@
|
|||||||
#include "../../common/http/httplib.h"
|
#include "../../common/http/httplib.h"
|
||||||
#include "../../common/eqemu_logsys.h"
|
#include "../../common/eqemu_logsys.h"
|
||||||
#include "../../common/platform.h"
|
#include "../../common/platform.h"
|
||||||
#include "../zone.h"
|
#include "../../zone.h"
|
||||||
#include "../client.h"
|
#include "../../client.h"
|
||||||
#include "../../common/net/eqstream.h"
|
#include "../../common/net/eqstream.h"
|
||||||
|
#include "../../common/json/json.hpp"
|
||||||
|
|
||||||
extern Zone *zone;
|
extern Zone *zone;
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &description)
|
struct HandinEntry {
|
||||||
|
std::string item_id = "0";
|
||||||
|
uint32 count = 0;
|
||||||
|
const EQ::ItemInstance *item = nullptr;
|
||||||
|
bool is_multiquest_item = false; // state
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HandinMoney {
|
||||||
|
uint32 platinum = 0;
|
||||||
|
uint32 gold = 0;
|
||||||
|
uint32 silver = 0;
|
||||||
|
uint32 copper = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Handin {
|
||||||
|
std::vector<HandinEntry> items = {}; // items can be removed from this set as successful handins are made
|
||||||
|
HandinMoney money = {}; // money can be removed from this set as successful handins are made
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestCase {
|
||||||
|
std::string description;
|
||||||
|
Handin hand_in;
|
||||||
|
Handin required;
|
||||||
|
Handin returned;
|
||||||
|
bool handin_check_result;
|
||||||
|
};
|
||||||
|
|
||||||
|
void RunSerializedTest(const std::string &test_name, const std::string &expected, const std::string &actual)
|
||||||
|
{
|
||||||
|
if (expected == actual) {
|
||||||
|
std::cout << "[✅] " << test_name << " PASSED\n";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cerr << "[❌] " << test_name << " FAILED\n";
|
||||||
|
std::cerr << " 📌 Expected: " << expected << "\n";
|
||||||
|
std::cerr << " ❌ Got: " << actual << "\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SerializeHandin(const std::map<std::string, uint32> &items, const HandinMoney &money)
|
||||||
|
{
|
||||||
|
json j;
|
||||||
|
j["items"] = items;
|
||||||
|
j["money"] = {
|
||||||
|
{"platinum", money.platinum},
|
||||||
|
{"gold", money.gold},
|
||||||
|
{"silver", money.silver},
|
||||||
|
{"copper", money.copper}
|
||||||
|
};
|
||||||
|
return j.dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZoneCLI::TestNpcHandins(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||||
{
|
{
|
||||||
if (cmd[{"-h", "--help"}]) {
|
if (cmd[{"-h", "--help"}]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 break_length = 50;
|
|
||||||
int failed_count = 0;
|
|
||||||
|
|
||||||
RegisterExecutablePlatform(EQEmuExePlatform::ExePlatformZoneSidecar);
|
|
||||||
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
|
||||||
LogInfo("Booting test zone for NPC handins");
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
|
||||||
|
|
||||||
LogSys.SilenceConsoleLogging();
|
LogSys.SilenceConsoleLogging();
|
||||||
|
|
||||||
Zone::Bootup(ZoneID("qrg"), 0, false);
|
Zone::Bootup(ZoneID("qrg"), 0, false);
|
||||||
@@ -30,9 +76,9 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
entity_list.Process();
|
entity_list.Process();
|
||||||
entity_list.MobProcess();
|
entity_list.MobProcess();
|
||||||
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
std::cout << "===========================================\n";
|
||||||
LogInfo("> Done booting test zone");
|
std::cout << "⚙\uFE0F> Running Hand-in Tests...\n";
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
std::cout << "===========================================\n\n";
|
||||||
|
|
||||||
Client *c = new Client();
|
Client *c = new Client();
|
||||||
auto npc_type = content_db.LoadNPCTypesData(754008);
|
auto npc_type = content_db.LoadNPCTypesData(754008);
|
||||||
@@ -46,36 +92,6 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
|
|
||||||
entity_list.AddNPC(npc);
|
entity_list.AddNPC(npc);
|
||||||
|
|
||||||
LogInfo("> Spawned NPC [{}]", npc->GetCleanName());
|
|
||||||
LogInfo("> Spawned client [{}]", c->GetCleanName());
|
|
||||||
|
|
||||||
struct HandinEntry {
|
|
||||||
std::string item_id = "0";
|
|
||||||
uint32 count = 0;
|
|
||||||
const EQ::ItemInstance *item = nullptr;
|
|
||||||
bool is_multiquest_item = false; // state
|
|
||||||
};
|
|
||||||
|
|
||||||
struct HandinMoney {
|
|
||||||
uint32 platinum = 0;
|
|
||||||
uint32 gold = 0;
|
|
||||||
uint32 silver = 0;
|
|
||||||
uint32 copper = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Handin {
|
|
||||||
std::vector<HandinEntry> items = {}; // items can be removed from this set as successful handins are made
|
|
||||||
HandinMoney money = {}; // money can be removed from this set as successful handins are made
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TestCase {
|
|
||||||
std::string description = "";
|
|
||||||
Handin hand_in;
|
|
||||||
Handin required;
|
|
||||||
Handin returned;
|
|
||||||
bool handin_check_result;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<TestCase> test_cases = {
|
std::vector<TestCase> test_cases = {
|
||||||
TestCase{
|
TestCase{
|
||||||
.description = "Test basic cloth-cap hand-in",
|
.description = "Test basic cloth-cap hand-in",
|
||||||
@@ -155,7 +171,10 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
.items = {},
|
.items = {},
|
||||||
.money = {.platinum = 100},
|
.money = {.platinum = 100},
|
||||||
},
|
},
|
||||||
.returned = {},
|
.returned = {
|
||||||
|
.items = {},
|
||||||
|
.money = {.platinum = 1},
|
||||||
|
},
|
||||||
.handin_check_result = false,
|
.handin_check_result = false,
|
||||||
},
|
},
|
||||||
TestCase{
|
TestCase{
|
||||||
@@ -168,7 +187,10 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
.items = {},
|
.items = {},
|
||||||
.money = {.platinum = 100, .gold = 100, .silver = 100, .copper = 100},
|
.money = {.platinum = 100, .gold = 100, .silver = 100, .copper = 100},
|
||||||
},
|
},
|
||||||
.returned = {},
|
.returned = {
|
||||||
|
.items = {},
|
||||||
|
.money = {.platinum = 1, .gold = 1, .silver = 1, .copper = 1},
|
||||||
|
},
|
||||||
.handin_check_result = false,
|
.handin_check_result = false,
|
||||||
},
|
},
|
||||||
TestCase{
|
TestCase{
|
||||||
@@ -217,9 +239,12 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
},
|
},
|
||||||
.returned = {
|
.returned = {
|
||||||
.items = {
|
.items = {
|
||||||
HandinEntry{.item_id = "1001", .count = 1},
|
HandinEntry{
|
||||||
|
.item_id = "1001", .count = 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
.money = {.platinum = 1},
|
||||||
|
},
|
||||||
.handin_check_result = false,
|
.handin_check_result = false,
|
||||||
},
|
},
|
||||||
TestCase{
|
TestCase{
|
||||||
@@ -304,12 +329,7 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
HandinEntry{.item_id = "1007", .count = 1},
|
HandinEntry{.item_id = "1007", .count = 1},
|
||||||
HandinEntry{.item_id = "1007", .count = 1},
|
HandinEntry{.item_id = "1007", .count = 1},
|
||||||
},
|
},
|
||||||
.money = {
|
.money = {},
|
||||||
.platinum = 1,
|
|
||||||
.gold = 666,
|
|
||||||
.silver = 234,
|
|
||||||
.copper = 444,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
.handin_check_result = true,
|
.handin_check_result = true,
|
||||||
},
|
},
|
||||||
@@ -411,14 +431,12 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
// turn this on to see debugging output
|
// turn this on to see debugging output
|
||||||
LogSys.log_settings[Logs::NpcHandin].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
LogSys.log_settings[Logs::NpcHandin].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||||
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
for (auto &test: test_cases) {
|
||||||
|
|
||||||
for (auto &test_case: test_cases) {
|
|
||||||
hand_ins.clear();
|
hand_ins.clear();
|
||||||
required.clear();
|
required.clear();
|
||||||
items.clear();
|
items.clear();
|
||||||
|
|
||||||
for (auto &hand_in: test_case.hand_in.items) {
|
for (auto &hand_in: test.hand_in.items) {
|
||||||
auto item_id = Strings::ToInt(hand_in.item_id);
|
auto item_id = Strings::ToInt(hand_in.item_id);
|
||||||
EQ::ItemInstance *inst = database.CreateItem(item_id);
|
EQ::ItemInstance *inst = database.CreateItem(item_id);
|
||||||
if (inst->IsStackable()) {
|
if (inst->IsStackable()) {
|
||||||
@@ -434,72 +452,76 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
}
|
}
|
||||||
|
|
||||||
// money
|
// money
|
||||||
if (test_case.hand_in.money.platinum > 0) {
|
if (test.hand_in.money.platinum > 0) {
|
||||||
hand_ins["platinum"] = test_case.hand_in.money.platinum;
|
hand_ins["platinum"] = test.hand_in.money.platinum;
|
||||||
}
|
}
|
||||||
if (test_case.hand_in.money.gold > 0) {
|
if (test.hand_in.money.gold > 0) {
|
||||||
hand_ins["gold"] = test_case.hand_in.money.gold;
|
hand_ins["gold"] = test.hand_in.money.gold;
|
||||||
}
|
}
|
||||||
if (test_case.hand_in.money.silver > 0) {
|
if (test.hand_in.money.silver > 0) {
|
||||||
hand_ins["silver"] = test_case.hand_in.money.silver;
|
hand_ins["silver"] = test.hand_in.money.silver;
|
||||||
}
|
}
|
||||||
if (test_case.hand_in.money.copper > 0) {
|
if (test.hand_in.money.copper > 0) {
|
||||||
hand_ins["copper"] = test_case.hand_in.money.copper;
|
hand_ins["copper"] = test.hand_in.money.copper;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &req: test_case.required.items) {
|
for (auto &req: test.required.items) {
|
||||||
required[req.item_id] = req.count;
|
required[req.item_id] = req.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// money
|
// money
|
||||||
if (test_case.required.money.platinum > 0) {
|
if (test.required.money.platinum > 0) {
|
||||||
required["platinum"] = test_case.required.money.platinum;
|
required["platinum"] = test.required.money.platinum;
|
||||||
}
|
}
|
||||||
if (test_case.required.money.gold > 0) {
|
if (test.required.money.gold > 0) {
|
||||||
required["gold"] = test_case.required.money.gold;
|
required["gold"] = test.required.money.gold;
|
||||||
}
|
}
|
||||||
if (test_case.required.money.silver > 0) {
|
if (test.required.money.silver > 0) {
|
||||||
required["silver"] = test_case.required.money.silver;
|
required["silver"] = test.required.money.silver;
|
||||||
}
|
}
|
||||||
if (test_case.required.money.copper > 0) {
|
if (test.required.money.copper > 0) {
|
||||||
required["copper"] = test_case.required.money.copper;
|
required["copper"] = test.required.money.copper;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = npc->CheckHandin(c, hand_ins, required, items);
|
auto result = npc->CheckHandin(c, hand_ins, required, items);
|
||||||
if (result != test_case.handin_check_result) {
|
|
||||||
failed_count++;
|
RunTest(test.description, test.handin_check_result, result);
|
||||||
LogError("FAIL [{}]", test_case.description);
|
|
||||||
// print out the hand-ins
|
|
||||||
LogError("Hand-ins >");
|
|
||||||
for (auto &hand_in: hand_ins) {
|
|
||||||
LogError(" > Item [{}] count [{}]", hand_in.first, hand_in.second);
|
|
||||||
}
|
|
||||||
LogError("Required >");
|
|
||||||
for (auto &req: required) {
|
|
||||||
LogError(" > Item [{}] count [{}]", req.first, req.second);
|
|
||||||
}
|
|
||||||
LogError("Expected [{}] got [{}]", test_case.handin_check_result, result);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LogInfo("PASS [{}]", test_case.description);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto returned = npc->ReturnHandinItems(c);
|
auto returned = npc->ReturnHandinItems(c);
|
||||||
|
|
||||||
// assert that returned items are expected
|
std::map<std::string, uint32> returned_items;
|
||||||
for (auto &item: test_case.returned.items) {
|
HandinMoney returned_money{};
|
||||||
auto found = false;
|
|
||||||
for (auto &ret: returned.items) {
|
// Serialize returned items
|
||||||
if (ret.item_id == item.item_id) {
|
for (const auto &ret: returned.items) {
|
||||||
found = true;
|
// if (ret.item->IsStackable() && ret.item->GetCharges() != ret.count) {
|
||||||
break;
|
// ret.item->SetCharges(ret.count);
|
||||||
}
|
// }
|
||||||
}
|
returned_items[ret.item_id] += ret.count;
|
||||||
if (!found) {
|
|
||||||
LogError("Returned item [{}] not expected", item.item_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Serialize returned money
|
||||||
|
returned_money.platinum = returned.money.platinum;
|
||||||
|
returned_money.gold = returned.money.gold;
|
||||||
|
returned_money.silver = returned.money.silver;
|
||||||
|
returned_money.copper = returned.money.copper;
|
||||||
|
|
||||||
|
// Serialize expected and actual return values for comparison
|
||||||
|
std::map<std::string, uint32> expected_returned_items;
|
||||||
|
for (const auto &entry: test.returned.items) {
|
||||||
|
expected_returned_items[entry.item_id] += entry.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string expected_serialized = SerializeHandin(
|
||||||
|
expected_returned_items,
|
||||||
|
test.returned.money
|
||||||
|
);
|
||||||
|
|
||||||
|
std::string actual_serialized = SerializeHandin(returned_items, returned_money);
|
||||||
|
|
||||||
|
// Run serialization check test
|
||||||
|
RunSerializedTest(test.description + " (Return Validation)", expected_serialized, actual_serialized);
|
||||||
|
|
||||||
npc->ResetHandin();
|
npc->ResetHandin();
|
||||||
|
|
||||||
if (LogSys.log_settings[Logs::NpcHandin].log_to_console > 0) {
|
if (LogSys.log_settings[Logs::NpcHandin].log_to_console > 0) {
|
||||||
@@ -508,11 +530,7 @@ void ZoneCLI::NpcHandins(int argc, char **argv, argh::parser &cmd, std::string &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failed_count > 0) {
|
std::cout << "\n===========================================\n";
|
||||||
LogError("Failed [{}] tests", failed_count);
|
std::cout << "✅ All NPC Hand-in Tests Completed!\n";
|
||||||
std::exit(1);
|
std::cout << "===========================================\n";
|
||||||
}
|
|
||||||
else {
|
|
||||||
LogInfo("All tests passed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
#include "../../common/http/httplib.h"
|
#include "../../common/http/httplib.h"
|
||||||
#include "../../common/eqemu_logsys.h"
|
#include "../../common/eqemu_logsys.h"
|
||||||
#include "../../common/platform.h"
|
#include "../../common/platform.h"
|
||||||
#include "../zone.h"
|
#include "../../zone.h"
|
||||||
#include "../client.h"
|
#include "../../client.h"
|
||||||
#include "../../common/net/eqstream.h"
|
#include "../../common/net/eqstream.h"
|
||||||
|
|
||||||
extern Zone *zone;
|
extern Zone *zone;
|
||||||
|
|
||||||
void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std::string &description)
|
void ZoneCLI::TestNpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||||
{
|
{
|
||||||
if (cmd[{"-h", "--help"}]) {
|
if (cmd[{"-h", "--help"}]) {
|
||||||
return;
|
return;
|
||||||
@@ -16,12 +16,6 @@ void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std
|
|||||||
uint32 break_length = 50;
|
uint32 break_length = 50;
|
||||||
int failed_count = 0;
|
int failed_count = 0;
|
||||||
|
|
||||||
RegisterExecutablePlatform(EQEmuExePlatform::ExePlatformZoneSidecar);
|
|
||||||
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
|
||||||
LogInfo("Booting test zone for NPC handins (MultiQuest)");
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
|
||||||
|
|
||||||
LogSys.SilenceConsoleLogging();
|
LogSys.SilenceConsoleLogging();
|
||||||
|
|
||||||
Zone::Bootup(ZoneID("qrg"), 0, false);
|
Zone::Bootup(ZoneID("qrg"), 0, false);
|
||||||
@@ -30,9 +24,9 @@ void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std
|
|||||||
entity_list.Process();
|
entity_list.Process();
|
||||||
entity_list.MobProcess();
|
entity_list.MobProcess();
|
||||||
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
std::cout << "===========================================\n";
|
||||||
LogInfo("> Done booting test zone");
|
std::cout << "⚙\uFE0F> Running Hand-in Tests (Multi-Quest)...\n";
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
std::cout << "===========================================\n\n";
|
||||||
|
|
||||||
Client *c = new Client();
|
Client *c = new Client();
|
||||||
auto npc_type = content_db.LoadNPCTypesData(754008);
|
auto npc_type = content_db.LoadNPCTypesData(754008);
|
||||||
@@ -47,9 +41,6 @@ void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std
|
|||||||
entity_list.AddNPC(npc);
|
entity_list.AddNPC(npc);
|
||||||
npc->MultiQuestEnable();
|
npc->MultiQuestEnable();
|
||||||
|
|
||||||
LogInfo("> Spawned NPC [{}]", npc->GetCleanName());
|
|
||||||
LogInfo("> Spawned client [{}]", c->GetCleanName());
|
|
||||||
|
|
||||||
struct HandinEntry {
|
struct HandinEntry {
|
||||||
std::string item_id = "0";
|
std::string item_id = "0";
|
||||||
uint32 count = 0;
|
uint32 count = 0;
|
||||||
@@ -108,12 +99,10 @@ void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std
|
|||||||
// turn this on to see debugging output
|
// turn this on to see debugging output
|
||||||
LogSys.log_settings[Logs::NpcHandin].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
LogSys.log_settings[Logs::NpcHandin].log_to_console = std::getenv("DEBUG") ? 3 : 0;
|
||||||
|
|
||||||
LogInfo("{}", Strings::Repeat("-", break_length));
|
for (auto &test: test_cases) {
|
||||||
|
|
||||||
for (auto &test_case: test_cases) {
|
|
||||||
required.clear();
|
required.clear();
|
||||||
|
|
||||||
for (auto &hand_in: test_case.hand_in.items) {
|
for (auto &hand_in: test.hand_in.items) {
|
||||||
hand_ins.clear();
|
hand_ins.clear();
|
||||||
items.clear();
|
items.clear();
|
||||||
|
|
||||||
@@ -135,72 +124,43 @@ void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std
|
|||||||
}
|
}
|
||||||
|
|
||||||
// money
|
// money
|
||||||
if (test_case.hand_in.money.platinum > 0) {
|
if (test.hand_in.money.platinum > 0) {
|
||||||
hand_ins["platinum"] = test_case.hand_in.money.platinum;
|
hand_ins["platinum"] = test.hand_in.money.platinum;
|
||||||
}
|
}
|
||||||
if (test_case.hand_in.money.gold > 0) {
|
if (test.hand_in.money.gold > 0) {
|
||||||
hand_ins["gold"] = test_case.hand_in.money.gold;
|
hand_ins["gold"] = test.hand_in.money.gold;
|
||||||
}
|
}
|
||||||
if (test_case.hand_in.money.silver > 0) {
|
if (test.hand_in.money.silver > 0) {
|
||||||
hand_ins["silver"] = test_case.hand_in.money.silver;
|
hand_ins["silver"] = test.hand_in.money.silver;
|
||||||
}
|
}
|
||||||
if (test_case.hand_in.money.copper > 0) {
|
if (test.hand_in.money.copper > 0) {
|
||||||
hand_ins["copper"] = test_case.hand_in.money.copper;
|
hand_ins["copper"] = test.hand_in.money.copper;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &req: test_case.required.items) {
|
for (auto &req: test.required.items) {
|
||||||
required[req.item_id] = req.count;
|
required[req.item_id] = req.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
// money
|
// money
|
||||||
if (test_case.required.money.platinum > 0) {
|
if (test.required.money.platinum > 0) {
|
||||||
required["platinum"] = test_case.required.money.platinum;
|
required["platinum"] = test.required.money.platinum;
|
||||||
}
|
}
|
||||||
if (test_case.required.money.gold > 0) {
|
if (test.required.money.gold > 0) {
|
||||||
required["gold"] = test_case.required.money.gold;
|
required["gold"] = test.required.money.gold;
|
||||||
}
|
}
|
||||||
if (test_case.required.money.silver > 0) {
|
if (test.required.money.silver > 0) {
|
||||||
required["silver"] = test_case.required.money.silver;
|
required["silver"] = test.required.money.silver;
|
||||||
}
|
}
|
||||||
if (test_case.required.money.copper > 0) {
|
if (test.required.money.copper > 0) {
|
||||||
required["copper"] = test_case.required.money.copper;
|
required["copper"] = test.required.money.copper;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = npc->CheckHandin(c, hand_ins, required, items);
|
auto result = npc->CheckHandin(c, hand_ins, required, items);
|
||||||
if (result != test_case.handin_check_result) {
|
|
||||||
failed_count++;
|
RunTest(test.description, test.handin_check_result, result);
|
||||||
LogError("FAIL [{}]", test_case.description);
|
|
||||||
// print out the hand-ins
|
|
||||||
LogError("Hand-ins >");
|
|
||||||
for (auto &item: npc->GetHandin().items) {
|
|
||||||
LogError(" > Item [{}] count [{}]", item.item_id, item.count);
|
|
||||||
}
|
|
||||||
LogError("Required >");
|
|
||||||
for (auto &req: required) {
|
|
||||||
LogError(" > Item [{}] count [{}]", req.first, req.second);
|
|
||||||
}
|
|
||||||
LogError("Expected [{}] got [{}]", test_case.handin_check_result, result);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LogInfo("PASS [{}]", test_case.description);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto returned = npc->ReturnHandinItems(c);
|
auto returned = npc->ReturnHandinItems(c);
|
||||||
|
|
||||||
// assert that returned items are expected
|
|
||||||
for (auto &item: test_case.returned.items) {
|
|
||||||
auto found = false;
|
|
||||||
for (auto &ret: returned.items) {
|
|
||||||
if (ret.item_id == item.item_id) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
LogError("Returned item [{}] not expected", item.item_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
npc->ResetHandin();
|
npc->ResetHandin();
|
||||||
|
|
||||||
if (LogSys.log_settings[Logs::NpcHandin].log_to_console > 0) {
|
if (LogSys.log_settings[Logs::NpcHandin].log_to_console > 0) {
|
||||||
@@ -209,11 +169,7 @@ void ZoneCLI::NpcHandinsMultiQuest(int argc, char **argv, argh::parser &cmd, std
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (failed_count > 0) {
|
std::cout << "\n===========================================\n";
|
||||||
LogError("Failed [{}] tests", failed_count);
|
std::cout << "✅ All NPC Hand-in Tests Completed (Multi-Quest)!\n";
|
||||||
std::exit(1);
|
std::cout << "===========================================\n";
|
||||||
}
|
|
||||||
else {
|
|
||||||
LogInfo("All tests passed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
+144
-37
@@ -708,6 +708,9 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Client::~Client() {
|
Client::~Client() {
|
||||||
|
entity_list.RemoveMobFromCloseLists(this);
|
||||||
|
m_close_mobs.clear();
|
||||||
|
|
||||||
if (ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB (Parcel, EnableParcelMerchants)) {
|
if (ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB (Parcel, EnableParcelMerchants)) {
|
||||||
DoParcelCancel();
|
DoParcelCancel();
|
||||||
}
|
}
|
||||||
@@ -879,9 +882,13 @@ void Client::SendZoneInPackets()
|
|||||||
//SendGuildMembers();
|
//SendGuildMembers();
|
||||||
SendGuildURL();
|
SendGuildURL();
|
||||||
SendGuildChannel();
|
SendGuildChannel();
|
||||||
|
if (RuleB(Guild, EnableLFGuild)) {
|
||||||
SendGuildLFGuildStatus();
|
SendGuildLFGuildStatus();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (RuleB(Guild, EnableLFGuild)) {
|
||||||
SendLFGuildStatus();
|
SendLFGuildStatus();
|
||||||
|
}
|
||||||
|
|
||||||
//No idea why live sends this if even were not in a guild
|
//No idea why live sends this if even were not in a guild
|
||||||
SendGuildMOTD();
|
SendGuildMOTD();
|
||||||
@@ -965,6 +972,10 @@ bool Client::SaveAA()
|
|||||||
|
|
||||||
m_pp.aapoints_spent = aa_points_spent + m_epp.expended_aa;
|
m_pp.aapoints_spent = aa_points_spent + m_epp.expended_aa;
|
||||||
|
|
||||||
|
if (v.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return CharacterAlternateAbilitiesRepository::ReplaceMany(database, v);
|
return CharacterAlternateAbilitiesRepository::ReplaceMany(database, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2542,40 +2553,59 @@ void Client::ChangeLastName(std::string last_name) {
|
|||||||
safe_delete(outapp);
|
safe_delete(outapp);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::ChangeFirstName(const char* in_firstname, const char* gmname)
|
// Deprecated, this packet does not actually work in ROF2
|
||||||
|
bool Client::ChangeFirstName(const std::string in_firstname, const std::string gmname)
|
||||||
{
|
{
|
||||||
// check duplicate name
|
if (!ChangeFirstName(in_firstname)) {
|
||||||
bool used_name = database.IsNameUsed((const char*) in_firstname);
|
|
||||||
if (used_name) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update character_
|
|
||||||
if(!database.UpdateName(GetName(), in_firstname))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// update pp
|
|
||||||
memset(m_pp.name, 0, sizeof(m_pp.name));
|
|
||||||
snprintf(m_pp.name, sizeof(m_pp.name), "%s", in_firstname);
|
|
||||||
strcpy(name, m_pp.name);
|
|
||||||
Save();
|
|
||||||
|
|
||||||
// send name update packet
|
// send name update packet
|
||||||
auto outapp = new EQApplicationPacket(OP_GMNameChange, sizeof(GMName_Struct));
|
auto outapp = new EQApplicationPacket(OP_GMNameChange, sizeof(GMName_Struct));
|
||||||
GMName_Struct* gmn=(GMName_Struct*)outapp->pBuffer;
|
GMName_Struct* gmn=(GMName_Struct*)outapp->pBuffer;
|
||||||
strn0cpy(gmn->gmname,gmname,64);
|
strn0cpy(gmn->gmname,gmname.c_str(),64);
|
||||||
strn0cpy(gmn->oldname,GetName(),64);
|
strn0cpy(gmn->oldname,GetName(),64);
|
||||||
strn0cpy(gmn->newname,in_firstname,64);
|
strn0cpy(gmn->newname,in_firstname.c_str(),64);
|
||||||
gmn->unknown[0] = 1;
|
gmn->unknown[0] = 1;
|
||||||
gmn->unknown[1] = 1;
|
gmn->unknown[1] = 1;
|
||||||
gmn->unknown[2] = 1;
|
gmn->unknown[2] = 1;
|
||||||
entity_list.QueueClients(this, outapp, false);
|
entity_list.QueueClients(this, outapp, false);
|
||||||
safe_delete(outapp);
|
safe_delete(outapp);
|
||||||
|
|
||||||
|
// success
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::ChangeFirstName(const std::string in_firstname)
|
||||||
|
{
|
||||||
|
// check duplicate name
|
||||||
|
bool used_name = database.IsNameUsed(in_firstname) || database.IsPetNameUsed(in_firstname);
|
||||||
|
if (used_name || !database.CheckNameFilter(in_firstname, false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update character_
|
||||||
|
if(!database.UpdateNameByID(CharacterID(), in_firstname))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Send Name Update to Clients
|
||||||
|
SendRename(this, GetName(), in_firstname.c_str());
|
||||||
|
SetName(in_firstname.c_str());
|
||||||
|
|
||||||
|
// update pp
|
||||||
|
memset(m_pp.name, 0, sizeof(m_pp.name));
|
||||||
|
snprintf(m_pp.name, sizeof(m_pp.name), "%s", in_firstname.c_str());
|
||||||
|
strcpy(name, m_pp.name);
|
||||||
|
Save();
|
||||||
|
|
||||||
|
// Update the active char in account table
|
||||||
|
database.UpdateLiveChar(in_firstname, AccountID());
|
||||||
|
|
||||||
// finally, update the /who list
|
// finally, update the /who list
|
||||||
UpdateWho();
|
UpdateWho();
|
||||||
|
|
||||||
// success
|
// success
|
||||||
|
ClearNameChange();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4622,7 +4652,7 @@ void Client::KeyRingLoad()
|
|||||||
const auto &l = KeyringRepository::GetWhere(
|
const auto &l = KeyringRepository::GetWhere(
|
||||||
database,
|
database,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"`char_id` = {} ORDER BY `item_id`",
|
"`char_id` = {} ORDER BY `item_id` ASC",
|
||||||
character_id
|
character_id
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -4631,21 +4661,15 @@ void Client::KeyRingLoad()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& e : l) {
|
||||||
for (const auto &e : l) {
|
|
||||||
keyring.emplace_back(e.item_id);
|
keyring.emplace_back(e.item_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::KeyRingAdd(uint32 item_id)
|
bool Client::KeyRingAdd(uint32 item_id)
|
||||||
{
|
{
|
||||||
if (!item_id) {
|
if (!item_id || KeyRingCheck(item_id)) {
|
||||||
return;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
const bool found = KeyRingCheck(item_id);
|
|
||||||
if (found) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto e = KeyringRepository::NewEntity();
|
auto e = KeyringRepository::NewEntity();
|
||||||
@@ -4656,14 +4680,14 @@ void Client::KeyRingAdd(uint32 item_id)
|
|||||||
e = KeyringRepository::InsertOne(database, e);
|
e = KeyringRepository::InsertOne(database, e);
|
||||||
|
|
||||||
if (!e.id) {
|
if (!e.id) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
keyring.emplace_back(item_id);
|
keyring.emplace_back(item_id);
|
||||||
|
|
||||||
if (!RuleB(World, UseItemLinksForKeyRing)) {
|
if (!RuleB(World, UseItemLinksForKeyRing)) {
|
||||||
Message(Chat::LightBlue, "Added to keyring.");
|
Message(Chat::LightBlue, "Added to keyring.");
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &item_link = database.CreateItemLink(item_id);
|
const std::string &item_link = database.CreateItemLink(item_id);
|
||||||
@@ -4675,17 +4699,25 @@ void Client::KeyRingAdd(uint32 item_id)
|
|||||||
item_link
|
item_link
|
||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::KeyRingCheck(uint32 item_id)
|
bool Client::KeyRingCheck(uint32 item_id)
|
||||||
{
|
{
|
||||||
for (const auto &e : keyring) {
|
return std::find(keyring.begin(), keyring.end(), item_id) != keyring.end();
|
||||||
if (e == item_id) {
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
bool Client::KeyRingClear()
|
||||||
|
{
|
||||||
|
keyring.clear();
|
||||||
|
|
||||||
|
return KeyringRepository::DeleteWhere(
|
||||||
|
database,
|
||||||
|
fmt::format(
|
||||||
|
"`char_id` = {}",
|
||||||
|
CharacterID()
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::KeyRingList()
|
void Client::KeyRingList()
|
||||||
@@ -4694,16 +4726,91 @@ void Client::KeyRingList()
|
|||||||
|
|
||||||
const EQ::ItemData *item = nullptr;
|
const EQ::ItemData *item = nullptr;
|
||||||
|
|
||||||
for (const auto &e : keyring) {
|
for (const uint32& e : keyring) {
|
||||||
item = database.GetItem(e);
|
item = database.GetItem(e);
|
||||||
if (item) {
|
if (item) {
|
||||||
const std::string &item_string = RuleB(World, UseItemLinksForKeyRing) ? database.CreateItemLink(e) : item->Name;
|
const std::string& item_string = (
|
||||||
|
RuleB(World, UseItemLinksForKeyRing) ?
|
||||||
|
database.CreateItemLink(e) :
|
||||||
|
item->Name
|
||||||
|
);
|
||||||
|
|
||||||
Message(Chat::LightBlue, item_string.c_str());
|
Message(Chat::LightBlue, item_string.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Client::KeyRingRemove(uint32 item_id)
|
||||||
|
{
|
||||||
|
keyring.erase(
|
||||||
|
std::remove(
|
||||||
|
keyring.begin(),
|
||||||
|
keyring.end(),
|
||||||
|
item_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return KeyringRepository::DeleteWhere(
|
||||||
|
database,
|
||||||
|
fmt::format(
|
||||||
|
"`char_id` = {} AND `item_id` = {}",
|
||||||
|
CharacterID(),
|
||||||
|
item_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::IsNameChangeAllowed() {
|
||||||
|
if (RuleB(Character, AlwaysAllowNameChange)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto k = GetScopedBucketKeys();
|
||||||
|
k.key = "name_change_allowed";
|
||||||
|
|
||||||
|
auto b = DataBucket::GetData(k);
|
||||||
|
if (!b.value.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::ClearNameChange() {
|
||||||
|
if (!IsNameChangeAllowed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto k = GetScopedBucketKeys();
|
||||||
|
k.key = "name_change_allowed";
|
||||||
|
|
||||||
|
DataBucket::DeleteData(k);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::InvokeChangeNameWindow(bool immediate) {
|
||||||
|
if (!IsNameChangeAllowed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto packet_op = immediate ? OP_InvokeNameChangeImmediate : OP_InvokeNameChangeLazy;
|
||||||
|
|
||||||
|
auto outapp = new EQApplicationPacket(packet_op, 0);
|
||||||
|
QueuePacket(outapp);
|
||||||
|
safe_delete(outapp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::GrantNameChange() {
|
||||||
|
|
||||||
|
auto k = GetScopedBucketKeys();
|
||||||
|
k.key = "name_change_allowed";
|
||||||
|
k.value = "allowed"; // potentially put a timestamp here
|
||||||
|
DataBucket::SetData(k);
|
||||||
|
|
||||||
|
InvokeChangeNameWindow(true);
|
||||||
|
}
|
||||||
|
|
||||||
bool Client::IsPetNameChangeAllowed() {
|
bool Client::IsPetNameChangeAllowed() {
|
||||||
if (RuleB(Pets, AlwaysAllowPetRename)) {
|
if (RuleB(Pets, AlwaysAllowPetRename)) {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+43
-5
@@ -327,9 +327,15 @@ public:
|
|||||||
// void TraderPriceUpdate(const EQApplicationPacket *app);
|
// void TraderPriceUpdate(const EQApplicationPacket *app);
|
||||||
uint8 WithCustomer(uint16 NewCustomer);
|
uint8 WithCustomer(uint16 NewCustomer);
|
||||||
void KeyRingLoad();
|
void KeyRingLoad();
|
||||||
void KeyRingAdd(uint32 item_id);
|
bool KeyRingAdd(uint32 item_id);
|
||||||
bool KeyRingCheck(uint32 item_id);
|
bool KeyRingCheck(uint32 item_id);
|
||||||
|
bool KeyRingClear();
|
||||||
|
bool KeyRingRemove(uint32 item_id);
|
||||||
void KeyRingList();
|
void KeyRingList();
|
||||||
|
bool IsNameChangeAllowed();
|
||||||
|
void InvokeChangeNameWindow(bool immediate = true);
|
||||||
|
bool ClearNameChange();
|
||||||
|
void GrantNameChange();
|
||||||
bool IsPetNameChangeAllowed();
|
bool IsPetNameChangeAllowed();
|
||||||
void GrantPetNameChange();
|
void GrantPetNameChange();
|
||||||
void ClearPetNameChange();
|
void ClearPetNameChange();
|
||||||
@@ -402,6 +408,7 @@ public:
|
|||||||
void LoadParcels();
|
void LoadParcels();
|
||||||
std::map<uint32, CharacterParcelsRepository::CharacterParcels> GetParcels() { return m_parcels; }
|
std::map<uint32, CharacterParcelsRepository::CharacterParcels> GetParcels() { return m_parcels; }
|
||||||
int32 FindNextFreeParcelSlot(uint32 char_id);
|
int32 FindNextFreeParcelSlot(uint32 char_id);
|
||||||
|
int32 FindNextFreeParcelSlotUsingMemory();
|
||||||
void SendParcelIconStatus();
|
void SendParcelIconStatus();
|
||||||
|
|
||||||
void SendBecomeTraderToWorld(Client *trader, BazaarTraderBarterActions action);
|
void SendBecomeTraderToWorld(Client *trader, BazaarTraderBarterActions action);
|
||||||
@@ -440,6 +447,8 @@ public:
|
|||||||
int64 ValidateBuyLineCost(std::map<uint32, BuylineItemDetails_Struct>& item_map);
|
int64 ValidateBuyLineCost(std::map<uint32, BuylineItemDetails_Struct>& item_map);
|
||||||
bool DoBarterBuyerChecks(BuyerLineSellItem_Struct& sell_line);
|
bool DoBarterBuyerChecks(BuyerLineSellItem_Struct& sell_line);
|
||||||
bool DoBarterSellerChecks(BuyerLineSellItem_Struct& sell_line);
|
bool DoBarterSellerChecks(BuyerLineSellItem_Struct& sell_line);
|
||||||
|
void CancelBuyerTradeWindow();
|
||||||
|
void CancelTraderTradeWindow();
|
||||||
|
|
||||||
void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho);
|
void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho);
|
||||||
bool ShouldISpawnFor(Client *c) { return !GMHideMe(c) && !IsHoveringForRespawn(); }
|
bool ShouldISpawnFor(Client *c) { return !GMHideMe(c) && !IsHoveringForRespawn(); }
|
||||||
@@ -506,7 +515,8 @@ public:
|
|||||||
bool AutoAttackEnabled() const { return auto_attack; }
|
bool AutoAttackEnabled() const { return auto_attack; }
|
||||||
bool AutoFireEnabled() const { return auto_fire; }
|
bool AutoFireEnabled() const { return auto_fire; }
|
||||||
|
|
||||||
bool ChangeFirstName(const char* in_firstname,const char* gmname);
|
bool ChangeFirstName(const std::string in_firstname,const std::string gmname);
|
||||||
|
bool ChangeFirstName(const std::string in_firstname);
|
||||||
|
|
||||||
void Duck();
|
void Duck();
|
||||||
void Stand();
|
void Stand();
|
||||||
@@ -1289,6 +1299,29 @@ public:
|
|||||||
void SendSpellTypePrompts(bool commanded_types = false, bool client_only_types = false);
|
void SendSpellTypePrompts(bool commanded_types = false, bool client_only_types = false);
|
||||||
|
|
||||||
// Task System Methods
|
// Task System Methods
|
||||||
|
inline void LoadClientSharedCompletedTasks()
|
||||||
|
{
|
||||||
|
std::string query = fmt::format(R"(
|
||||||
|
SELECT
|
||||||
|
cst.task_id
|
||||||
|
FROM completed_shared_task_members cstm
|
||||||
|
JOIN completed_shared_tasks cst ON cstm.shared_task_id = cst.id
|
||||||
|
WHERE cstm.character_id = {}
|
||||||
|
GROUP BY cst.task_id;
|
||||||
|
)", CharacterID());
|
||||||
|
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
|
if (!results.Success()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_completed_shared_tasks.clear();
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
m_completed_shared_tasks.push_back(std::stoi(row[0]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
inline std::vector<uint32_t> GetCompletedSharedTasks() const { return m_completed_shared_tasks; };
|
||||||
void LoadClientTaskState();
|
void LoadClientTaskState();
|
||||||
void RemoveClientTaskState();
|
void RemoveClientTaskState();
|
||||||
void SendTaskActivityComplete(int task_id, int activity_id, int task_index, TaskType task_type, int task_incomplete=1);
|
void SendTaskActivityComplete(int task_id, int activity_id, int task_index, TaskType task_type, int task_incomplete=1);
|
||||||
@@ -1459,7 +1492,10 @@ public:
|
|||||||
{
|
{
|
||||||
return (task_state ? task_state->EnabledTaskCount(task_set_id) : -1);
|
return (task_state ? task_state->EnabledTaskCount(task_set_id) : -1);
|
||||||
}
|
}
|
||||||
inline bool IsTaskCompleted(int task_id) { return (task_state ? task_state->IsTaskCompleted(task_id) : false); }
|
inline bool IsTaskCompleted(int task_id)
|
||||||
|
{
|
||||||
|
return (task_state ? task_state->IsTaskCompleted(task_id, this) : false);
|
||||||
|
}
|
||||||
inline bool AreTasksCompleted(std::vector<int> task_ids)
|
inline bool AreTasksCompleted(std::vector<int> task_ids)
|
||||||
{
|
{
|
||||||
return (task_state ? task_state->AreTasksCompleted(task_ids) : false);
|
return (task_state ? task_state->AreTasksCompleted(task_ids) : false);
|
||||||
@@ -1872,7 +1908,7 @@ public:
|
|||||||
void SendEvolvingPacket(int8 action, const CharacterEvolvingItemsRepository::CharacterEvolvingItems &item);
|
void SendEvolvingPacket(int8 action, const CharacterEvolvingItemsRepository::CharacterEvolvingItems &item);
|
||||||
void DoEvolveItemToggle(const EQApplicationPacket* app);
|
void DoEvolveItemToggle(const EQApplicationPacket* app);
|
||||||
void DoEvolveItemDisplayFinalResult(const EQApplicationPacket* app);
|
void DoEvolveItemDisplayFinalResult(const EQApplicationPacket* app);
|
||||||
bool DoEvolveCheckProgression(const EQ::ItemInstance &inst);
|
bool DoEvolveCheckProgression(EQ::ItemInstance &inst);
|
||||||
void SendEvolveXPWindowDetails(const EQApplicationPacket* app);
|
void SendEvolveXPWindowDetails(const EQApplicationPacket* app);
|
||||||
void DoEvolveTransferXP(const EQApplicationPacket* app);
|
void DoEvolveTransferXP(const EQApplicationPacket* app);
|
||||||
void SendEvolveXPTransferWindow();
|
void SendEvolveXPTransferWindow();
|
||||||
@@ -2012,7 +2048,7 @@ private:
|
|||||||
bool GuildBanker;
|
bool GuildBanker;
|
||||||
uint16 duel_target;
|
uint16 duel_target;
|
||||||
bool duelaccepted;
|
bool duelaccepted;
|
||||||
std::list<uint32> keyring;
|
std::vector<uint32> keyring;
|
||||||
bool tellsoff; // GM /toggle
|
bool tellsoff; // GM /toggle
|
||||||
bool gm_hide_me;
|
bool gm_hide_me;
|
||||||
bool LFG;
|
bool LFG;
|
||||||
@@ -2290,6 +2326,8 @@ private:
|
|||||||
bool m_has_quest_compass = false;
|
bool m_has_quest_compass = false;
|
||||||
std::vector<uint32_t> m_dynamic_zone_ids;
|
std::vector<uint32_t> m_dynamic_zone_ids;
|
||||||
|
|
||||||
|
std::vector<uint32_t> m_completed_shared_tasks;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum BotOwnerOption : size_t {
|
enum BotOwnerOption : size_t {
|
||||||
booDeathMarquee,
|
booDeathMarquee,
|
||||||
|
|||||||
+28
-26
@@ -120,48 +120,46 @@ int Client::GetBotSpawnLimit(uint8 class_id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto& zones_list = Strings::Split(RuleS(Bots, ZonesWithSpawnLimits), ",");
|
const auto& zones_list = Strings::Split(RuleS(Bots, ZonesWithSpawnLimits), ",");
|
||||||
|
|
||||||
|
if (!zones_list.empty()) {
|
||||||
|
auto it = std::find(zones_list.begin(), zones_list.end(), std::to_string(zone->GetZoneID()));
|
||||||
|
|
||||||
|
if (it != zones_list.end()) {
|
||||||
const auto& zones_limits_list = Strings::Split(RuleS(Bots, ZoneSpawnLimits), ",");
|
const auto& zones_limits_list = Strings::Split(RuleS(Bots, ZoneSpawnLimits), ",");
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (const auto& result : zones_list) {
|
if (zones_list.size() == zones_limits_list.size()) {
|
||||||
try {
|
try {
|
||||||
if (
|
auto new_limit = std::stoul(zones_limits_list[std::distance(zones_list.begin(), it)]);
|
||||||
std::stoul(result) == zone->GetZoneID() &&
|
|
||||||
std::stoul(zones_limits_list[i]) < bot_spawn_limit
|
|
||||||
) {
|
|
||||||
bot_spawn_limit = std::stoul(zones_limits_list[i]);
|
|
||||||
|
|
||||||
break;
|
if (new_limit < bot_spawn_limit) {
|
||||||
|
bot_spawn_limit = new_limit;
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LogInfo("Invalid entry in Rule Bots:ZoneSpawnLimits: [{}]", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (const std::exception& e) {
|
|
||||||
LogInfo("Invalid entry in Rule VegasScaling:SpecialScalingZones or SpecialScalingZonesVersions: [{}]", e.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& zones_forced_list = Strings::Split(RuleS(Bots, ZonesWithForcedSpawnLimits), ",");
|
const auto& zones_forced_list = Strings::Split(RuleS(Bots, ZonesWithForcedSpawnLimits), ",");
|
||||||
|
|
||||||
|
if (!zones_forced_list.empty()) {
|
||||||
|
auto it = std::find(zones_forced_list.begin(), zones_forced_list.end(), std::to_string(zone->GetZoneID()));
|
||||||
|
|
||||||
|
if (it != zones_forced_list.end()) {
|
||||||
const auto& zones_forced_limits_list = Strings::Split(RuleS(Bots, ZoneForcedSpawnLimits), ",");
|
const auto& zones_forced_limits_list = Strings::Split(RuleS(Bots, ZoneForcedSpawnLimits), ",");
|
||||||
i = 0;
|
|
||||||
|
|
||||||
for (const auto& result : zones_forced_list) {
|
if (zones_forced_list.size() == zones_forced_limits_list.size()) {
|
||||||
try {
|
try {
|
||||||
if (
|
auto new_limit = std::stoul(zones_forced_limits_list[std::distance(zones_forced_list.begin(), it)]);
|
||||||
std::stoul(result) == zone->GetZoneID() &&
|
|
||||||
std::stoul(zones_forced_limits_list[i]) != bot_spawn_limit
|
|
||||||
) {
|
|
||||||
bot_spawn_limit = std::stoul(zones_forced_limits_list[i]);
|
|
||||||
|
|
||||||
break;
|
if (new_limit != bot_spawn_limit) {
|
||||||
|
bot_spawn_limit = new_limit;
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
LogInfo("Invalid entry in Rule Bots:ZoneForcedSpawnLimits: [{}]", e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (const std::exception& e) {
|
|
||||||
LogInfo("Invalid entry in Rule VegasScaling:SpecialScalingZones or SpecialScalingZonesVersions: [{}]", e.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,6 +256,8 @@ int Client::GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting) {
|
|||||||
return GetDefaultSpellTypeMinThreshold(bot_setting);
|
return GetDefaultSpellTypeMinThreshold(bot_setting);
|
||||||
case BotSettingCategories::SpellMaxThreshold:
|
case BotSettingCategories::SpellMaxThreshold:
|
||||||
return GetDefaultSpellTypeMaxThreshold(bot_setting);
|
return GetDefaultSpellTypeMaxThreshold(bot_setting);
|
||||||
|
default:
|
||||||
|
return 0; // default return for any unsupported setting type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,6 +271,8 @@ int Client::GetBotSetting(uint8 setting_type, uint16 bot_setting) {
|
|||||||
return GetSpellTypeMinThreshold(bot_setting);
|
return GetSpellTypeMinThreshold(bot_setting);
|
||||||
case BotSettingCategories::SpellMaxThreshold:
|
case BotSettingCategories::SpellMaxThreshold:
|
||||||
return GetSpellTypeMaxThreshold(bot_setting);
|
return GetSpellTypeMaxThreshold(bot_setting);
|
||||||
|
default:
|
||||||
|
return 0; // default return for any unsupported setting type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+119
-25
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
extern WorldServer worldserver;
|
extern WorldServer worldserver;
|
||||||
extern QueryServ* QServ;
|
extern QueryServ* QServ;
|
||||||
|
const std::string SUB_TYPE_DELIMITER = ".";
|
||||||
|
|
||||||
void Client::DoEvolveItemToggle(const EQApplicationPacket *app)
|
void Client::DoEvolveItemToggle(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
@@ -70,18 +71,19 @@ void Client::SendEvolvingPacket(const int8 action, const CharacterEvolvingItemsR
|
|||||||
|
|
||||||
void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
||||||
{
|
{
|
||||||
std::vector<const EQ::ItemInstance *> queue{};
|
std::vector<EQ::ItemInstance *> queue{};
|
||||||
|
|
||||||
for (auto &[key, inst]: GetInv().GetWorn()) {
|
for (auto &[key, inst]: GetInv().GetWorn()) {
|
||||||
LogEvolveItemDetail(
|
LogEvolveItemDetail(
|
||||||
"CharacterID <green>[{}] found equipped item ID <yellow>[{}]", CharacterID(), inst->GetID());
|
"CharacterID <green>[{}] found equipped item ID <yellow>[{}]", CharacterID(), inst->GetID());
|
||||||
if (!inst->IsEvolving() || !inst->GetEvolveActivated()) {
|
if (!inst->IsEvolving() || !inst->GetEvolveActivated()) {
|
||||||
LogEvolveItemDetail(
|
LogEvolveItemDetail(
|
||||||
"CharacterID <green>[{}], item ID <yellow>[{}] not an evolving item.", CharacterID(), inst->GetID());
|
"CharacterID <green>[{}], item ID <yellow>[{}] not an evolving item.", CharacterID(), inst->GetID()
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst->GetTimers().contains("evolve") && !inst->GetTimers().at("evolve").Check()) {
|
if (inst->GetTimers().contains("evolve") && !inst->GetTimers().at("evolve").Check(false)) {
|
||||||
LogEvolveItemDetail(
|
LogEvolveItemDetail(
|
||||||
"CharacterID <green>[{}], item ID <yellow>[{}] timer not yet expired. <red>[{}] secs remaining.",
|
"CharacterID <green>[{}], item ID <yellow>[{}] timer not yet expired. <red>[{}] secs remaining.",
|
||||||
CharacterID(),
|
CharacterID(),
|
||||||
@@ -98,32 +100,40 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
CharacterID(),
|
CharacterID(),
|
||||||
inst->GetID(),
|
inst->GetID(),
|
||||||
type,
|
type,
|
||||||
sub_type);
|
sub_type
|
||||||
|
);
|
||||||
|
|
||||||
|
auto sub_types = Strings::Split(sub_type, SUB_TYPE_DELIMITER);
|
||||||
|
auto has_sub_type = [&](uint32_t type) {
|
||||||
|
return Strings::Contains(sub_types, std::to_string(type));
|
||||||
|
};
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case EvolvingItems::Types::AMOUNT_OF_EXP: {
|
case EvolvingItems::Types::AMOUNT_OF_EXP: {
|
||||||
LogEvolveItemDetail("Type <green>[{}] Processing sub_type", type);
|
LogEvolveItemDetail("Type <green>[{}] Processing sub_type", type);
|
||||||
if (sub_type == EvolvingItems::SubTypes::ALL_EXP ||
|
|
||||||
(sub_type == EvolvingItems::SubTypes::GROUP_EXP && IsGrouped())) {
|
// Determine the evolve amount based on sub_type conditions
|
||||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
int evolve_amount = 0;
|
||||||
inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfGroupExperience) / 100);
|
|
||||||
|
if (has_sub_type(EvolvingItems::SubTypes::ALL_EXP) ||
|
||||||
|
(has_sub_type(EvolvingItems::SubTypes::GROUP_EXP) && IsGrouped())) {
|
||||||
|
evolve_amount = exp * RuleR(EvolvingItems, PercentOfGroupExperience) / 100;
|
||||||
}
|
}
|
||||||
else if (
|
else if (has_sub_type(EvolvingItems::SubTypes::ALL_EXP) ||
|
||||||
sub_type == EvolvingItems::SubTypes::ALL_EXP ||
|
(has_sub_type(EvolvingItems::SubTypes::RAID_EXP) && IsRaidGrouped())) {
|
||||||
(sub_type == EvolvingItems::SubTypes::RAID_EXP && IsRaidGrouped())) {
|
evolve_amount = exp * RuleR(EvolvingItems, PercentOfRaidExperience) / 100;
|
||||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
|
||||||
inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfRaidExperience) / 100);
|
|
||||||
}
|
}
|
||||||
else if (
|
else if (has_sub_type(EvolvingItems::SubTypes::ALL_EXP) ||
|
||||||
sub_type == EvolvingItems::SubTypes::ALL_EXP || sub_type == EvolvingItems::SubTypes::SOLO_EXP) {
|
has_sub_type(EvolvingItems::SubTypes::SOLO_EXP)) {
|
||||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
evolve_amount = exp * RuleR(EvolvingItems, PercentOfSoloExperience) / 100;
|
||||||
inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfSoloExperience) / 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inst->SetEvolveAddToCurrentAmount(evolve_amount);
|
||||||
inst->CalculateEvolveProgression();
|
inst->CalculateEvolveProgression();
|
||||||
|
|
||||||
auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression(
|
auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression(
|
||||||
database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), inst->GetEvolveProgression());
|
database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), inst->GetEvolveProgression()
|
||||||
|
);
|
||||||
if (!e.id) {
|
if (!e.id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -146,7 +156,7 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
}
|
}
|
||||||
case EvolvingItems::Types::SPECIFIC_MOB_RACE: {
|
case EvolvingItems::Types::SPECIFIC_MOB_RACE: {
|
||||||
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
|
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
|
||||||
if (mob && mob->GetRace() == sub_type) {
|
if (mob && has_sub_type(mob->GetRace())) {
|
||||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
||||||
inst->SetEvolveAddToCurrentAmount(1);
|
inst->SetEvolveAddToCurrentAmount(1);
|
||||||
inst->CalculateEvolveProgression();
|
inst->CalculateEvolveProgression();
|
||||||
@@ -155,7 +165,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
database,
|
database,
|
||||||
inst->GetEvolveUniqueID(),
|
inst->GetEvolveUniqueID(),
|
||||||
inst->GetEvolveCurrentAmount(),
|
inst->GetEvolveCurrentAmount(),
|
||||||
inst->GetEvolveProgression());
|
inst->GetEvolveProgression()
|
||||||
|
);
|
||||||
if (!e.id) {
|
if (!e.id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -163,7 +174,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
|
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
|
||||||
|
|
||||||
LogEvolveItem(
|
LogEvolveItem(
|
||||||
"Processing Complete for item id <green>[{1}] Type 3 Specific Mob Race - SubType <yellow>[{0}] "
|
"Processing Complete for item id <green>[{1}] Type 3 Specific Mob Race - SubType "
|
||||||
|
"<yellow>[{0}] "
|
||||||
"- Increased count by 1 for <green>[{1}]",
|
"- Increased count by 1 for <green>[{1}]",
|
||||||
sub_type,
|
sub_type,
|
||||||
inst->GetID()
|
inst->GetID()
|
||||||
@@ -178,7 +190,7 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
}
|
}
|
||||||
case EvolvingItems::Types::SPECIFIC_ZONE_ID: {
|
case EvolvingItems::Types::SPECIFIC_ZONE_ID: {
|
||||||
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
|
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
|
||||||
if (mob && mob->GetZoneID() == sub_type) {
|
if (mob && has_sub_type(mob->GetZoneID())) {
|
||||||
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
||||||
inst->SetEvolveAddToCurrentAmount(1);
|
inst->SetEvolveAddToCurrentAmount(1);
|
||||||
inst->CalculateEvolveProgression();
|
inst->CalculateEvolveProgression();
|
||||||
@@ -187,7 +199,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
database,
|
database,
|
||||||
inst->GetEvolveUniqueID(),
|
inst->GetEvolveUniqueID(),
|
||||||
inst->GetEvolveCurrentAmount(),
|
inst->GetEvolveCurrentAmount(),
|
||||||
inst->GetEvolveProgression());
|
inst->GetEvolveProgression()
|
||||||
|
);
|
||||||
if (!e.id) {
|
if (!e.id) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -195,7 +208,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
|
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
|
||||||
|
|
||||||
LogEvolveItem(
|
LogEvolveItem(
|
||||||
"Processing Complete for item id <green>[{1}] Type 4 Specific Zone ID - SubType <yellow>[{0}] "
|
"Processing Complete for item id <green>[{1}] Type 4 Specific Zone ID - SubType "
|
||||||
|
"<yellow>[{0}] "
|
||||||
"- Increased count by 1 for <green>[{1}]",
|
"- Increased count by 1 for <green>[{1}]",
|
||||||
sub_type,
|
sub_type,
|
||||||
inst->GetID()
|
inst->GetID()
|
||||||
@@ -208,6 +222,44 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case EvolvingItems::Types::NUMBER_OF_KILLS: {
|
||||||
|
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
|
||||||
|
if (mob) {
|
||||||
|
if (mob->GetLevel() >= Strings::ToUnsignedInt(sub_types.front()) ||
|
||||||
|
Strings::ToUnsignedInt(sub_types.front()) == 0
|
||||||
|
) {
|
||||||
|
LogEvolveItemDetail("Sub_Type <green>[{}] Processing Item", sub_type);
|
||||||
|
inst->SetEvolveAddToCurrentAmount(1);
|
||||||
|
inst->CalculateEvolveProgression();
|
||||||
|
|
||||||
|
auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression(
|
||||||
|
database,
|
||||||
|
inst->GetEvolveUniqueID(),
|
||||||
|
inst->GetEvolveCurrentAmount(),
|
||||||
|
inst->GetEvolveProgression()
|
||||||
|
);
|
||||||
|
if (!e.id) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e);
|
||||||
|
|
||||||
|
LogEvolveItem(
|
||||||
|
"Processing Complete for item id <green>[{1}] Type 4 Specific Zone ID - SubType "
|
||||||
|
"<yellow>[{0}] "
|
||||||
|
"- Increased count by 1 for <green>[{1}]",
|
||||||
|
sub_type,
|
||||||
|
inst->GetID()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inst->GetEvolveProgression() >= 100) {
|
||||||
|
queue.push_back(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,7 +299,7 @@ void Client::DoEvolveItemDisplayFinalResult(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::DoEvolveCheckProgression(const EQ::ItemInstance &inst)
|
bool Client::DoEvolveCheckProgression(EQ::ItemInstance &inst)
|
||||||
{
|
{
|
||||||
if (inst.GetEvolveProgression() < 100 || inst.GetEvolveLvl() == inst.GetMaxEvolveLvl()) {
|
if (inst.GetEvolveProgression() < 100 || inst.GetEvolveLvl() == inst.GetMaxEvolveLvl()) {
|
||||||
return false;
|
return false;
|
||||||
@@ -264,6 +316,48 @@ bool Client::DoEvolveCheckProgression(const EQ::ItemInstance &inst)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RuleB(EvolvingItems, EnableParcelMerchants) &&
|
||||||
|
!RuleB(EvolvingItems, DestroyAugmentsOnEvolve) &&
|
||||||
|
inst.IsAugmented()
|
||||||
|
) {
|
||||||
|
auto const augs = inst.GetAugmentIDs();
|
||||||
|
std::vector<CharacterParcelsRepository::CharacterParcels> parcels;
|
||||||
|
for (auto const &item_id: augs) {
|
||||||
|
if (!item_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterParcelsRepository::CharacterParcels p{};
|
||||||
|
p.char_id = CharacterID();
|
||||||
|
p.from_name = "Evolving Item Sub-System";
|
||||||
|
p.note = fmt::format(
|
||||||
|
"System automatically removed from {} which recently evolved.",
|
||||||
|
inst.GetItem()->Name
|
||||||
|
);
|
||||||
|
p.slot_id = FindNextFreeParcelSlotUsingMemory();
|
||||||
|
p.sent_date = time(nullptr);
|
||||||
|
p.item_id = item_id;
|
||||||
|
p.quantity = 1;
|
||||||
|
|
||||||
|
if (player_event_logs.IsEventEnabled(PlayerEvent::PARCEL_SEND)) {
|
||||||
|
PlayerEvent::ParcelSend e{};
|
||||||
|
e.from_player_name = p.from_name;
|
||||||
|
e.to_player_name = GetCleanName();
|
||||||
|
e.item_id = p.item_id;
|
||||||
|
e.quantity = 1;
|
||||||
|
e.sent_date = p.sent_date;
|
||||||
|
|
||||||
|
RecordPlayerEventLog(PlayerEvent::PARCEL_SEND, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
parcels.push_back(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
CharacterParcelsRepository::InsertMany(database, parcels);
|
||||||
|
SendParcelStatus();
|
||||||
|
SendParcelIconStatus();
|
||||||
|
}
|
||||||
|
|
||||||
CheckItemDiscoverability(new_inst->GetID());
|
CheckItemDiscoverability(new_inst->GetID());
|
||||||
|
|
||||||
PlayerEvent::EvolveItem e{};
|
PlayerEvent::EvolveItem e{};
|
||||||
|
|||||||
+43
-15
@@ -73,6 +73,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "../common/repositories/character_stats_record_repository.h"
|
#include "../common/repositories/character_stats_record_repository.h"
|
||||||
#include "dialogue_window.h"
|
#include "dialogue_window.h"
|
||||||
#include "../common/rulesys.h"
|
#include "../common/rulesys.h"
|
||||||
|
#include "../common/repositories/adventure_members_repository.h"
|
||||||
|
|
||||||
extern QueryServ* QServ;
|
extern QueryServ* QServ;
|
||||||
extern Zone* zone;
|
extern Zone* zone;
|
||||||
@@ -826,6 +827,10 @@ void Client::CompleteConnect()
|
|||||||
if (IsPetNameChangeAllowed() && !RuleB(Pets, AlwaysAllowPetRename)) {
|
if (IsPetNameChangeAllowed() && !RuleB(Pets, AlwaysAllowPetRename)) {
|
||||||
InvokeChangePetName(false);
|
InvokeChangePetName(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsNameChangeAllowed() && !RuleB(Character, AlwaysAllowNameChange)) {
|
||||||
|
InvokeChangeNameWindow(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
|
if(ClientVersion() == EQ::versions::ClientVersion::RoF2 && RuleB(Parcel, EnableParcelMerchants)) {
|
||||||
@@ -909,11 +914,14 @@ void Client::CompleteConnect()
|
|||||||
|
|
||||||
SendDynamicZoneUpdates();
|
SendDynamicZoneUpdates();
|
||||||
|
|
||||||
/** Request adventure info **/
|
// Request adventure info
|
||||||
|
auto members = AdventureMembersRepository::GetWhere(database, fmt::format("charid = {}", CharacterID()));
|
||||||
|
if (!members.empty()) {
|
||||||
auto pack = new ServerPacket(ServerOP_AdventureDataRequest, 64);
|
auto pack = new ServerPacket(ServerOP_AdventureDataRequest, 64);
|
||||||
strcpy((char*)pack->pBuffer, GetName());
|
strcpy((char*)pack->pBuffer, GetName());
|
||||||
worldserver.SendPacket(pack);
|
worldserver.SendPacket(pack);
|
||||||
delete pack;
|
delete pack;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskUFAndLater) {
|
if (IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskUFAndLater) {
|
||||||
EQApplicationPacket *outapp = MakeBuffsPacket(false);
|
EQApplicationPacket *outapp = MakeBuffsPacket(false);
|
||||||
@@ -4548,14 +4556,14 @@ void Client::Handle_OP_ChangePetName(const EQApplicationPacket *app) {
|
|||||||
|
|
||||||
auto p = (ChangePetName_Struct *) app->pBuffer;
|
auto p = (ChangePetName_Struct *) app->pBuffer;
|
||||||
if (!IsPetNameChangeAllowed()) {
|
if (!IsPetNameChangeAllowed()) {
|
||||||
p->response_code = ChangePetNameResponse::NotEligible;
|
p->response_code = ChangeNameResponse::Ineligible;
|
||||||
QueuePacket(app);
|
QueuePacket(app);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
p->response_code = ChangePetNameResponse::Denied;
|
p->response_code = ChangeNameResponse::Denied;
|
||||||
if (ChangePetName(p->new_pet_name)) {
|
if (ChangePetName(p->new_pet_name)) {
|
||||||
p->response_code = ChangePetNameResponse::Accepted;
|
p->response_code = ChangeNameResponse::Accepted;
|
||||||
}
|
}
|
||||||
|
|
||||||
QueuePacket(app);
|
QueuePacket(app);
|
||||||
@@ -6776,6 +6784,21 @@ void Client::Handle_OP_GMLastName(const EQApplicationPacket *app)
|
|||||||
|
|
||||||
void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app)
|
void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
|
if (app->size == sizeof(AltChangeName_Struct)) {
|
||||||
|
auto p = (AltChangeName_Struct *) app->pBuffer;
|
||||||
|
|
||||||
|
if (!IsNameChangeAllowed()) {
|
||||||
|
p->response_code = ChangeNameResponse::Ineligible;
|
||||||
|
QueuePacket(app);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->response_code = ChangeFirstName(p->new_name) ? ChangeNameResponse::Accepted : ChangeNameResponse::Denied;
|
||||||
|
QueuePacket(app);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (app->size != sizeof(GMName_Struct)) {
|
if (app->size != sizeof(GMName_Struct)) {
|
||||||
LogError("Wrong size: OP_GMNameChange, size=[{}], expected [{}]", app->size, sizeof(GMName_Struct));
|
LogError("Wrong size: OP_GMNameChange, size=[{}], expected [{}]", app->size, sizeof(GMName_Struct));
|
||||||
return;
|
return;
|
||||||
@@ -7653,7 +7676,11 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app)
|
|||||||
log.char_id = CharacterID();
|
log.char_id = CharacterID();
|
||||||
log.guild_id = GuildID();
|
log.guild_id = GuildID();
|
||||||
log.item_id = inst->GetID();
|
log.item_id = inst->GetID();
|
||||||
|
log.quantity = 1;
|
||||||
|
if (inst->GetCharges() > 0 || inst->IsStackable() || inst->GetItem()->MaxCharges > 0) {
|
||||||
log.quantity = inst->GetCharges();
|
log.quantity = inst->GetCharges();
|
||||||
|
}
|
||||||
|
|
||||||
if (inst->IsAugmented()) {
|
if (inst->IsAugmented()) {
|
||||||
auto augs = inst->GetAugmentIDs();
|
auto augs = inst->GetAugmentIDs();
|
||||||
log.aug_slot_one = augs.at(0);
|
log.aug_slot_one = augs.at(0);
|
||||||
@@ -7737,7 +7764,11 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app)
|
|||||||
item.guild_id = GuildID();
|
item.guild_id = GuildID();
|
||||||
item.area = GuildBankDepositArea;
|
item.area = GuildBankDepositArea;
|
||||||
item.item_id = cursor_item->ID;
|
item.item_id = cursor_item->ID;
|
||||||
|
item.quantity = 1;
|
||||||
|
if (cursor_item_inst->GetCharges() > 0 || cursor_item_inst->IsStackable() || cursor_item->MaxCharges > 0) {
|
||||||
item.quantity = cursor_item_inst->GetCharges();
|
item.quantity = cursor_item_inst->GetCharges();
|
||||||
|
}
|
||||||
|
|
||||||
item.donator = GetCleanName();
|
item.donator = GetCleanName();
|
||||||
item.permissions = GuildBankBankerOnly;
|
item.permissions = GuildBankBankerOnly;
|
||||||
if (cursor_item_inst->IsAugmented()) {
|
if (cursor_item_inst->IsAugmented()) {
|
||||||
@@ -7821,12 +7852,9 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst->GetCharges() > 0) {
|
|
||||||
gbwis->Quantity = inst->GetCharges();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inst->GetCharges() < 0) {
|
|
||||||
gbwis->Quantity = 1;
|
gbwis->Quantity = 1;
|
||||||
|
if (inst->GetCharges() > 0 || inst->IsStackable() || inst->GetItem()->MaxCharges > 0) {
|
||||||
|
gbwis->Quantity = inst->GetCharges();
|
||||||
}
|
}
|
||||||
|
|
||||||
PushItemOnCursor(*inst.get());
|
PushItemOnCursor(*inst.get());
|
||||||
@@ -7956,7 +7984,7 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetGuildID(new_guild_id);
|
SetGuildID(new_guild_id);
|
||||||
SendGuildList();
|
UpdateWho();
|
||||||
guild_mgr.MemberAdd(new_guild_id, CharacterID(), GetLevel(), GetClass(), GUILD_LEADER, GetZoneID(), GetName());
|
guild_mgr.MemberAdd(new_guild_id, CharacterID(), GetLevel(), GetClass(), GUILD_LEADER, GetZoneID(), GetName());
|
||||||
guild_mgr.SendGuildRefresh(new_guild_id, true, true, true, true);
|
guild_mgr.SendGuildRefresh(new_guild_id, true, true, true, true);
|
||||||
guild_mgr.SendToWorldSendGuildList();
|
guild_mgr.SendToWorldSendGuildList();
|
||||||
@@ -8125,7 +8153,7 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app)
|
|||||||
if (!invitee) {
|
if (!invitee) {
|
||||||
Message(
|
Message(
|
||||||
Chat::Red,
|
Chat::Red,
|
||||||
"Prospective guild member %s must be in zone to preform guild operations on them.",
|
"Prospective guild member %s must be in zone to perform guild operations on them.",
|
||||||
gc->othername
|
gc->othername
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@@ -11038,7 +11066,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
|
|||||||
if (!target)
|
if (!target)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (RuleB(Map, CheckForLoSCheat) && (!DoLosChecks(target) || !CheckLosCheat(target))) {
|
if (RuleB(Pets, PetsRequireLoS) && !DoLosChecks(target)) {
|
||||||
mypet->SayString(this, NOT_LEGAL_TARGET);
|
mypet->SayString(this, NOT_LEGAL_TARGET);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -11106,7 +11134,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RuleB(Map, CheckForLoSCheat) && (!DoLosChecks(GetTarget()) || !CheckLosCheat(GetTarget()))) {
|
if (RuleB(Pets, PetsRequireLoS) && !DoLosChecks(GetTarget())) {
|
||||||
mypet->SayString(this, NOT_LEGAL_TARGET);
|
mypet->SayString(this, NOT_LEGAL_TARGET);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -15458,7 +15486,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
|||||||
);
|
);
|
||||||
Message(
|
Message(
|
||||||
Chat::Yellow,
|
Chat::Yellow,
|
||||||
"Direct inventory delivey is not yet implemented. Please visit the vendor directly or purchase via parcel delivery."
|
"Direct inventory delivery is not yet implemented. Please visit the vendor directly or purchase via parcel delivery."
|
||||||
);
|
);
|
||||||
in->method = BazaarByDirectToInventory;
|
in->method = BazaarByDirectToInventory;
|
||||||
in->sub_action = Failed;
|
in->sub_action = Failed;
|
||||||
@@ -16974,7 +17002,7 @@ void Client::Handle_OP_GuildTributeDonateItem(const EQApplicationPacket *app)
|
|||||||
|
|
||||||
SendGuildTributeDonateItemReply(in, favor);
|
SendGuildTributeDonateItemReply(in, favor);
|
||||||
|
|
||||||
if(player_event_logs.IsEventEnabled(PlayerEvent::GUILD_TRIBUTE_DONATE_ITEM)) {
|
if(inst && player_event_logs.IsEventEnabled(PlayerEvent::GUILD_TRIBUTE_DONATE_ITEM)) {
|
||||||
auto e = PlayerEvent::GuildTributeDonateItem{ .item_id = inst->GetID(),
|
auto e = PlayerEvent::GuildTributeDonateItem{ .item_id = inst->GetID(),
|
||||||
.augment_1_id = inst->GetAugmentItemID(0),
|
.augment_1_id = inst->GetAugmentItemID(0),
|
||||||
.augment_2_id = inst->GetAugmentItemID(1),
|
.augment_2_id = inst->GetAugmentItemID(1),
|
||||||
|
|||||||
+1
-1
@@ -245,7 +245,7 @@ int command_init(void)
|
|||||||
command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", AccountStatus::GMLeadAdmin, command_zonebootup) ||
|
command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", AccountStatus::GMLeadAdmin, command_zonebootup) ||
|
||||||
command_add("zoneinstance", "[Instance ID] [X] [Y] [Z] - Teleport to specified Instance by ID (coordinates are optional)", AccountStatus::Guide, command_zone_instance) ||
|
command_add("zoneinstance", "[Instance ID] [X] [Y] [Z] - Teleport to specified Instance by ID (coordinates are optional)", AccountStatus::Guide, command_zone_instance) ||
|
||||||
command_add("zoneshard", "[zone] [instance_id] - Teleport explicitly to a zone shard", AccountStatus::Player, command_zone_shard) ||
|
command_add("zoneshard", "[zone] [instance_id] - Teleport explicitly to a zone shard", AccountStatus::Player, command_zone_shard) ||
|
||||||
command_add("zoneshutdown", "[shortname] - Shut down a zone server", AccountStatus::GMLeadAdmin, command_zoneshutdown) ||
|
command_add("zoneshutdown", "[instance|zone] [Instance ID|Zone ID|Zone Short Name] - Shut down a zone server by Instance ID, Zone ID, or Zone Short Name", AccountStatus::GMLeadAdmin, command_zoneshutdown) ||
|
||||||
command_add("zsave", " Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave)
|
command_add("zsave", " Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave)
|
||||||
) {
|
) {
|
||||||
command_deinit();
|
command_deinit();
|
||||||
|
|||||||
@@ -111,7 +111,6 @@ void command_ipban(Client *c, const Seperator *sep);
|
|||||||
void command_kick(Client *c, const Seperator *sep);
|
void command_kick(Client *c, const Seperator *sep);
|
||||||
void command_killallnpcs(Client *c, const Seperator *sep);
|
void command_killallnpcs(Client *c, const Seperator *sep);
|
||||||
void command_kill(Client *c, const Seperator *sep);
|
void command_kill(Client *c, const Seperator *sep);
|
||||||
void command_level(Client *c, const Seperator *sep);
|
|
||||||
void command_list(Client *c, const Seperator *sep);
|
void command_list(Client *c, const Seperator *sep);
|
||||||
void command_lootsim(Client *c, const Seperator *sep);
|
void command_lootsim(Client *c, const Seperator *sep);
|
||||||
void command_load_shared_memory(Client *c, const Seperator *sep);
|
void command_load_shared_memory(Client *c, const Seperator *sep);
|
||||||
@@ -139,7 +138,6 @@ void command_nudge(Client *c, const Seperator *sep);
|
|||||||
void command_nukebuffs(Client *c, const Seperator *sep);
|
void command_nukebuffs(Client *c, const Seperator *sep);
|
||||||
void command_nukeitem(Client *c, const Seperator *sep);
|
void command_nukeitem(Client *c, const Seperator *sep);
|
||||||
void command_object(Client *c, const Seperator *sep);
|
void command_object(Client *c, const Seperator *sep);
|
||||||
void command_oocmute(Client *c, const Seperator *sep);
|
|
||||||
void command_parcels(Client *c, const Seperator *sep);
|
void command_parcels(Client *c, const Seperator *sep);
|
||||||
void command_path(Client *c, const Seperator *sep);
|
void command_path(Client *c, const Seperator *sep);
|
||||||
void command_peqzone(Client *c, const Seperator *sep);
|
void command_peqzone(Client *c, const Seperator *sep);
|
||||||
@@ -147,7 +145,6 @@ void command_petitems(Client *c, const Seperator *sep);
|
|||||||
void command_picklock(Client *c, const Seperator *sep);
|
void command_picklock(Client *c, const Seperator *sep);
|
||||||
void command_profanity(Client *c, const Seperator *sep);
|
void command_profanity(Client *c, const Seperator *sep);
|
||||||
void command_push(Client *c, const Seperator *sep);
|
void command_push(Client *c, const Seperator *sep);
|
||||||
void command_pvp(Client *c, const Seperator *sep);
|
|
||||||
void command_raidloot(Client* c, const Seperator* sep);
|
void command_raidloot(Client* c, const Seperator* sep);
|
||||||
void command_randomfeatures(Client *c, const Seperator *sep);
|
void command_randomfeatures(Client *c, const Seperator *sep);
|
||||||
void command_refreshgroup(Client *c, const Seperator *sep);
|
void command_refreshgroup(Client *c, const Seperator *sep);
|
||||||
@@ -201,8 +198,6 @@ void command_zone_instance(Client *c, const Seperator *sep);
|
|||||||
void command_zone_shard(Client *c, const Seperator *sep);
|
void command_zone_shard(Client *c, const Seperator *sep);
|
||||||
void command_zonebootup(Client *c, const Seperator *sep);
|
void command_zonebootup(Client *c, const Seperator *sep);
|
||||||
void command_zoneshutdown(Client *c, const Seperator *sep);
|
void command_zoneshutdown(Client *c, const Seperator *sep);
|
||||||
void command_zopp(Client *c, const Seperator *sep);
|
|
||||||
void command_zsafecoords(Client *c, const Seperator *sep);
|
|
||||||
void command_zsave(Client *c, const Seperator *sep);
|
void command_zsave(Client *c, const Seperator *sep);
|
||||||
|
|
||||||
#include "bot.h"
|
#include "bot.h"
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include "../common/types.h"
|
#include "../common/types.h"
|
||||||
#include "../common/spdat.h"
|
#include "../common/spdat.h"
|
||||||
|
|
||||||
|
#include <cereal/cereal.hpp>
|
||||||
|
|
||||||
#define HIGHEST_RESIST 9 //Max resist type value
|
#define HIGHEST_RESIST 9 //Max resist type value
|
||||||
#define MAX_SPELL_PROJECTILE 10 //Max amount of spell projectiles that can be active by a single mob.
|
#define MAX_SPELL_PROJECTILE 10 //Max amount of spell projectiles that can be active by a single mob.
|
||||||
|
|
||||||
@@ -272,6 +274,46 @@ struct Buffs_Struct {
|
|||||||
bool persistant_buff;
|
bool persistant_buff;
|
||||||
bool client; //True if the caster is a client
|
bool client; //True if the caster is a client
|
||||||
bool UpdateClient;
|
bool UpdateClient;
|
||||||
|
|
||||||
|
// cereal
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive &ar)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string caster_name_str(caster_name);
|
||||||
|
if (Archive::is_saving::value) {
|
||||||
|
caster_name_str = std::string(caster_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ar(
|
||||||
|
CEREAL_NVP(spellid),
|
||||||
|
CEREAL_NVP(casterlevel),
|
||||||
|
CEREAL_NVP(casterid),
|
||||||
|
CEREAL_NVP(caster_name_str),
|
||||||
|
CEREAL_NVP(ticsremaining),
|
||||||
|
CEREAL_NVP(counters),
|
||||||
|
CEREAL_NVP(hit_number),
|
||||||
|
CEREAL_NVP(melee_rune),
|
||||||
|
CEREAL_NVP(magic_rune),
|
||||||
|
CEREAL_NVP(dot_rune),
|
||||||
|
CEREAL_NVP(caston_x),
|
||||||
|
CEREAL_NVP(caston_y),
|
||||||
|
CEREAL_NVP(caston_z),
|
||||||
|
CEREAL_NVP(ExtraDIChance),
|
||||||
|
CEREAL_NVP(RootBreakChance),
|
||||||
|
CEREAL_NVP(instrument_mod),
|
||||||
|
CEREAL_NVP(virus_spread_time),
|
||||||
|
CEREAL_NVP(persistant_buff),
|
||||||
|
CEREAL_NVP(client),
|
||||||
|
CEREAL_NVP(UpdateClient)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Copy back into caster_name after deserialization
|
||||||
|
if (Archive::is_loading::value) {
|
||||||
|
strncpy(caster_name, caster_name_str.c_str(), sizeof(caster_name));
|
||||||
|
caster_name[sizeof(caster_name) - 1] = '\0'; // Ensure null termination
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StatBonuses {
|
struct StatBonuses {
|
||||||
|
|||||||
+3
-3
@@ -290,7 +290,7 @@ Corpse::Corpse(Client *c, int32 rez_exp, KilledByTypes in_killed_by) : Mob(
|
|||||||
m_corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS));
|
m_corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS));
|
||||||
m_loot_cooldown_timer.SetTimer(10);
|
m_loot_cooldown_timer.SetTimer(10);
|
||||||
m_check_rezzable_timer.SetTimer(1000);
|
m_check_rezzable_timer.SetTimer(1000);
|
||||||
m_check_owner_online_timer.SetTimer(RuleI(Character, CorpseOwnerOnlineTime));
|
m_check_owner_online_timer.SetTimer(RuleI(Character, CorpseOwnerOnlineCheckTime) * 1000);
|
||||||
|
|
||||||
m_corpse_rezzable_timer.Disable();
|
m_corpse_rezzable_timer.Disable();
|
||||||
SetRezTimer(true);
|
SetRezTimer(true);
|
||||||
@@ -583,7 +583,7 @@ Corpse::Corpse(
|
|||||||
m_corpse_delay_timer.SetTimer(RuleI(NPC, CorpseUnlockTimer));
|
m_corpse_delay_timer.SetTimer(RuleI(NPC, CorpseUnlockTimer));
|
||||||
m_corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS));
|
m_corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS));
|
||||||
m_loot_cooldown_timer.SetTimer(10);
|
m_loot_cooldown_timer.SetTimer(10);
|
||||||
m_check_owner_online_timer.SetTimer(RuleI(Character, CorpseOwnerOnlineTime));
|
m_check_owner_online_timer.SetTimer(RuleI(Character, CorpseOwnerOnlineCheckTime) * 1000);
|
||||||
m_check_rezzable_timer.SetTimer(1000);
|
m_check_rezzable_timer.SetTimer(1000);
|
||||||
m_corpse_rezzable_timer.Disable();
|
m_corpse_rezzable_timer.Disable();
|
||||||
|
|
||||||
@@ -1569,7 +1569,7 @@ void Corpse::LootCorpseItem(Client *c, const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player_event_logs.IsEventEnabled(PlayerEvent::LOOT_ITEM) && !IsPlayerCorpse()) {
|
if (inst && player_event_logs.IsEventEnabled(PlayerEvent::LOOT_ITEM) && !IsPlayerCorpse()) {
|
||||||
auto e = PlayerEvent::LootItemEvent{
|
auto e = PlayerEvent::LootItemEvent{
|
||||||
.item_id = inst->GetItem()->ID,
|
.item_id = inst->GetItem()->ID,
|
||||||
.item_name = inst->GetItem()->Name,
|
.item_name = inst->GetItem()->Name,
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ public:
|
|||||||
uint32 GetItemIDBySlot(uint16 loot_slot);
|
uint32 GetItemIDBySlot(uint16 loot_slot);
|
||||||
uint16 GetFirstLootSlotByItemID(uint32 item_id);
|
uint16 GetFirstLootSlotByItemID(uint32 item_id);
|
||||||
std::vector<int> GetLootList();
|
std::vector<int> GetLootList();
|
||||||
|
inline const LootItems &GetLootItems() { return m_item_list; }
|
||||||
void LootCorpseItem(Client *c, const EQApplicationPacket *app);
|
void LootCorpseItem(Client *c, const EQApplicationPacket *app);
|
||||||
void EndLoot(Client *c, const EQApplicationPacket *app);
|
void EndLoot(Client *c, const EQApplicationPacket *app);
|
||||||
void MakeLootRequestPackets(Client *c, const EQApplicationPacket *app);
|
void MakeLootRequestPackets(Client *c, const EQApplicationPacket *app);
|
||||||
|
|||||||
+127
-23
@@ -27,7 +27,8 @@ void DataBucket::SetData(const std::string &bucket_key, const std::string &bucke
|
|||||||
void DataBucket::SetData(const DataBucketKey &k_)
|
void DataBucket::SetData(const DataBucketKey &k_)
|
||||||
{
|
{
|
||||||
DataBucketKey k = k_; // copy the key so we can modify it
|
DataBucketKey k = k_; // copy the key so we can modify it
|
||||||
if (k.key.find(NESTED_KEY_DELIMITER) != std::string::npos) {
|
bool is_nested = k.key.find(NESTED_KEY_DELIMITER) != std::string::npos;
|
||||||
|
if (is_nested) {
|
||||||
k.key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
|
k.key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +64,10 @@ void DataBucket::SetData(const DataBucketKey &k_)
|
|||||||
if (isalpha(k.expires[0]) || isalpha(k.expires[k.expires.length() - 1])) {
|
if (isalpha(k.expires[0]) || isalpha(k.expires[k.expires.length() - 1])) {
|
||||||
expires_time_unix = static_cast<int64>(std::time(nullptr)) + Strings::TimeToSeconds(k.expires);
|
expires_time_unix = static_cast<int64>(std::time(nullptr)) + Strings::TimeToSeconds(k.expires);
|
||||||
}
|
}
|
||||||
|
if (is_nested) {
|
||||||
|
LogDataBuckets("Nested keys can't expire; set expiration on the parent key");
|
||||||
|
expires_time_unix = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b.expires = expires_time_unix;
|
b.expires = expires_time_unix;
|
||||||
@@ -75,26 +80,45 @@ void DataBucket::SetData(const DataBucketKey &k_)
|
|||||||
std::string existing_value = r.id > 0 ? r.value : "{}";
|
std::string existing_value = r.id > 0 ? r.value : "{}";
|
||||||
json json_value = json::object();
|
json json_value = json::object();
|
||||||
|
|
||||||
|
// Check if the JSON is valid
|
||||||
|
if (Strings::IsValidJson(existing_value)) {
|
||||||
try {
|
try {
|
||||||
json_value = json::parse(existing_value);
|
json_value = json::parse(existing_value);
|
||||||
} catch (json::parse_error &e) {
|
} catch (json::parse_error &e) {
|
||||||
LogError("Failed to parse JSON for key [{}]: {}", k_.key, e.what());
|
LogDataBuckets("Failed to parse JSON for key [{}] [{}]", k_.key, e.what());
|
||||||
json_value = json::object(); // Reset to an empty object on error
|
json_value = json::object(); // Reset to an empty object on error
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Recursively merge new key-value pair into the JSON object
|
// Recursively merge new key-value pair into the JSON object
|
||||||
auto nested_keys = Strings::Split(k_.key, NESTED_KEY_DELIMITER);
|
auto nested_keys = Strings::Split(k_.key, NESTED_KEY_DELIMITER);
|
||||||
|
auto top_key = nested_keys.front();
|
||||||
|
// remove the top-level key
|
||||||
|
nested_keys.erase(nested_keys.begin());
|
||||||
|
|
||||||
json *current = &json_value;
|
json *current = &json_value;
|
||||||
|
|
||||||
for (size_t i = 0; i < nested_keys.size(); ++i) {
|
for (size_t i = 0; i < nested_keys.size(); ++i) {
|
||||||
const std::string &key_part = nested_keys[i];
|
const std::string &key_part = nested_keys[i];
|
||||||
|
|
||||||
if (i == nested_keys.size() - 1) {
|
if (i == nested_keys.size() - 1) {
|
||||||
|
|
||||||
|
LogDataBucketsDetail("Setting key [{}] key_part [{}]", k.key, key_part);
|
||||||
|
|
||||||
|
// If the key already exists and is an object or array, prevent overwriting to avoid data loss
|
||||||
|
if (current->contains(key_part) &&
|
||||||
|
((*current)[key_part].is_object() || (*current)[key_part].is_array())) {
|
||||||
|
LogDataBuckets("Attempted to overwrite an existing object or array at key [{}] - skipping", k_.key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Set the value at the final key
|
// Set the value at the final key
|
||||||
(*current)[key_part] = k_.value;
|
(*current)[key_part] = k_.value;
|
||||||
} else {
|
} else {
|
||||||
// Traverse or create nested objects
|
// Traverse or create nested objects
|
||||||
if (!current->contains(key_part)) {
|
if (!current->contains(key_part)) {
|
||||||
(*current)[key_part] = json::object();
|
(*current)[key_part] = json::object();
|
||||||
|
LogDataBucketsDetail("Creating nested root key [{}] key_part [{}]", k.key, key_part);
|
||||||
} else if (!(*current)[key_part].is_object()) {
|
} else if (!(*current)[key_part].is_object()) {
|
||||||
// If key exists but is not an object, reset to object to avoid conflicts
|
// If key exists but is not an object, reset to object to avoid conflicts
|
||||||
(*current)[key_part] = json::object();
|
(*current)[key_part] = json::object();
|
||||||
@@ -105,7 +129,7 @@ void DataBucket::SetData(const DataBucketKey &k_)
|
|||||||
|
|
||||||
// Serialize JSON back to string
|
// Serialize JSON back to string
|
||||||
b.value = json_value.dump();
|
b.value = json_value.dump();
|
||||||
b.key_ = nested_keys.front(); // Use the top-level key
|
b.key_ = top_key; // Use the top-level key
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bucket_id) {
|
if (bucket_id) {
|
||||||
@@ -142,12 +166,20 @@ DataBucketsRepository::DataBuckets DataBucket::ExtractNestedValue(
|
|||||||
const std::string &full_key)
|
const std::string &full_key)
|
||||||
{
|
{
|
||||||
auto nested_keys = Strings::Split(full_key, NESTED_KEY_DELIMITER);
|
auto nested_keys = Strings::Split(full_key, NESTED_KEY_DELIMITER);
|
||||||
|
auto top_key = nested_keys.front();
|
||||||
|
nested_keys.erase(nested_keys.begin());
|
||||||
json json_value;
|
json json_value;
|
||||||
|
|
||||||
|
// Check if the JSON is valid
|
||||||
|
if (!Strings::IsValidJson(bucket.value)) {
|
||||||
|
LogDataBuckets("Invalid JSON for key [{}]", bucket.key_);
|
||||||
|
return DataBucketsRepository::NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
json_value = json::parse(bucket.value); // Parse the JSON
|
json_value = json::parse(bucket.value); // Parse the JSON
|
||||||
} catch (json::parse_error &ex) {
|
} catch (json::parse_error &ex) {
|
||||||
LogError("Failed to parse JSON for key [{}]: {}", bucket.key_, ex.what());
|
LogDataBuckets("Failed to parse JSON for key [{}] [{}]", bucket.key_, ex.what());
|
||||||
return DataBucketsRepository::NewEntity(); // Return empty entity on parse error
|
return DataBucketsRepository::NewEntity(); // Return empty entity on parse error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,9 +368,11 @@ bool DataBucket::GetDataBuckets(Mob *mob)
|
|||||||
|
|
||||||
bool DataBucket::DeleteData(const DataBucketKey &k)
|
bool DataBucket::DeleteData(const DataBucketKey &k)
|
||||||
{
|
{
|
||||||
if (CanCache(k)) {
|
bool is_nested_key = k.key.find(NESTED_KEY_DELIMITER) != std::string::npos;
|
||||||
size_t size_before = g_data_bucket_cache.size();
|
|
||||||
|
|
||||||
|
if (!is_nested_key) {
|
||||||
|
// Update cache
|
||||||
|
if (CanCache(k)) {
|
||||||
// delete from cache where contents match
|
// delete from cache where contents match
|
||||||
g_data_bucket_cache.erase(
|
g_data_bucket_cache.erase(
|
||||||
std::remove_if(
|
std::remove_if(
|
||||||
@@ -350,30 +384,100 @@ bool DataBucket::DeleteData(const DataBucketKey &k)
|
|||||||
),
|
),
|
||||||
g_data_bucket_cache.end()
|
g_data_bucket_cache.end()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
LogDataBuckets(
|
// Regular key deletion, no nesting involved
|
||||||
"Deleting bucket key [{}] bot_id [{}] account_id [{}] character_id [{}] npc_id [{}] bot_id [{}] zone_id [{}] instance_id [{}] cache size before [{}] after [{}]",
|
return DataBucketsRepository::DeleteWhere(
|
||||||
k.key,
|
database,
|
||||||
k.bot_id,
|
fmt::format("{} `key` = '{}'", DataBucket::GetScopedDbFilters(k), k.key)
|
||||||
k.account_id,
|
);
|
||||||
k.character_id,
|
}
|
||||||
k.npc_id,
|
|
||||||
k.bot_id,
|
// If it's a nested key, retrieve the top-level JSON object
|
||||||
k.zone_id,
|
auto top_level_key = Strings::Split(k.key, NESTED_KEY_DELIMITER).front();
|
||||||
k.instance_id,
|
DataBucketKey top_level_k = k;
|
||||||
size_before,
|
top_level_k.key = top_level_key;
|
||||||
g_data_bucket_cache.size()
|
|
||||||
|
auto r = GetData(top_level_k);
|
||||||
|
if (r.id == 0 || r.value.empty() || !Strings::IsValidJson(r.value)) {
|
||||||
|
LogDataBuckets("Attempted to delete nested key [{}] but parent key [{}] does not exist or is invalid JSON", k.key, top_level_key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
json json_value;
|
||||||
|
try {
|
||||||
|
json_value = json::parse(r.value);
|
||||||
|
} catch (json::parse_error &ex) {
|
||||||
|
LogDataBuckets("Failed to parse JSON for key [{}] [{}]", top_level_key, ex.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively remove the nested key
|
||||||
|
auto nested_keys = Strings::Split(k.key, NESTED_KEY_DELIMITER);
|
||||||
|
auto top_key = nested_keys.front();
|
||||||
|
nested_keys.erase(nested_keys.begin());
|
||||||
|
json *current = &json_value;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < nested_keys.size(); ++i) {
|
||||||
|
const std::string &key_part = nested_keys[i];
|
||||||
|
|
||||||
|
if (i == nested_keys.size() - 1) {
|
||||||
|
// Last key in the hierarchy - delete it
|
||||||
|
if (current->contains(key_part)) {
|
||||||
|
current->erase(key_part);
|
||||||
|
LogDataBuckets("Deleted nested key [{}] from [{}]", key_part, k.key);
|
||||||
|
} else {
|
||||||
|
LogDataBuckets("Key [{}] not found in JSON - nothing to delete", k.key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!current->contains(key_part) || !(*current)[key_part].is_object()) {
|
||||||
|
LogDataBuckets("Parent key [{}] does not exist or is not an object", key_part);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
current = &(*current)[key_part];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the JSON object is now empty, delete the top-level key
|
||||||
|
if (json_value.empty()) {
|
||||||
|
LogDataBuckets("Top-level key [{}] is now empty, deleting entire entry", top_level_key);
|
||||||
|
|
||||||
|
// delete cache
|
||||||
|
if (CanCache(k)) {
|
||||||
|
g_data_bucket_cache.erase(
|
||||||
|
std::remove_if(
|
||||||
|
g_data_bucket_cache.begin(),
|
||||||
|
g_data_bucket_cache.end(),
|
||||||
|
[&](DataBucketsRepository::DataBuckets &e) {
|
||||||
|
return CheckBucketMatch(e, top_level_k);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
g_data_bucket_cache.end()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return DataBucketsRepository::DeleteWhere(
|
return DataBucketsRepository::DeleteWhere(
|
||||||
database,
|
database,
|
||||||
fmt::format(
|
fmt::format("{} `key` = '{}'", DataBucket::GetScopedDbFilters(k), top_level_key)
|
||||||
"{} `key` = '{}'",
|
|
||||||
DataBucket::GetScopedDbFilters(k),
|
|
||||||
k.key
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, update the existing JSON without the deleted key
|
||||||
|
r.value = json_value.dump();
|
||||||
|
DataBucketsRepository::UpdateOne(database, r);
|
||||||
|
|
||||||
|
// Update cache
|
||||||
|
if (CanCache(k)) {
|
||||||
|
for (auto &e : g_data_bucket_cache) {
|
||||||
|
if (CheckBucketMatch(e, top_level_k)) {
|
||||||
|
e.value = r.value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DataBucket::GetDataExpires(const DataBucketKey &k)
|
std::string DataBucket::GetDataExpires(const DataBucketKey &k)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user