Compare commits

...

34 Commits

Author SHA1 Message Date
Chris Miles 3e4767269e [Release] 22.4.1 (#2950) 2023-02-17 14:48:46 -06:00
Aeadoin e0eb145081 [Bots] Set Taunt to enabled for SK/Paladin Bots by Default. (#2941) 2023-02-17 10:46:49 -05:00
Chris Miles a6dd65435f [DevTools] Fix NPC targetting dev tools display window (#2943) 2023-02-17 06:07:20 -06:00
Chris Miles 26dc05c0dc [Player Events] Fix issue with item instances not being validated properly before accessing causing crashes on handin (#2945) 2023-02-17 06:07:02 -06:00
Chris Miles da20a6ab67 [Player Events] Fix rare out of bound issue when loading event types (#2946) 2023-02-17 06:06:53 -06:00
Chris Miles 3949a31246 [Fix] Issue with AssignRaidToInstance that was using the groups repository instead of raid (#2947) 2023-02-17 06:06:45 -06:00
Chris Miles e898be1ce9 [Player Events] Turn off KILLED_NPC (trash) off by default (#2948) 2023-02-17 06:05:43 -06:00
Akkadius 2962575dda [Hotfix] Missing comma in schema list breaking dumps 2023-02-16 22:27:09 -06:00
Akkadius 6a6045a21c [Release] Update should-release 2023-02-16 13:23:53 -06:00
Akkadius 717fe7dc8c [Release] 22.4.0 2023-02-16 10:24:45 -06:00
Chris Miles df69d12c0c [Release] 22.4.0 (#2940)
* [Release] 22.4.0

* Update changelog notes [skip ci]
2023-02-16 10:23:01 -06:00
Aeadoin 99e49cb2ec [Tradeskills] Check if combine would result in lore conflict (#2932)
* [Tradeskills] Check if Combine would result in Lore Conflict.

* formatting

* Add Saylinks to lore message output.

* Aknowledgement packets to prevent client issues.
2023-02-15 21:03:16 -06:00
Aeadoin 5ee2856133 [Bug Fix] Replace uses of SPELL_UNKNOWN with IsValidSpell() (#2938) 2023-02-15 21:01:35 -06:00
JJ 4a64048744 [Crash] Fix crash issue with dropping items and order of operations (#2939)
Should wait to null out invalid_drop until after it is used in log message.
2023-02-15 20:55:15 -06:00
Chris Miles 2ae795fd61 [Crash] Crash fix where invalid input to #heromodel would crash zone (#2937) 2023-02-15 14:36:09 -05:00
Chris Miles 0829bc08b8 [Doors] Fix issue where NPC's wouldn't open doors because door param overflow (#2934) 2023-02-15 10:59:38 -06:00
Chris Miles 903a385229 [Hotfix] Fix crash where dropped items crash Lua logic (#2936) 2023-02-15 10:57:05 -06:00
Chris Miles fafa33e190 [Groundspawns] Fix issue where groundspawns appear floating high off the ground (#2930) 2023-02-15 10:52:03 -06:00
Chris Miles 90a01f7c53 [Quest] Fix SetSimpleRoamBox in Perl to have optional params again (#2935) 2023-02-15 10:49:33 -06:00
Alex King 19434197d4 [Bug Fix] Fix crash in EVENT_DISCOVER_ITEM (#2933)
* [Bug Fix] Fix crash in EVENT_DISCOVER_ITEM

# Notes
- `const` didn't like the `std::any_cast`, also was passing `EQ::ItemData*` instead of `EQ::ItemInstance*`.

* Update client.cpp
2023-02-15 10:49:14 -06:00
Paul Coene 18b62667f0 [Bug Fix] Self Only Spells will no longer check target level or buff restrictions (#2931)
* [Bug Fix] Self Only Spells now work again (BuffSpellRestrictions)

* Moved variables under quick checks per review
2023-02-14 19:47:32 -05:00
Akkadius 8ed7ca977f [Hotfix] Another doors fix 2023-02-14 06:28:42 -06:00
Chris Miles 665e336946 [Fix] Fix issue where Lore groundspawn pickups will desync ROF2+ (#2929)
* [Fix] Fix issue where Lore groundspawn pickups will desync ROF2+

* Update object.cpp
2023-02-14 04:36:22 -06:00
Chris Miles ccf8504dec [Hotfix] Fix EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE regression caused by #2897 (#2928)
* [Hotfix] Fix EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE regression caused by  #2897

* Update client_packet.cpp
2023-02-14 04:36:10 -06:00
Akkadius d107213fe1 [Cleanup] Doors EVENT_CLICK_DOOR syntax adjustment 2023-02-14 04:10:15 -06:00
Akkadius c115cbcd6a [Hotfix] Fix Door opening regression caused by #2880 2023-02-14 01:36:03 -06:00
Chris Miles 064ae7ba89 [Hotfix] Fix HP_EVENT regression (#2927)
* [Hotfix] Fix HP_EVENT regression

* Update mob.cpp
2023-02-13 23:28:40 -05:00
Chris Miles 02302802b8 [CI] Fix Windows stderr not bubbling properly (#2925)
* [CI] Fix Windows stderr not bubbling properly

* Update windows-build.ps1
2023-02-13 21:41:29 -06:00
Chris Miles 536e248424 [Windows] Fix MSVC compilation bug via workaround (#2926) 2023-02-13 21:21:05 -06:00
Chris Miles 5b56a23a8a [Hotfix] Fix issue with EVENT_HP firing regression from #2904 (#2924)
* [Hotfix] Fix issue with EVENT_HP firing from https://github.com/EQEmu/Server/pull/2904

* Fix

* Fix
2023-02-13 22:02:18 -05:00
Alex King 97edb09fba [Rules] Add Group/Raid Experience Rules (#2850)
# Notes
- Add `Character:EnableGroupMemberEXPModifier`, enables or disables member-based experience modifiers in groups, default `true`.
- Add `Character:EnableRaidEXPModifier`, enables or disables member-based experience modifiers in raids, default `true`.
- Add `Character:EnableRaidMemberEXPModifier`, enables or disables member-based experience modifiers in raids, default `true`.
2023-02-13 00:25:59 -06:00
Alex King 85f7b10f90 [Cleanup] Remove Unused Mod Hooks (#2856)
- Removes old `mod_` hooks that have gone unused for years in favor of Lua mods.
2023-02-13 00:24:23 -06:00
Aeadoin 0f49fbcfcd [Bots] Add Support for TryTriggerOnCastFocusEffect (#2864) 2023-02-13 00:19:50 -06:00
Vayle e57979c3a8 [Quest API] Add Additional XP Events EVENT_AA_EXP_GAIN, EVENT_EXP_GAIN (#2865)
* Add XP Events

* Tweak

* Formatting

* Additional tweak

* Adjustment

* Update export naming

* Formatting

* Indenting

* Finalizing formatting

* Indenting adjustments

* Use tab character setting

* Remove double tabs

* Update exp.cpp

---------

Co-authored-by: Alex King <89047260+Kinglykrab@users.noreply.github.com>
2023-02-13 00:19:02 -06:00
55 changed files with 682 additions and 848 deletions
+163
View File
@@ -1,3 +1,166 @@
## [22.4.1] - 02/17/2023
### Bots
* Set Taunt to enabled for SK/Paladin Bots by Default. ([#2941](https://github.com/EQEmu/Server/pull/2941)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-17
### DevTools
* Fix NPC targetting dev tools display window ([#2943](https://github.com/EQEmu/Server/pull/2943)) ([Akkadius](https://github.com/Akkadius)) 2023-02-17
### Fixes
* Issue with AssignRaidToInstance that was using the groups repository instead of raid ([#2947](https://github.com/EQEmu/Server/pull/2947)) ([Akkadius](https://github.com/Akkadius)) 2023-02-17
* Missing comma in schema list breaking dumps ([Akkadius](https://github.com/Akkadius)) 2023-02-17
### Player Events
* Fix issue with item instances not being validated properly before accessing causing crashes on handin ([#2945](https://github.com/EQEmu/Server/pull/2945)) ([Akkadius](https://github.com/Akkadius)) 2023-02-17
* Fix rare out of bound issue when loading event types ([#2946](https://github.com/EQEmu/Server/pull/2946)) ([Akkadius](https://github.com/Akkadius)) 2023-02-17
* Turn off KILLED_NPC (trash) off by default ([#2948](https://github.com/EQEmu/Server/pull/2948)) ([Akkadius](https://github.com/Akkadius)) 2023-02-17
## [22.4.0] - 02/17/2023
### Bots
* Add Additional HeroicAgi/Dex Modifiers. ([#2838](https://github.com/EQEmu/Server/pull/2838)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-07
* Add Additional HeroicStr modifiers. ([#2837](https://github.com/EQEmu/Server/pull/2837)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-07
* Add IsBot() to methods in attack.cpp where applicable. ([#2840](https://github.com/EQEmu/Server/pull/2840)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-09
* Add Lore Check for Augments. ([#2874](https://github.com/EQEmu/Server/pull/2874)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-12
* Add Pet Power Support for Temp Pets. ([#2853](https://github.com/EQEmu/Server/pull/2853)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-11
* Add Support for TryTriggerOnCastFocusEffect ([#2864](https://github.com/EQEmu/Server/pull/2864)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-13
* Add TotalDominationBonus modifiers. ([#2852](https://github.com/EQEmu/Server/pull/2852)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-09
* ST_AreaClientOnly spells to land on Bots ([#2849](https://github.com/EQEmu/Server/pull/2849)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-09
* Update ResistSpell to use temp_level_diff client formula ([#2851](https://github.com/EQEmu/Server/pull/2851)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-09
### Bots & Mercenaries
* Add 100% Hit chance if sitting while attacked. ([#2839](https://github.com/EQEmu/Server/pull/2839)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-07
* Add Support for TrySympatheticProc ([#2866](https://github.com/EQEmu/Server/pull/2866)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-13
### CI
* Fix Windows stderr not bubbling properly ([#2925](https://github.com/EQEmu/Server/pull/2925)) ([Akkadius](https://github.com/Akkadius)) 2023-02-14
### Code
* Add IsOfClientBot() virtual method. ([#2845](https://github.com/EQEmu/Server/pull/2845)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-07
* Doors EVENT_CLICK_DOOR syntax adjustment ([Akkadius](https://github.com/Akkadius)) 2023-02-14
* Remove Unused Mod Hooks ([#2856](https://github.com/EQEmu/Server/pull/2856)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
### Crash
* Crash fix where invalid input to #heromodel would crash zone ([#2937](https://github.com/EQEmu/Server/pull/2937)) ([Akkadius](https://github.com/Akkadius)) 2023-02-15
* Fix Bot Crash in Bot::Bot Constructor. ([#2868](https://github.com/EQEmu/Server/pull/2868)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-12
* Fix Crash in FindType ([#2867](https://github.com/EQEmu/Server/pull/2867)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-13
* Fix crash in Mob::CommonDamage when attacker was null ([#2872](https://github.com/EQEmu/Server/pull/2872)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-13
* Fix crash issue with dropping items and order of operations ([#2939](https://github.com/EQEmu/Server/pull/2939)) ([joligario](https://github.com/joligario)) 2023-02-16
* Fix issue where long short names overflow file_name ([Akkadius](https://github.com/Akkadius)) 2023-02-09
* Fix potential crash in Mob::CommonDamage ([#2848](https://github.com/EQEmu/Server/pull/2848)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-09
### Doors
* Fix issue where NPC's wouldn't open doors because door param overflow ([#2934](https://github.com/EQEmu/Server/pull/2934)) ([Akkadius](https://github.com/Akkadius)) 2023-02-15
### Feature
* Add IsOfClientBotMerc() virtual method. ([#2843](https://github.com/EQEmu/Server/pull/2843)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-07
### Fixes
* Another doors fix ([Akkadius](https://github.com/Akkadius)) 2023-02-14
* Fix CheckNumHitsRemaining() with 1H Blunt ([#2846](https://github.com/EQEmu/Server/pull/2846)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-07
* Fix Door opening regression caused by #2880 ([Akkadius](https://github.com/Akkadius)) 2023-02-14
* Fix EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE regression caused by #2897 ([#2928](https://github.com/EQEmu/Server/pull/2928)) ([Akkadius](https://github.com/Akkadius)) 2023-02-14
* Fix HP_EVENT regression ([#2927](https://github.com/EQEmu/Server/pull/2927)) ([Akkadius](https://github.com/Akkadius)) 2023-02-14
* Fix crash in EVENT_DISCOVER_ITEM ([#2933](https://github.com/EQEmu/Server/pull/2933)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-15
* Fix crash where dropped items crash Lua logic ([#2936](https://github.com/EQEmu/Server/pull/2936)) ([Akkadius](https://github.com/Akkadius)) 2023-02-15
* Fix for interrupting item casts to no longer lock the client if cast time of item greater than 0 ([#2921](https://github.com/EQEmu/Server/pull/2921)) ([Natedog2012](https://github.com/Natedog2012)) 2023-02-13
* Fix issue where Lore groundspawn pickups will desync ROF2+ ([#2929](https://github.com/EQEmu/Server/pull/2929)) ([Akkadius](https://github.com/Akkadius)) 2023-02-14
* Fix issue with EVENT_HP firing regression from #2904 ([#2924](https://github.com/EQEmu/Server/pull/2924)) ([Akkadius](https://github.com/Akkadius)) 2023-02-14
* Replace uses of SPELL_UNKNOWN with IsValidSpell() ([#2938](https://github.com/EQEmu/Server/pull/2938)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-16
* Self Only Spells will no longer check target level or buff restrictions ([#2931](https://github.com/EQEmu/Server/pull/2931)) ([noudess](https://github.com/noudess)) 2023-02-15
### Groundspawns
* Fix issue where groundspawns appear floating high off the ground ([#2930](https://github.com/EQEmu/Server/pull/2930)) ([Akkadius](https://github.com/Akkadius)) 2023-02-15
### Logging
* Add raw opcode when emu translated opcode is not found (OP_Unknown) via (C->S) ([#2847](https://github.com/EQEmu/Server/pull/2847)) ([Akkadius](https://github.com/Akkadius)) 2023-02-08
* Implement Player Event Logging system ([#2833](https://github.com/EQEmu/Server/pull/2833)) ([Akkadius](https://github.com/Akkadius)) 2023-02-13
### Quest API
* (Performance) Check equip or scale item events exist before export and execute ([#2898](https://github.com/EQEmu/Server/pull/2898)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event EVENT_AA_BUY or EVENT_AA_GAIN exist before export and execute ([#2892](https://github.com/EQEmu/Server/pull/2892)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event EVENT_AGGRO, EVENT_ATTACK, or EVENT_COMBAT exist before export and execute ([#2901](https://github.com/EQEmu/Server/pull/2901)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event EVENT_COMBINE, EVENT_COMBINE_SUCCESS, EVENT_COMBINE_FAILURE, or EVENT_COMBINE_VALIDATE exist before export and execute ([#2896](https://github.com/EQEmu/Server/pull/2896)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event EVENT_DEATH, EVENT_DEATH_COMPLETE, or EVENT_DEATH_ZONE exist before export and execute ([#2909](https://github.com/EQEmu/Server/pull/2909)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event EVENT_ENVIRONMENTAL_DAMAGE exists before export and execute ([#2899](https://github.com/EQEmu/Server/pull/2899)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event EVENT_FEIGN_DEATH exists before export and execute ([#2916](https://github.com/EQEmu/Server/pull/2916)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event EVENT_ITEM_TICK or EVENT_WEAPON_PROC exist before export and execute ([#2914](https://github.com/EQEmu/Server/pull/2914)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event EVENT_LANGUAGE_SKILL_UP, EVENT_SKILL_UP, or EVENT_USE_SKILL exist before export and execute ([#2894](https://github.com/EQEmu/Server/pull/2894)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event EVENT_PAYLOAD or EVENT_SIGNAL exist before export and execute ([#2902](https://github.com/EQEmu/Server/pull/2902)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event EVENT_SLAY exists before export and execute ([#2910](https://github.com/EQEmu/Server/pull/2910)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event EVENT_WAYPOINT_ARRIVE or EVENT_WAYPOINT_DEPART exist before export and execute ([#2905](https://github.com/EQEmu/Server/pull/2905)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_AGGRO_SAY, EVENT_SAY, and EVENT_PROXIMITY_SAY ([#2882](https://github.com/EQEmu/Server/pull/2882)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_BOT_CREATE ([#2886](https://github.com/EQEmu/Server/pull/2886)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_CLICK_DOOR and EVENT_CLICK_OBJECT ([#2880](https://github.com/EQEmu/Server/pull/2880)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_DESPAWN and EVENT_DESPAWN_ZONE ([#2887](https://github.com/EQEmu/Server/pull/2887)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_DISCOVER_ITEM ([#2912](https://github.com/EQEmu/Server/pull/2912)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_DUEL_LOSE and EVENT_DUEL_WIN ([#2915](https://github.com/EQEmu/Server/pull/2915)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_ENTER_ZONE and EVENT_ZONE ([#2900](https://github.com/EQEmu/Server/pull/2900)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_GM_COMMAND ([#2890](https://github.com/EQEmu/Server/pull/2890)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_GROUP_CHANGE ([#2884](https://github.com/EQEmu/Server/pull/2884)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_HP ([#2904](https://github.com/EQEmu/Server/pull/2904)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_KILLED_MERIT ([#2911](https://github.com/EQEmu/Server/pull/2911)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_LEVEL_UP and EVENT_LEVEL_DOWN ([#2889](https://github.com/EQEmu/Server/pull/2889)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_POPUP_RESPONSE ([#2881](https://github.com/EQEmu/Server/pull/2881)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_RESPAWN ([#2917](https://github.com/EQEmu/Server/pull/2917)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_TICK ([#2919](https://github.com/EQEmu/Server/pull/2919)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_TIMER ([#2903](https://github.com/EQEmu/Server/pull/2903)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_TRADE ([#2906](https://github.com/EQEmu/Server/pull/2906)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_UNHANDLED_OPCODE ([#2918](https://github.com/EQEmu/Server/pull/2918)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_WARP ([#2907](https://github.com/EQEmu/Server/pull/2907)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute area events ([#2888](https://github.com/EQEmu/Server/pull/2888)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check merchant events exist before export and execute ([#2893](https://github.com/EQEmu/Server/pull/2893)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check spell or cast events exist before export and execute ([#2897](https://github.com/EQEmu/Server/pull/2897)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check task events exist before export and execute ([#2883](https://github.com/EQEmu/Server/pull/2883)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_CONNECT and EVENT_DISCONNECT ([#2913](https://github.com/EQEmu/Server/pull/2913)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* (Performance) Check event exists before export and execute EVENT_TEST_BUFF ([#2920](https://github.com/EQEmu/Server/pull/2920)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Add $target export to EVENT_INSPECT in Perl ([#2891](https://github.com/EQEmu/Server/pull/2891)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Add Additional XP Events EVENT_AA_EXP_GAIN, EVENT_EXP_GAIN ([#2865](https://github.com/EQEmu/Server/pull/2865)) ([Valorith](https://github.com/Valorith)) 2023-02-13
* Add EVENT_DESTROY_ITEM_CLIENT to Perl/Lua. ([#2871](https://github.com/EQEmu/Server/pull/2871)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Add EVENT_DROP_ITEM_CLIENT to Perl/Lua ([#2869](https://github.com/EQEmu/Server/pull/2869)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Add Recipe-based methods to Perl/Lua. ([#2844](https://github.com/EQEmu/Server/pull/2844)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-08
* Export $door to EVENT_CLICKDOOR in Perl ([#2861](https://github.com/EQEmu/Server/pull/2861)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-11
* Export $hate_entity to EVENT_HATE_LIST in Perl ([#2885](https://github.com/EQEmu/Server/pull/2885)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Export $item and $augment to augment events in Perl ([#2895](https://github.com/EQEmu/Server/pull/2895)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Export $item and $corpse to EVENT_LOOT and EVENT_LOOT_ZONE in Perl ([#2878](https://github.com/EQEmu/Server/pull/2878)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Export $item to Client/Bot Equip Events in Perl ([#2860](https://github.com/EQEmu/Server/pull/2860)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-11
* Export $item to EVENT_DISCOVER_ITEM in Perl ([#2863](https://github.com/EQEmu/Server/pull/2863)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-11
* Export $item to EVENT_PLAYER_PICKUP in Perl. ([#2875](https://github.com/EQEmu/Server/pull/2875)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Export $item to Fishing and Forage Events in Perl ([#2876](https://github.com/EQEmu/Server/pull/2876)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Export $killed_npc to EVENT_NPC_SLAY to Perl ([#2879](https://github.com/EQEmu/Server/pull/2879)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Export $object to EVENT_CLICK_OBJECT in Perl ([#2862](https://github.com/EQEmu/Server/pull/2862)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-11
* Export $spawned to EVENT_SPAWN_ZONE in Perl ([#2877](https://github.com/EQEmu/Server/pull/2877)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Export target to EVENT_TARGET_CHANGE in Perl/Lua. ([#2870](https://github.com/EQEmu/Server/pull/2870)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Export targets to EVENT_CONSIDER and EVENT_CONSIDER_CORPSE ([#2908](https://github.com/EQEmu/Server/pull/2908)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
* Fix SetSimpleRoamBox in Perl to have optional params again ([#2935](https://github.com/EQEmu/Server/pull/2935)) ([Akkadius](https://github.com/Akkadius)) 2023-02-15
### Rules
* Add Group/Raid Experience Rules ([#2850](https://github.com/EQEmu/Server/pull/2850)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-13
### Tradeskills
* Check if combine would result in lore conflict ([#2932](https://github.com/EQEmu/Server/pull/2932)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-16
### Windows
* Fix MSVC compilation bug via workaround ([#2926](https://github.com/EQEmu/Server/pull/2926)) ([Akkadius](https://github.com/Akkadius)) 2023-02-14
## [22.3.0] - 02/06/2023
### Bots
+1 -1
View File
@@ -409,7 +409,7 @@ void Database::AssignRaidToInstance(uint32 raid_id, uint32 instance_id)
auto zone_id = GetInstanceZoneID(instance_id);
auto version = GetInstanceVersion(instance_id);
auto l = GroupIdRepository::GetWhere(
auto l = RaidMembersRepository::GetWhere(
*this,
fmt::format(
"raidid = {}",
+1 -1
View File
@@ -342,7 +342,7 @@ namespace DatabaseSchema {
"saylink",
"server_scheduled_events",
"player_event_log_settings",
"player_event_logs"
"player_event_logs",
"shared_task_activity_state",
"shared_task_dynamic_zones",
"shared_task_members",
+4 -1
View File
@@ -30,6 +30,9 @@ void PlayerEventLogs::Init()
std::vector<int> db{};
db.reserve(s.size());
for (auto &e: s) {
if (e.id >= PlayerEvent::MAX) {
continue;
}
m_settings[e.id] = e;
db.emplace_back(e.id);
}
@@ -693,7 +696,7 @@ void PlayerEventLogs::SetSettingsDefaults()
m_settings[PlayerEvent::BANDOLIER_SWAP].event_enabled = 0;
m_settings[PlayerEvent::DISCOVER_ITEM].event_enabled = 1;
m_settings[PlayerEvent::POSSIBLE_HACK].event_enabled = 1;
m_settings[PlayerEvent::KILLED_NPC].event_enabled = 1;
m_settings[PlayerEvent::KILLED_NPC].event_enabled = 0;
m_settings[PlayerEvent::KILLED_NAMED_NPC].event_enabled = 1;
m_settings[PlayerEvent::KILLED_RAID_NPC].event_enabled = 1;
+1 -1
View File
@@ -111,7 +111,7 @@ namespace cereal
@param indentChar The type of character to indent with
@param indentLength The number of indentChar to use for indentation
(0 corresponds to no indentation) */
explicit Options( int precision = JSONWriterSL::Writer::kDefaultMaxDecimalPlaces,
explicit Options( int precision = 324,
IndentChar indentChar = IndentChar::space,
unsigned int indentLength = 4,
bool singleLine = false) :
+4 -1
View File
@@ -197,10 +197,13 @@ RULE_BOOL(Character, PetZoneWithOwner, true, "Should Pets Zone with Owner")
RULE_BOOL(Character, FullManaOnDeath, true, "On death set mana to full")
RULE_BOOL(Character, FullEndurOnDeath, true, "On death set endurance to full")
RULE_INT(Character, ExperiencePercentCapPerKill, -1, "Caps the percentage of experience that can be gained per kill. -1 disables the cap; 0 blocks all (non-aa) xp.")
RULE_BOOL(Character, EnableGroupEXPModifier, true, "Enable or disable the group experience modifier based on number of players in group, default is true")
RULE_BOOL(Character, EnableGroupEXPModifier, true, "Enable or disable the group experience modifier in group, default is true")
RULE_BOOL(Character, EnableGroupMemberEXPModifier, true, "Enable or disable the group member experience modifier based on number of players in group, default is true")
RULE_REAL(Character, GroupMemberEXPModifier, 0.2, "Sets the group experience modifier per members between 2 and 5, default is 0.2")
RULE_REAL(Character, FullGroupEXPModifier, 2.16, "Sets the group experience modifier for a full group, default is 2.16")
RULE_BOOL(Character, IgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.")
RULE_BOOL(Character, EnableRaidEXPModifier, true, "Enable or disable the raid experience modifier, default is true")
RULE_BOOL(Character, EnableRaidMemberEXPModifier, true, "Enable or disable the raid experience modifier based on members in raid, default is true")
RULE_CATEGORY_END()
RULE_CATEGORY(Mercs)
+1 -1
View File
@@ -25,7 +25,7 @@
// Build variables
// these get injected during the build pipeline
#define CURRENT_VERSION "22.3.0-dev" // always append -dev to the current version for custom-builds
#define CURRENT_VERSION "22.4.0-dev" // always append -dev to the current version for custom-builds
#define LOGIN_VERSION "0.8.0"
#define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "eqemu-server",
"version": "22.3.0",
"version": "22.4.1",
"repository": {
"type": "git",
"url": "https://github.com/EQEmu/Server.git"
+1 -7
View File
@@ -72,13 +72,7 @@ func main() {
}
if len(os.Getenv("RCLONE_FTP_PASS")) == 0 {
fmt.Printf("Missing RCLONE_FTP_PASS no need to deploy\n")
fmt.Printf("Exiting code 78 to halt pipeline steps gracefully\n")
os.Exit(78)
}
if len(os.Getenv("GH_RELEASE_GITHUB_API_TOKEN")) == 0 {
fmt.Printf("Missing GH_RELEASE_GITHUB_API_TOKEN no need to deploy\n")
fmt.Printf("Missing RCLONE_FTP_PASS no need to release\n")
fmt.Printf("Exiting code 78 to halt pipeline steps gracefully\n")
os.Exit(78)
}
Binary file not shown.
+38 -45
View File
@@ -1,49 +1,42 @@
try
$cwd = Get-Location
Set-Location -Path "$cwd"
git submodule init
git submodule update
if (![System.IO.Directory]::Exists("$cwd\win-build-x64"))
{
$cwd = Get-Location
Set-Location -Path "$cwd"
git submodule init
git submodule update
if (![System.IO.Directory]::Exists("$cwd\win-build-x64"))
{
Write-Information -MessageData "Creating build x64 folder" -InformationAction Continue
New-Item -Path "$cwd\win-build-x64" -ItemType Directory
}
perl .\utils\scripts\build\tag-version.pl
Write-Information -MessageData "Creating build x64" -InformationAction Continue
Set-Location -Path "$cwd\win-build-x64"
cmake -Wno-dev -G "Visual Studio 17 2022" -A x64 -DEQEMU_BUILD_TESTS=ON -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_ZLIB=ON "$cwd"
cmake --build . --config RelWithDebInfo --clean-first
Set-Location -Path "$cwd"
.\utils\scripts\build\should-release\should-release.exe; if ($LASTEXITCODE -ne 0) { exit }
# trim some fat
del $cwd\win-build-x64\bin\RelWithDebInfo\export_client_files.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\import_client_files.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\shared_memory.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\queryserv.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\eqlaunch.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\cppunit.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\tests.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\tests.exe
7z a eqemu-server-windows-x64.zip $cwd\win-build-x64\bin\RelWithDebInfo\*.exe $cwd\win-build-x64\bin\RelWithDebInfo\*.dll $cwd\win-build-x64\bin\RelWithDebInfo\*.pdb $cwd\win-build-x64\libs\zlibng\RelWithDebInfo\*.dll $cwd\win-build-x64\libs\zlibng\RelWithDebInfo\*.pdb
dir *.zip
rclone config create remote ftp env_auth true
rclone copy eqemu-server-windows-x64.zip remote: 2>&1
rclone ls remote: 2>&1
}
catch
{
Write-Host ("Caught signal to end")
Write-Host $_
Write-Information -MessageData "Creating build x64 folder" -InformationAction Continue
New-Item -Path "$cwd\win-build-x64" -ItemType Directory
}
perl .\utils\scripts\build\tag-version.pl
Write-Information -MessageData "Creating build x64" -InformationAction Continue
Set-Location -Path "$cwd\win-build-x64"
cmake -Wno-dev -G "Visual Studio 17 2022" -A x64 -DEQEMU_BUILD_TESTS=ON -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_ZLIB=ON "$cwd"
cmake --build . --config RelWithDebInfo --clean-first
Set-Location -Path "$cwd"
if ($LASTEXITCODE -ne 0) { echo "Build emitted error"; exit 1 }
.\utils\scripts\build\should-release\should-release.exe; if ($LASTEXITCODE -ne 0) { exit }
# trim some fat
del $cwd\win-build-x64\bin\RelWithDebInfo\export_client_files.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\import_client_files.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\shared_memory.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\queryserv.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\eqlaunch.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\cppunit.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\tests.pdb
del $cwd\win-build-x64\bin\RelWithDebInfo\tests.exe
7z a eqemu-server-windows-x64.zip $cwd\win-build-x64\bin\RelWithDebInfo\*.exe $cwd\win-build-x64\bin\RelWithDebInfo\*.dll $cwd\win-build-x64\bin\RelWithDebInfo\*.pdb $cwd\win-build-x64\libs\zlibng\RelWithDebInfo\*.dll $cwd\win-build-x64\libs\zlibng\RelWithDebInfo\*.pdb
dir *.zip
rclone config create remote ftp env_auth true
rclone copy eqemu-server-windows-x64.zip remote: 2>&1
rclone ls remote: 2>&1
-1
View File
@@ -97,7 +97,6 @@ SET(zone_sources
mob_appearance.cpp
mob_movement_manager.cpp
mob_info.cpp
mod_functions.cpp
npc.cpp
npc_ai.cpp
npc_scale_manager.cpp
+2 -11
View File
@@ -59,7 +59,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
if (IsOfClientBot()) {
act_power = GetFocusEffect(focusPetPower, spell_id);
if (IsClient()) {
act_power = CastToClient()->mod_pet_power(act_power, spell_id);
act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);
}
}
@@ -302,15 +302,6 @@ void Mob::WakeTheDead(uint16 spell_id, Corpse *corpse_to_use, Mob *target, uint3
return;
}
/* TODO: Does WTD use pet focus?
int act_power = 0;
if (IsClient()) {
act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);
act_power = CastToClient()->mod_pet_power(act_power, spell_id);
}
*/
SwarmPet_Struct pet;
pet.count = 1;
pet.duration = 1;
@@ -832,7 +823,7 @@ void Client::InspectBuffs(Client* Inspector, int Rank)
uint32 buff_count = GetMaxTotalSlots();
uint32 packet_index = 0;
for (uint32 i = 0; i < buff_count; i++) {
if (buffs[i].spellid == SPELL_UNKNOWN)
if (!IsValidSpell(buffs[i].spellid))
continue;
ib->spell_id[packet_index] = buffs[i].spellid;
if (Rank > 1)
+2 -2
View File
@@ -520,7 +520,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
) {
if(CheckLosFN(mob)) {
LogAggro("Check aggro for [{}] target [{}]", GetName(), mob->GetName());
return mod_will_aggro(mob, this);
return true;
}
} else {
if (
@@ -548,7 +548,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
) {
if(CheckLosFN(mob)) {
LogAggro("Check aggro for [{}] target [{}]", GetName(), mob->GetName());
return mod_will_aggro(mob, this);
return true;
}
}
}
+42 -49
View File
@@ -1694,8 +1694,9 @@ void Client::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::Skill
if (dead || IsCorpse())
return;
if (spell_id == 0)
if (!IsValidSpell(spell_id)) {
spell_id = SPELL_UNKNOWN;
}
// cut all PVP spell damage to 2/3
// Blasting ourselfs is considered PvP
@@ -1719,8 +1720,9 @@ void Client::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::Skill
if (damage > 0) {
if (spell_id == SPELL_UNKNOWN)
if (!IsValidSpell(spell_id)) {
CheckIncreaseSkill(EQ::skills::SkillDefense, other, -15);
}
}
}
@@ -1755,7 +1757,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
}
}
if (killerMob && (killerMob->IsClient() || killerMob->IsBot()) && (spell != SPELL_UNKNOWN) && damage > 0) {
if (killerMob && killerMob->IsOfClientBot() && IsValidSpell(spell) && damage > 0) {
char val1[20] = { 0 };
entity_list.MessageCloseString(
@@ -1794,8 +1796,8 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
d->killer_id = killerMob ? killerMob->GetID() : 0;
d->corpseid = GetID();
d->bindzoneid = m_pp.binds[0].zone_id;
d->spell_id = spell == SPELL_UNKNOWN ? 0xffffffff : spell;
d->attack_skill = spell != SPELL_UNKNOWN ? 0xe7 : attack_skill;
d->spell_id = IsValidSpell(spell) ? spell : 0xffffffff;
d->attack_skill = IsValidSpell(spell) ? 0xe7 : attack_skill;
d->damage = damage;
app.priority = 6;
entity_list.QueueClients(this, &app);
@@ -1824,8 +1826,6 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0);
}
mod_client_death_npc(killerMob);
auto emote_id = killerMob->GetEmoteID();
if (emote_id) {
killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid);
@@ -1855,9 +1855,6 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
killerMob->CastToClient()->SetDueling(false);
killerMob->CastToClient()->SetDuelTarget(0);
entity_list.DuelMessage(killerMob, this, false);
mod_client_death_duel(killerMob);
}
else {
//otherwise, we just died, end the duel.
@@ -1928,7 +1925,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
}
}
if (spell != SPELL_UNKNOWN) {
if (IsValidSpell(spell)) {
uint32 buff_count = GetMaxTotalSlots();
for (uint16 buffIt = 0; buffIt < buff_count; buffIt++) {
if (buffs[buffIt].spellid == spell && buffs[buffIt].client) {
@@ -2260,8 +2257,6 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
otherlevel = otherlevel ? otherlevel : 1;
mylevel = mylevel ? mylevel : 1;
//damage = mod_npc_damage(damage, skillinuse, Hand, weapon, other);
my_hit.base_damage = GetBaseDamage() + eleBane;
my_hit.min_damage = GetMinDamage();
int32 hate = my_hit.base_damage + my_hit.min_damage;
@@ -2423,7 +2418,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
}
}
if (killer_mob && (killer_mob->IsClient() || killer_mob->IsBot()) && (spell != SPELL_UNKNOWN) && damage > 0) {
if (killer_mob && killer_mob->IsOfClientBot() && IsValidSpell(spell) && damage > 0) {
char val1[20] = { 0 };
entity_list.MessageCloseString(
@@ -2545,7 +2540,6 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
Raid *kr = entity_list.GetRaidByClient(give_exp_client);
int64 finalxp = give_exp_client->GetExperienceForKill(this);
finalxp = give_exp_client->mod_client_xp(finalxp, this);
// handle task credit on behalf of the killer
if (RuleB(TaskSystem, EnableTaskSystem)) {
@@ -2577,8 +2571,6 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
if (RuleB(NPC, EnableMeritBasedFaction))
c->SetFactionLevel(c->CharacterID(), GetNPCFactionID(), c->GetBaseClass(), c->GetBaseRace(), c->GetDeity());
mod_npc_killed_merit(kr->members[i].member);
PlayerCount++;
}
}
@@ -2629,8 +2621,6 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
if (RuleB(NPC, EnableMeritBasedFaction))
c->SetFactionLevel(c->CharacterID(), GetNPCFactionID(), c->GetBaseClass(), c->GetBaseRace(), c->GetDeity());
mod_npc_killed_merit(c);
PlayerCount++;
}
}
@@ -2680,8 +2670,6 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
give_exp_client->SetFactionLevel(give_exp_client->CharacterID(), GetNPCFactionID(), give_exp_client->GetBaseClass(),
give_exp_client->GetBaseRace(), give_exp_client->GetDeity());
mod_npc_killed_merit(give_exp_client);
// QueryServ Logging - Solo
if (RuleB(QueryServ, PlayerLogNPCKills)) {
auto pack = new ServerPacket(ServerOP_QSPlayerLogNPCKills,
@@ -2817,8 +2805,6 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
// Parse quests even if we're killed by an NPC
if (oos) {
mod_npc_killed(oos);
if (IsNPC()) {
auto emote_id = GetEmoteID();
if (emote_id) {
@@ -2962,7 +2948,7 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
if (GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && other->IsClient())
return;
if (spell_id != SPELL_UNKNOWN && NoDetrimentalSpellAggro(spell_id))
if (IsValidSpell(spell_id) && NoDetrimentalSpellAggro(spell_id))
return;
if (other == myowner)
@@ -3126,8 +3112,9 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) {
DS = spellbonuses.DamageShield;
rev_ds = attacker->spellbonuses.ReverseDamageShield;
if (spellbonuses.DamageShieldSpellID != 0 && spellbonuses.DamageShieldSpellID != SPELL_UNKNOWN)
if (IsValidSpell(spellbonuses.DamageShieldSpellID)) {
spellid = spellbonuses.DamageShieldSpellID;
}
}
else {
DS = spellbonuses.SpellDamageShield + itembonuses.SpellDamageShield + aabonuses.SpellDamageShield;
@@ -3196,8 +3183,9 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) {
//if we've gotten to this point, we know we know "attacker" hit "this" (us) for damage & we aren't invulnerable
uint16 rev_ds_spell_id = SPELL_UNKNOWN;
if (spellbonuses.ReverseDamageShieldSpellID != 0 && spellbonuses.ReverseDamageShieldSpellID != SPELL_UNKNOWN)
if (IsValidSpell(spellbonuses.ReverseDamageShieldSpellID)) {
rev_ds_spell_id = spellbonuses.ReverseDamageShieldSpellID;
}
if (rev_ds < 0) {
LogCombat("Applying Reverse Damage Shield of value [{}] to [{}]", rev_ds, attacker->GetName());
@@ -3630,7 +3618,7 @@ int64 Mob::ReduceAllDamage(int64 damage)
bool Mob::HasProcs() const
{
for (int i = 0; i < MAX_PROCS; i++) {
if (PermaProcs[i].spellID != SPELL_UNKNOWN || SpellProcs[i].spellID != SPELL_UNKNOWN) {
if (IsValidSpell(PermaProcs[i].spellID) || IsValidSpell(SpellProcs[i].spellID)) {
return true;
}
}
@@ -3648,7 +3636,7 @@ bool Mob::HasProcs() const
bool Mob::HasDefensiveProcs() const
{
for (int i = 0; i < MAX_PROCS; i++) {
if (DefensiveProcs[i].spellID != SPELL_UNKNOWN) {
if (IsValidSpell(DefensiveProcs[i].spellID)) {
return true;
}
}
@@ -3684,7 +3672,7 @@ bool Mob::HasSkillProcSuccess() const
bool Mob::HasRangedProcs() const
{
for (int i = 0; i < MAX_PROCS; i++){
if (RangedProcs[i].spellID != SPELL_UNKNOWN) {
if (IsValidSpell(RangedProcs[i].spellID)) {
return true;
}
}
@@ -3776,16 +3764,16 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
damage = DMG_INVULNERABLE;
}
if (spell_id != SPELL_UNKNOWN || attacker == nullptr)
if (IsValidSpell(spell_id) || attacker == nullptr)
avoidable = false;
// only apply DS if physical damage (no spell damage)
// damage shield calls this function with spell_id set, so its unavoidable
if (attacker && damage > 0 && spell_id == SPELL_UNKNOWN && skill_used != EQ::skills::SkillArchery && skill_used != EQ::skills::SkillThrowing) {
if (attacker && damage > 0 && !IsValidSpell(spell_id) && skill_used != EQ::skills::SkillArchery && skill_used != EQ::skills::SkillThrowing) {
DamageShield(attacker);
}
if (spell_id == SPELL_UNKNOWN && skill_used >= EQ::skills::Skill1HBlunt) {
if (!IsValidSpell(spell_id) && skill_used >= EQ::skills::Skill1HBlunt) {
CheckNumHitsRemaining(NumHit::IncomingHitAttempts);
if (attacker)
@@ -3812,7 +3800,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
//if there is some damage being done and theres an attacker involved
if (attacker) {
// if spell is lifetap add hp to the caster
if (spell_id != SPELL_UNKNOWN && IsLifetapSpell(spell_id)) {
if (IsValidSpell(spell_id) && IsLifetapSpell(spell_id)) {
int64 healed = damage;
healed = RuleB(Spells, CompoundLifetapHeals) ? attacker->GetActSpellHealing(spell_id, healed) : healed;
@@ -3883,7 +3871,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
}
//see if any runes want to reduce this damage
if (spell_id == SPELL_UNKNOWN) {
if (!IsValidSpell(spell_id)) {
damage = ReduceDamage(damage);
LogCombat("Melee Damage reduced to [{}]", damage);
damage = ReduceAllDamage(damage);
@@ -4163,12 +4151,12 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
}
}
if (spell_id != SPELL_UNKNOWN && !iBuffTic) {
if (IsValidSpell(spell_id) && !iBuffTic) {
//see if root will break
if (IsRooted() && !FromDamageShield) // neotoyko: only spells cancel root
TryRootFadeByDamage(buffslot, attacker);
}
else if (spell_id == SPELL_UNKNOWN)
else if (!IsValidSpell(spell_id))
{
//increment chances of interrupting
if (IsCasting()) { //shouldnt interrupt on regular spell damage
@@ -4229,17 +4217,18 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
//attacker is a pet, let pet owners see their pet's damage
Mob* owner = attacker->GetOwner();
if (owner && owner->IsClient()) {
if (((spell_id != SPELL_UNKNOWN) || (FromDamageShield)) && damage>0) {
if ((IsValidSpell(spell_id) || (FromDamageShield)) && damage > 0) {
//special crap for spell damage, looks hackish to me
char val1[20] = { 0 };
owner->MessageString(Chat::NonMelee, OTHER_HIT_NONMELEE, GetCleanName(), ConvertArray(damage, val1));
}
else {
if (damage > 0) {
if (spell_id != SPELL_UNKNOWN)
if (IsValidSpell(spell_id)) {
filter = iBuffTic ? FilterDOT : FilterSpellDamage;
else
} else {
filter = FilterPetHits;
}
}
else if (damage == -5)
filter = FilterNone; //cant filter invulnerable
@@ -4256,7 +4245,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
//attacker is not a pet, send to the attacker
//if the attacker is a client, try them with the correct filter
if (attacker && (attacker->IsClient() || attacker->IsBot())) {
if ((spell_id != SPELL_UNKNOWN || FromDamageShield) && damage > 0) {
if ((IsValidSpell(spell_id) || FromDamageShield) && damage > 0) {
//special crap for spell damage, looks hackish to me
char val1[20] = { 0 };
if (FromDamageShield) {
@@ -4281,10 +4270,12 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
// Only try to queue these packets to a client
else if (attacker && (attacker->IsClient())) {
if (damage > 0) {
if (spell_id != SPELL_UNKNOWN)
if (IsValidSpell(spell_id)) {
filter = iBuffTic ? FilterDOT : FilterSpellDamage;
else
filter = FilterNone; //cant filter our own hits
}
else {
filter = FilterNone; //cant filter our own hits
}
}
else if (damage == -5)
filter = FilterNone; //cant filter invulnerable
@@ -4299,10 +4290,12 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
//send damage to all clients around except the specified skip mob (attacker or the attacker's owner) and ourself
if (damage > 0) {
if (spell_id != SPELL_UNKNOWN)
if (IsValidSpell(spell_id)) {
filter = iBuffTic ? FilterDOT : FilterSpellDamage;
else
}
else {
filter = FilterOthersHit;
}
}
else if (damage == -5)
filter = FilterNone; //cant filter invulnerable
@@ -4363,7 +4356,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
else {
//else, it is a buff tic...
// So we can see our dot dmg like live shows it.
if (spell_id != SPELL_UNKNOWN && damage > 0 && attacker && attacker != this && !attacker->IsCorpse()) {
if (IsValidSpell(spell_id) && damage > 0 && attacker && attacker != this && !attacker->IsCorpse()) {
//might filter on (attack_skill>200 && attack_skill<250), but I dont think we need it
if (attacker->IsClient()) {
attacker->FilteredMessageString(attacker, Chat::DotDamage,
@@ -4756,7 +4749,7 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
// Not ranged
if (!rangedattk) {
// Perma procs (Not used for AA, they are handled below)
if (PermaProcs[i].spellID != SPELL_UNKNOWN) {
if (IsValidSpell(PermaProcs[i].spellID)) {
if (zone->random.Roll(PermaProcs[i].chance)) { // TODO: Do these get spell bonus?
LogCombat("Permanent proc [{}] procing spell [{}] ([{}] percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance);
ExecWeaponProc(nullptr, PermaProcs[i].spellID, on);
@@ -4767,7 +4760,7 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
}
// Spell procs (buffs)
if (SpellProcs[i].spellID != SPELL_UNKNOWN) {
if (IsValidSpell(SpellProcs[i].spellID)) {
if (SpellProcs[i].base_spellID == POISON_PROC) {
poison_slot=i;
continue; // Process the poison proc last per @mackal
@@ -4792,7 +4785,7 @@ void Mob::TrySpellProc(const EQ::ItemInstance *inst, const EQ::ItemData *weapon,
}
else if (rangedattk) { // ranged only
// ranged spell procs (buffs)
if (RangedProcs[i].spellID != SPELL_UNKNOWN) {
if (IsValidSpell(RangedProcs[i].spellID)) {
passed_skill_limit_check = PassLimitToSkill(skillinuse, RangedProcs[i].base_spellID, ProcType::RANGED_PROC);
+5 -4
View File
@@ -1858,12 +1858,13 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
newbon->AssistRange = -1;
int buff_count = GetMaxTotalSlots();
for(i = 0; i < buff_count; i++) {
if(buffs[i].spellid != SPELL_UNKNOWN){
for (i = 0; i < buff_count; i++) {
if (IsValidSpell(buffs[i].spellid)) {
ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining, i, buffs[i].instrument_mod);
if (buffs[i].hit_number > 0)
if (buffs[i].hit_number > 0) {
Numhits(true);
}
}
}
@@ -1874,7 +1875,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
//Disables a specific spell effect bonus completely, can also be limited to negate only item, AA or spell bonuses.
if (spellbonuses.NegateEffects){
for(i = 0; i < buff_count; i++) {
if( (buffs[i].spellid != SPELL_UNKNOWN) && (IsEffectInSpell(buffs[i].spellid, SE_NegateSpellEffect)) )
if(IsValidSpell(buffs[i].spellid) && (IsEffectInSpell(buffs[i].spellid, SE_NegateSpellEffect)) )
NegateSpellEffectBonuses(buffs[i].spellid);
}
}
+5 -5
View File
@@ -77,7 +77,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
SetBotCharmer(false);
SetPetChooser(false);
SetRangerAutoWeaponSelect(false);
SetTaunting(GetClass() == WARRIOR);
SetTaunting(GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT);
SetDefaultBotStance();
SetAltOutOfCombatBehavior(GetClass() == BARD); // will need to be updated if more classes make use of this flag
@@ -5352,7 +5352,7 @@ void Bot::Damage(Mob *from, int64 damage, uint16 spell_id, EQ::skills::SkillType
attacked_timer.Start(CombatEventTimer_expire);
// if spell is lifetap add hp to the caster
if (spell_id != SPELL_UNKNOWN && IsLifetapSpell(spell_id)) {
if (IsValidSpell(spell_id) && IsLifetapSpell(spell_id)) {
int64 healed = GetActSpellHealing(spell_id, damage);
LogCombatDetail("Applying lifetap heal of [{}] to [{}]", healed, GetCleanName());
HealDamage(healed);
@@ -7043,7 +7043,7 @@ void Bot::CalcRestState() {
uint32 buff_count = GetMaxTotalSlots();
for (unsigned int j = 0; j < buff_count; j++) {
if(buffs[j].spellid != SPELL_UNKNOWN) {
if(IsValidSpell(buffs[j].spellid)) {
if(IsDetrimentalSpell(buffs[j].spellid) && (buffs[j].ticsremaining > 0))
if(!DetrimentalSpellAllowsRest(buffs[j].spellid))
return;
@@ -7294,7 +7294,7 @@ void Bot::DoEnduranceUpkeep() {
uint32 buffs_i;
uint32 buff_count = GetMaxTotalSlots();
for (buffs_i = 0; buffs_i < buff_count; buffs_i++) {
if (buffs[buffs_i].spellid != SPELL_UNKNOWN) {
if (IsValidSpell(buffs[buffs_i].spellid)) {
int upkeep = spells[buffs[buffs_i].spellid].endurance_upkeep;
if(upkeep > 0) {
if(cost_redux > 0) {
@@ -8716,7 +8716,7 @@ bool Bot::GetNeedsCured(Mob *tar) {
int buffsWithCounters = 0;
needCured = true;
for (unsigned int j = 0; j < buff_count; j++) {
if(tar->GetBuffs()[j].spellid != SPELL_UNKNOWN) {
if(IsValidSpell(tar->GetBuffs()[j].spellid)) {
if(CalculateCounters(tar->GetBuffs()[j].spellid) > 0) {
buffsWithCounters++;
if(buffsWithCounters == 1 && (tar->GetBuffs()[j].ticsremaining < 2 || (int32)((tar->GetBuffs()[j].ticsremaining * 6) / tar->GetBuffs()[j].counters) < 2)) {
+2 -1
View File
@@ -1514,8 +1514,9 @@ bool BotDatabase::SavePetBuffs(const uint32 bot_id, const SpellBuff_Struct* pet_
return true;
for (int buff_index = 0; buff_index < PET_BUFF_COUNT; ++buff_index) {
if (!pet_buffs[buff_index].spellid || pet_buffs[buff_index].spellid == SPELL_UNKNOWN)
if (!IsValidSpell(pet_buffs[buff_index].spellid)) {
continue;
}
query = StringFormat(
"INSERT INTO `bot_pet_buffs` ("
+5 -24
View File
@@ -922,9 +922,6 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
safe_delete(pack);
}
//Return true to proceed, false to return
if(!mod_client_message(message, chan_num)) { return; }
// Garble the message based on drunkness
if (m_pp.intoxication > 0 && !(RuleB(Chat, ServerWideOOC) && chan_num == ChatChannel_OOC) && !GetGM()) {
GarbleMessage(message, (int)(m_pp.intoxication / 3));
@@ -2544,10 +2541,7 @@ bool Client::CheckIncreaseSkill(EQ::skills::SkillType skillid, Mob *against_who,
against_who->IsClient() ||
GetLevelCon(against_who->GetLevel()) == CON_GRAY
) {
//false by default
if (!mod_can_increase_skill(skillid, against_who)) {
return false;
}
return false;
}
}
@@ -2564,7 +2558,6 @@ bool Client::CheckIncreaseSkill(EQ::skills::SkillType skillid, Mob *against_who,
// This result is increased by the existing SkillUpModifier rule
double working_chance = (((RuleI(Character, SkillUpMaximumChancePercentage) - RuleI(Character, SkillUpMinimumChancePercentage) + chancemodi) * (pow(0.99, skillval))) + RuleI(Character, SkillUpMinimumChancePercentage));
Chance = (working_chance * RuleI(Character, SkillUpModifier) / 100);
Chance = mod_increase_skill_chance(Chance, against_who);
}
if(zone->random.Real(0, 99) < Chance)
@@ -2961,8 +2954,6 @@ bool Client::BindWound(Mob *bindmob, bool start, bool fail)
max_percent = 70 + maxHPBonus;
}
max_percent = mod_bindwound_percent(max_percent, bindmob);
int64 max_hp = bindmob->GetMaxHP() * max_percent / 100;
// send bindmob new hp's
@@ -2983,8 +2974,6 @@ bool Client::BindWound(Mob *bindmob, bool start, bool fail)
bindhps += bindhps * bindBonus / 100;
bindhps = mod_bindwound_hp(bindhps, bindmob);
// if the bind takes them above the max bindable
// cap it at that value. Dont know if live does it this way
// but it makes sense to me.
@@ -3026,8 +3015,6 @@ bool Client::BindWound(Mob *bindmob, bool start, bool fail)
if (max_percent > 100)
max_percent = 100;
max_percent = mod_bindwound_percent(max_percent, bindmob);
int max_hp = (bindmob->GetMaxHP() * max_percent) / 100;
if (max_hp > bindmob->GetMaxHP())
max_hp = bindmob->GetMaxHP();
@@ -3048,8 +3035,6 @@ bool Client::BindWound(Mob *bindmob, bool start, bool fail)
if (bindhps < 3)
bindhps = 3;
bindhps = mod_bindwound_hp(bindhps, bindmob);
bindhps += bindmob->GetHP();
if (bindhps > max_hp)
bindhps = max_hp;
@@ -4108,8 +4093,8 @@ void Client::DiscoverItem(uint32 item_id) {
}
if (parse->PlayerHasQuestSub(EVENT_DISCOVER_ITEM)) {
const auto* item = database.GetItem(item_id);
std::vector<std::any> args = {item};
auto* item = database.CreateItem(item_id);
std::vector<std::any> args = { item };
parse->EventPlayer(EVENT_DISCOVER_ITEM, this, "", item_id, &args);
}
@@ -6702,7 +6687,7 @@ void Client::SendStatsWindow(Client* client, bool use_window)
uint32 magic_rune_number = 0;
uint32 buff_count = GetMaxTotalSlots();
for (int i=0; i < buff_count; i++) {
if (buffs[i].spellid != SPELL_UNKNOWN) {
if (IsValidSpell(buffs[i].spellid)) {
if (buffs[i].melee_rune > 0) { rune_number += buffs[i].melee_rune; }
if (buffs[i].magic_rune > 0) { magic_rune_number += buffs[i].magic_rune; }
@@ -8387,8 +8372,6 @@ void Client::Consume(const EQ::ItemData *item, uint8 type, int16 slot, bool auto
return;
if (type == EQ::item::ItemTypeFood) {
increase = mod_food_value(item, increase);
if (increase < 0)
return;
@@ -8404,8 +8387,6 @@ void Client::Consume(const EQ::ItemData *item, uint8 type, int16 slot, bool auto
LogFood("Eating from slot: [{}]", (int)slot);
} else {
increase = mod_drink_value(item, increase);
if (increase < 0)
return;
@@ -8513,7 +8494,7 @@ void Client::ShowNumHits()
uint32 buffcount = GetMaxTotalSlots();
for (uint32 buffslot = 0; buffslot < buffcount; buffslot++) {
const Buffs_Struct &curbuff = buffs[buffslot];
if (curbuff.spellid != SPELL_UNKNOWN && curbuff.hit_number)
if (IsValidSpell(curbuff.spellid) && curbuff.hit_number)
Message(0, "You have %d hits left on %s", curbuff.hit_number, GetSpellName(curbuff.spellid));
}
return;
-22
View File
@@ -1598,28 +1598,6 @@ public:
void Consume(const EQ::ItemData *item, uint8 type, int16 slot, bool auto_consume);
void PlayMP3(const char* fname);
void ExpeditionSay(const char *str, int ExpID);
int mod_client_damage(int64 damage, EQ::skills::SkillType skillinuse, int hand, const EQ::ItemInstance* weapon, Mob* other);
bool mod_client_message(char* message, uint8 chan_num);
bool mod_can_increase_skill(EQ::skills::SkillType skillid, Mob* against_who);
double mod_increase_skill_chance(double chance, Mob* against_who);
int mod_bindwound_percent(int max_percent, Mob* bindmob);
int mod_bindwound_hp(int bindhps, Mob* bindmob);
int mod_client_haste(int h);
void mod_consider(Mob* tmob, Consider_Struct* con);
bool mod_saylink(const std::string&, bool silentsaylink);
int16 mod_pet_power(int16 act_power, uint16 spell_id);
float mod_tradeskill_chance(float chance, DBTradeskillRecipe_Struct *spec);
float mod_tradeskill_skillup(float chance_stage2);
int32 mod_tribute_item_value(int32 pts, const EQ::ItemInstance* item);
void mod_client_death_npc(Mob* killerMob);
void mod_client_death_duel(Mob* killerMob);
void mod_client_death_env();
int64 mod_client_xp(int64 in_exp, NPC *npc);
uint32 mod_client_xp_for_level(uint32 xp, uint16 check_level);
int mod_client_haste_cap(int cap);
int mod_consume(EQ::ItemData *item, EQ::item::ItemType type, int change);
int mod_food_value(const EQ::ItemData *item, int change);
int mod_drink_value(const EQ::ItemData *item, int change);
inline int32 GetEnvironmentDamageModifier() const { return environment_damage_modifier; }
void SetEnvironmentDamageModifier(int32 val) { environment_damage_modifier = val; }
-2
View File
@@ -1047,7 +1047,6 @@ int Client::CalcHaste()
else { // 1-50
cap = level + 25;
}
cap = mod_client_haste_cap(cap);
if (h > cap) {
h = cap;
}
@@ -1064,7 +1063,6 @@ int Client::CalcHaste()
h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3;
}
h += ExtraHaste; //GM granted haste.
h = mod_client_haste(h);
Haste = 100 + h;
return Haste;
}
+37 -37
View File
@@ -1456,7 +1456,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
database.LoadBuffs(this);
uint32 max_slots = GetMaxBuffSlots();
for (int i = 0; i < BUFF_COUNT; i++) {
if (buffs[i].spellid != SPELL_UNKNOWN) {
if (IsValidSpell(buffs[i].spellid)) {
m_pp.buffs[i].spellid = buffs[i].spellid;
m_pp.buffs[i].bard_modifier = buffs[i].instrument_mod;
m_pp.buffs[i].effect_type = 2;
@@ -4560,11 +4560,15 @@ void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app)
// don't spam scripts with client controlled doors if not within distance
if (within_distance) {
int quest_return = 0;
if (parse->PlayerHasQuestSub(EVENT_CLICK_DOOR)) {
std::vector<std::any> args = { currentdoor };
if (parse->EventPlayer(EVENT_CLICK_DOOR, this, std::to_string(cd->doorid), 0, &args) == 0) {
currentdoor->HandleClick(this, 0);
}
std::vector<std::any> args = {currentdoor};
quest_return = parse->EventPlayer(EVENT_CLICK_DOOR, this, std::to_string(cd->doorid), 0, &args);
}
if (quest_return == 0) {
currentdoor->HandleClick(this, 0);
}
}
else {
@@ -5115,8 +5119,6 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app)
con->faction = FACTION_DUBIOUSLY;
}
mod_consider(t, con);
QueuePacket(outapp);
// only wanted to check raid target once
// and need con to still be around so, do it here!
@@ -6204,7 +6206,6 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app)
}
if (GetHP() <= 0) {
mod_client_death_env();
Death(0, 32000, SPELL_UNKNOWN, EQ::skills::SkillHandtoHand);
}
SendHPUpdate();
@@ -8736,10 +8737,6 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app)
}
if (!response.empty()) {
if (!mod_saylink(response, silentsaylink)) {
return;
}
ChannelMessageReceived(ChatChannel_Say, 0, 100, response.c_str(), nullptr, true);
if (!silentsaylink) {
@@ -15242,34 +15239,37 @@ void Client::Handle_OP_Translocate(const EQApplicationPacket *app)
zone->GetInstanceID() == PendingTranslocateData.instance_id
);
int quest_return = 0;
if (parse->SpellHasQuestSub(spell_id, EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE)) {
if (parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, spell_id, "", 0) == 0) {
// If the spell has a translocate to bind effect, AND we are already in the zone the client
// is bound in, use the GoToBind method. If we send OP_Translocate in this case, the client moves itself
// to the bind coords it has from the PlayerProfile, but with the X and Y reversed. I suspect they are
// reversed in the pp, and since spells like Gate are handled serverside, this has not mattered before.
if (
IsTranslocateSpell(spell_id) &&
in_translocate_zone
) {
PendingTranslocate = false;
GoToBind();
return;
}
quest_return = parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, spell_id, "", 0);
}
////Was sending the packet back to initiate client zone...
////but that could be abusable, so lets go through proper channels
MovePC(
PendingTranslocateData.zone_id,
PendingTranslocateData.instance_id,
PendingTranslocateData.x,
PendingTranslocateData.y,
PendingTranslocateData.z,
PendingTranslocateData.heading,
0,
ZoneSolicited
);
if (quest_return == 0) {
// If the spell has a translocate to bind effect, AND we are already in the zone the client
// is bound in, use the GoToBind method. If we send OP_Translocate in this case, the client moves itself
// to the bind coords it has from the PlayerProfile, but with the X and Y reversed. I suspect they are
// reversed in the pp, and since spells like Gate are handled serverside, this has not mattered before.
if (
IsTranslocateSpell(spell_id) &&
in_translocate_zone
) {
PendingTranslocate = false;
GoToBind();
return;
}
////Was sending the packet back to initiate client zone...
////but that could be abusable, so lets go through proper channels
MovePC(
PendingTranslocateData.zone_id,
PendingTranslocateData.instance_id,
PendingTranslocateData.x,
PendingTranslocateData.y,
PendingTranslocateData.z,
PendingTranslocateData.heading,
0,
ZoneSolicited
);
}
}
+2 -2
View File
@@ -1917,7 +1917,7 @@ void Client::DoEnduranceUpkeep() {
uint32 buffs_i;
uint32 buff_count = GetMaxTotalSlots();
for (buffs_i = 0; buffs_i < buff_count; buffs_i++) {
if (buffs[buffs_i].spellid != SPELL_UNKNOWN) {
if (IsValidSpell(buffs[buffs_i].spellid)) {
int upkeep = spells[buffs[buffs_i].spellid].endurance_upkeep;
if(upkeep > 0) {
has_effect = true;
@@ -1966,7 +1966,7 @@ void Client::CalcRestState()
uint32 buff_count = GetMaxTotalSlots();
for (unsigned int j = 0; j < buff_count; j++) {
if(buffs[j].spellid != SPELL_UNKNOWN) {
if(IsValidSpell(buffs[j].spellid)) {
if(IsDetrimentalSpell(buffs[j].spellid) && (buffs[j].ticsremaining > 0))
if(!DetrimentalSpellAllowsRest(buffs[j].spellid))
return;
+2 -2
View File
@@ -38,7 +38,7 @@ public:
uint16 GetSize() { return m_size; }
uint32 GetClientVersionMask() { return m_client_version_mask; }
uint32 GetDoorDBID() { return m_database_id; }
uint32 GetDoorParam() { return m_door_param; }
int32 GetDoorParam() { return m_door_param; }
uint32 GetEntityID() { return m_entity_id; }
uint32 GetGuildID() { return m_guild_id; }
uint32 GetKeyItem() { return m_key_item_id; }
@@ -82,7 +82,7 @@ private:
uint8 m_no_key_ring;
uint8 m_trigger_door;
uint8 m_trigger_type;
uint32 m_door_param;
int32 m_door_param;
uint16 m_size;
int m_invert_state;
uint32 m_entity_id;
+12
View File
@@ -161,6 +161,8 @@ const char *QuestEventSubroutines[_LargestEventID] = {
"EVENT_TASK_BEFORE_UPDATE",
"EVENT_AA_BUY",
"EVENT_AA_GAIN",
"EVENT_AA_EXP_GAIN",
"EVENT_EXP_GAIN",
"EVENT_PAYLOAD",
"EVENT_LEVEL_DOWN",
"EVENT_GM_COMMAND",
@@ -2084,6 +2086,16 @@ void PerlembParser::ExportEventVariables(
break;
}
case EVENT_AA_EXP_GAIN: {
ExportVar(package_name.c_str(), "aa_exp_gained", data);
break;
}
case EVENT_EXP_GAIN: {
ExportVar(package_name.c_str(), "exp_gained", data);
break;
}
case EVENT_INSPECT: {
ExportVar(package_name.c_str(), "target_id", extradata);
if (extra_pointers && extra_pointers->size() == 1) {
+2
View File
@@ -106,6 +106,8 @@ typedef enum {
EVENT_TASK_BEFORE_UPDATE,
EVENT_AA_BUY,
EVENT_AA_GAIN,
EVENT_AA_EXP_GAIN,
EVENT_EXP_GAIN,
EVENT_PAYLOAD,
EVENT_LEVEL_DOWN,
EVENT_GM_COMMAND,
+41 -20
View File
@@ -804,14 +804,23 @@ void Client::SetEXP(uint64 set_exp, uint64 set_aaxp, bool isrezzexp) {
}
}
if (parse->PlayerHasQuestSub(EVENT_EXP_GAIN) && m_pp.exp != set_exp) {
parse->EventPlayer(EVENT_EXP_GAIN, this, std::to_string(set_exp - m_pp.exp), 0);
}
if (parse->PlayerHasQuestSub(EVENT_AA_EXP_GAIN) && m_pp.expAA != set_aaxp) {
parse->EventPlayer(EVENT_AA_EXP_GAIN, this, std::to_string(set_aaxp - m_pp.expAA), 0);
}
//set the client's EXP and AAEXP
m_pp.exp = set_exp;
m_pp.expAA = set_aaxp;
if (GetLevel() < 51) {
m_epp.perAA = 0; // turn off aa exp if they drop below 51
} else
SendAlternateAdvancementStats(); //otherwise, send them an AA update
} else {
SendAlternateAdvancementStats(); //otherwise, send them an AA update
}
//send the expdata in any case so the xp bar isnt stuck after leveling
uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1);
@@ -1057,8 +1066,6 @@ uint32 Client::GetEXPForLevel(uint16 check_level)
finalxp = uint64(finalxp * classmod);
}
finalxp = mod_client_xp_for_level(finalxp, check_level);
return finalxp;
}
@@ -1108,11 +1115,11 @@ void Group::SplitExp(const uint64 exp, Mob* other) {
return;
}
auto group_experience = exp;
auto group_experience = exp;
const auto highest_level = GetHighestLevel();
auto group_modifier = 1.0f;
if (RuleB(Character, EnableGroupEXPModifier)) {
if (RuleB(Character, EnableGroupMemberEXPModifier)) {
if (EQ::ValueWithin(member_count, 2, 5)) {
group_modifier = 1 + RuleR(Character, GroupMemberEXPModifier) * (member_count - 1); // 2 = 1.2x, 3 = 1.4x, 4 = 1.6x, 5 = 1.8x
} else if (member_count == 6) {
@@ -1121,11 +1128,18 @@ void Group::SplitExp(const uint64 exp, Mob* other) {
}
if (EQ::ValueWithin(member_count, 2, 6)) {
group_experience += static_cast<uint64>(
static_cast<float>(exp) *
group_modifier *
RuleR(Character, GroupExpMultiplier)
);
if (RuleB(Character, EnableGroupEXPModifier)) {
group_experience += static_cast<uint64>(
static_cast<float>(exp) *
group_modifier *
RuleR(Character, GroupExpMultiplier)
);
} else {
group_experience += static_cast<uint64>(
static_cast<float>(exp) *
group_modifier
);
}
}
const uint8 consider_level = Mob::GetLevelCon(highest_level, other->GetLevel());
@@ -1135,8 +1149,8 @@ void Group::SplitExp(const uint64 exp, Mob* other) {
for (const auto& m : members) {
if (m && m->IsClient()) {
const int32 diff = m->GetLevel() - highest_level;
int32 max_diff = -(m->GetLevel() * 15 / 10 - m->GetLevel());
const int32 diff = m->GetLevel() - highest_level;
int32 max_diff = -(m->GetLevel() * 15 / 10 - m->GetLevel());
if (max_diff > -5) {
max_diff = -5;
@@ -1165,23 +1179,30 @@ void Raid::SplitExp(const uint64 exp, Mob* other) {
return;
}
auto raid_experience = exp;
auto raid_experience = exp;
const auto highest_level = GetHighestLevel();
raid_experience = static_cast<uint64>(
static_cast<float>(raid_experience) *
(1.0f - RuleR(Character, RaidExpMultiplier))
);
if (RuleB(Character, EnableRaidEXPModifier)) {
raid_experience = static_cast<uint64>(
static_cast<float>(raid_experience) *
(1.0f - RuleR(Character, RaidExpMultiplier))
);
}
const auto consider_level = Mob::GetLevelCon(highest_level, other->GetLevel());
if (consider_level == CON_GRAY) {
return;
}
uint32 member_modifier = 1;
if (RuleB(Character, EnableRaidMemberEXPModifier)) {
member_modifier = member_count;
}
for (const auto& m : members) {
if (m.member) {
const int32 diff = m.member->GetLevel() - highest_level;
int32 max_diff = -(m.member->GetLevel() * 15 / 10 - m.member->GetLevel());
int32 max_diff = -(m.member->GetLevel() * 15 / 10 - m.member->GetLevel());
if (max_diff > -5) {
max_diff = -5;
@@ -1189,7 +1210,7 @@ void Raid::SplitExp(const uint64 exp, Mob* other) {
if (diff >= max_diff) {
const uint64 tmp = (m.member->GetLevel() + 3) * (m.member->GetLevel() + 3) * 75 * 35 / 10;
const uint64 tmp2 = (raid_experience / member_count) + 1;
const uint64 tmp2 = (raid_experience / member_modifier) + 1;
m.member->AddEXP(tmp < tmp2 ? tmp : tmp2, consider_level);
}
}
+1 -1
View File
@@ -21,7 +21,7 @@ void command_heromodel(Client *c, const Seperator *sep)
t = c->GetTarget();
}
auto hero_forge_model = std::stoul(sep->arg[1]);
auto hero_forge_model = Strings::IsNumber(sep->arg[1]) ? std::stoul(sep->arg[1]) : 0;
if (arguments > 1) {
auto slot = static_cast<uint8>(std::stoul(sep->arg[2]));
+3 -2
View File
@@ -848,13 +848,14 @@ void Client::DropItem(int16 slot_id, bool recurse)
}
}
}
invalid_drop = nullptr;
std::string message = fmt::format(
"Tried to drop an item on the ground that was no-drop! item_name [{}] item_id ({})",
invalid_drop->GetItem()->Name,
invalid_drop->GetItem()->ID
);
invalid_drop = nullptr;
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
GetInv().DeleteItem(slot_id);
return;
+3 -1
View File
@@ -4736,7 +4736,9 @@ luabind::scope lua_register_events() {
luabind::value("inspect", static_cast<int>(EVENT_INSPECT)),
luabind::value("task_before_update", static_cast<int>(EVENT_TASK_BEFORE_UPDATE)),
luabind::value("aa_buy", static_cast<int>(EVENT_AA_BUY)),
luabind::value("aa_gain", static_cast<int>(EVENT_AA_GAIN)),
luabind::value("aa_gained", static_cast<int>(EVENT_AA_GAIN)),
luabind::value("aa_exp_gained", static_cast<int>(EVENT_AA_EXP_GAIN)),
luabind::value("exp_gain", static_cast<int>(EVENT_EXP_GAIN)),
luabind::value("payload", static_cast<int>(EVENT_PAYLOAD)),
luabind::value("level_down", static_cast<int>(EVENT_LEVEL_DOWN)),
luabind::value("gm_command", static_cast<int>(EVENT_GM_COMMAND)),
+5 -1
View File
@@ -148,6 +148,8 @@ const char *LuaEvents[_LargestEventID] = {
"event_task_before_update",
"event_aa_buy",
"event_aa_gain",
"event_aa_exp_gain",
"event_exp_gain",
"event_payload",
"event_level_down",
"event_gm_command",
@@ -162,7 +164,7 @@ const char *LuaEvents[_LargestEventID] = {
"event_damage_taken",
"event_item_click_client",
"event_item_click_cast_client",
"event_destroy_item_client"
"event_destroy_item_client",
"event_drop_item_client"
};
@@ -275,6 +277,8 @@ LuaParser::LuaParser() {
PlayerArgumentDispatch[EVENT_INSPECT] = handle_player_inspect;
PlayerArgumentDispatch[EVENT_AA_BUY] = handle_player_aa_buy;
PlayerArgumentDispatch[EVENT_AA_GAIN] = handle_player_aa_gain;
PlayerArgumentDispatch[EVENT_AA_EXP_GAIN] = handle_player_aa_exp_gain;
PlayerArgumentDispatch[EVENT_EXP_GAIN] = handle_player_exp_gain;
PlayerArgumentDispatch[EVENT_PAYLOAD] = handle_player_payload;
PlayerArgumentDispatch[EVENT_LEVEL_UP] = handle_player_level_up;
PlayerArgumentDispatch[EVENT_LEVEL_DOWN] = handle_player_level_down;
+24
View File
@@ -1181,6 +1181,30 @@ void handle_player_aa_gain(
lua_setfield(L, -2, "aa_gained");
}
void handle_player_aa_exp_gain(
QuestInterface *parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
lua_pushinteger(L, std::stoull(data));
lua_setfield(L, -2, "aa_exp_gained");
}
void handle_player_exp_gain(
QuestInterface *parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
lua_pushinteger(L, std::stoull(data));
lua_setfield(L, -2, "exp_gained");
}
void handle_player_level_up(
QuestInterface *parse,
lua_State* L,
+18
View File
@@ -608,6 +608,24 @@ void handle_player_aa_gain(
std::vector<std::any> *extra_pointers
);
void handle_player_aa_exp_gain(
QuestInterface *parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_player_exp_gain(
QuestInterface *parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
void handle_player_payload(
QuestInterface *parse,
lua_State* L,
+4 -4
View File
@@ -1113,7 +1113,7 @@ void Merc::DoEnduranceUpkeep() {
uint32 buffs_i;
uint32 buff_count = GetMaxTotalSlots();
for (buffs_i = 0; buffs_i < buff_count; buffs_i++) {
if (buffs[buffs_i].spellid != SPELL_UNKNOWN) {
if (IsValidSpell(buffs[buffs_i].spellid)) {
int upkeep = spells[buffs[buffs_i].spellid].endurance_upkeep;
if(upkeep > 0) {
has_effect = true;
@@ -1158,7 +1158,7 @@ void Merc::CalcRestState() {
uint32 buff_count = GetMaxTotalSlots();
for (unsigned int j = 0; j < buff_count; j++) {
if(buffs[j].spellid != SPELL_UNKNOWN) {
if(IsValidSpell(buffs[j].spellid)) {
if(IsDetrimentalSpell(buffs[j].spellid) && (buffs[j].ticsremaining > 0))
if(!DetrimentalSpellAllowsRest(buffs[j].spellid))
return;
@@ -2579,7 +2579,7 @@ int64 Merc::GetFocusEffect(focusType type, uint16 spell_id, bool from_buff_tic)
if (equipment[x] == 0)
continue;
TempItem = database.GetItem(equipment[x]);
if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) {
if (TempItem && IsValidSpell(TempItem->Focus.Effect)) {
if(rand_effectiveness) {
focus_max = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id, true);
if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) {
@@ -3895,7 +3895,7 @@ bool Merc::GetNeedsCured(Mob *tar) {
needCured = true;
for (unsigned int j = 0; j < buff_count; j++) {
if(tar->GetBuffs()[j].spellid != SPELL_UNKNOWN) {
if (IsValidSpell(tar->GetBuffs()[j].spellid)) {
if(CalculateCounters(tar->GetBuffs()[j].spellid) > 0) {
buffsWithCounters++;
+35 -37
View File
@@ -1378,26 +1378,22 @@ void Mob::CreateHPPacket(EQApplicationPacket* app)
ds->hp = (int)GetHPRatio();
// hp event
if (IsNPC() && (GetNextHPEvent() > 0))
{
if (ds->hp < GetNextHPEvent())
{
if (IsNPC() && (GetNextHPEvent() > 0)) {
if (ds->hp < GetNextHPEvent()) {
std::string export_string = fmt::format("{}", GetNextHPEvent());
SetNextHPEvent(-1);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_HP)) {
parse->EventNPC(EVENT_HP, CastToNPC(), nullptr, std::to_string(GetNextHPEvent()), 0);
if (parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_HP)) {
parse->EventNPC(EVENT_HP, CastToNPC(), nullptr, export_string, 0);
}
}
}
if (IsNPC() && (GetNextIncHPEvent() > 0))
{
if (ds->hp > GetNextIncHPEvent())
{
if (IsNPC() && (GetNextIncHPEvent() > 0)) {
if (ds->hp > GetNextIncHPEvent()) {
std::string export_string = fmt::format("{}", GetNextIncHPEvent());
SetNextIncHPEvent(-1);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_HP)) {
parse->EventNPC(EVENT_HP, CastToNPC(), nullptr, std::to_string(GetNextIncHPEvent()), 1);
if (parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_HP)) {
parse->EventNPC(EVENT_HP, CastToNPC(), nullptr, export_string, 1);
}
}
}
@@ -2335,7 +2331,7 @@ void Mob::ShowBuffs(Client* client) {
uint32 i;
uint32 buff_count = GetMaxTotalSlots();
for (i=0; i < buff_count; i++) {
if (buffs[i].spellid != SPELL_UNKNOWN) {
if (IsValidSpell(buffs[i].spellid)) {
if (spells[buffs[i].spellid].buff_duration_formula == DF_Permanent)
client->Message(Chat::White, " %i: %s: Permanent", i, spells[buffs[i].spellid].name);
else
@@ -2369,7 +2365,7 @@ void Mob::ShowBuffList(Client* client) {
uint32 i;
uint32 buff_count = GetMaxTotalSlots();
for (i = 0; i < buff_count; i++) {
if (buffs[i].spellid != SPELL_UNKNOWN) {
if (IsValidSpell(buffs[i].spellid)) {
if (spells[buffs[i].spellid].buff_duration_formula == DF_Permanent)
client->Message(Chat::White, " %i: %s: Permanent", i, spells[buffs[i].spellid].name);
else
@@ -4107,7 +4103,7 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on,
return;
}
if(spell_id == SPELL_UNKNOWN || on->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
if(!IsValidSpell(spell_id) || on->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
//This is so 65535 doesn't get passed to the client message and to logs because it is not relavant information for debugging.
return;
}
@@ -4265,6 +4261,10 @@ void Mob::SetTarget(Mob *mob)
parse->BotHasQuestSub(EVENT_TARGET_CHANGE)
);
if (IsClient() && CastToClient()->admin > AccountStatus::GMMgmt) {
DisplayInfo(mob);
}
if (has_target_change_event) {
std::vector<std::any> args;
@@ -4279,10 +4279,6 @@ void Mob::SetTarget(Mob *mob)
parse->EventPlayer(EVENT_TARGET_CHANGE, CastToClient(), "", 0, &args);
}
if (CastToClient()->admin > AccountStatus::GMMgmt) {
DisplayInfo(mob);
}
CastToClient()->SetBotPrecombat(false); // Any change in target will nullify this flag (target == mob checked above)
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_TARGET_CHANGE)) {
@@ -4368,8 +4364,9 @@ int Mob::CountDispellableBuffs()
if(spells[buffs[x].spellid].good_effect == 0)
continue;
if(buffs[x].spellid != SPELL_UNKNOWN && spells[buffs[x].spellid].buff_duration_formula != DF_Permanent)
if(IsValidSpell(buffs[x].spellid) && spells[buffs[x].spellid].buff_duration_formula != DF_Permanent) {
val++;
}
}
return val;
}
@@ -4930,9 +4927,9 @@ bool Mob::TryFadeEffect(int slot)
if(spell_id)
{
if(spell_id == SPELL_UNKNOWN)
if (!IsValidSpell(spell_id)) {
return false;
}
if(IsValidSpell(spell_id))
{
if (IsBeneficialSpell(spell_id)) {
@@ -5289,7 +5286,7 @@ void Mob::DoKnockback(Mob *caster, uint32 push_back, uint32 push_up)
void Mob::TrySpellOnKill(uint8 level, uint16 spell_id)
{
if (spell_id != SPELL_UNKNOWN)
if (IsValidSpell(spell_id))
{
if(IsEffectInSpell(spell_id, SE_ProcOnSpellKillShot)) {
for (int i = 0; i < EFFECT_COUNT; i++) {
@@ -5603,7 +5600,7 @@ void Mob::DoGravityEffect()
int buff_count = GetMaxTotalSlots();
for (int slot = 0; slot < buff_count; slot++)
{
if (buffs[slot].spellid != SPELL_UNKNOWN && IsEffectInSpell(buffs[slot].spellid, SE_GravityEffect))
if (IsValidSpell(buffs[slot].spellid) && IsEffectInSpell(buffs[slot].spellid, SE_GravityEffect))
{
for (int i = 0; i < EFFECT_COUNT; i++)
{
@@ -6174,19 +6171,20 @@ FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) {
bool Mob::HasSpellEffect(int effect_id)
{
int i;
int i;
int buff_count = GetMaxTotalSlots();
for(i = 0; i < buff_count; i++)
{
if(buffs[i].spellid == SPELL_UNKNOWN) { continue; }
int buff_count = GetMaxTotalSlots();
for(i = 0; i < buff_count; i++)
{
if (!IsValidSpell(buffs[i].spellid)) {
continue;
}
if(IsEffectInSpell(buffs[i].spellid, effect_id))
{
return(1);
}
}
return(0);
if (IsEffectInSpell(buffs[i].spellid, effect_id)) {
return(1);
}
}
return(0);
}
int Mob::GetSpecialAbility(int ability)
-39
View File
@@ -1323,45 +1323,6 @@ public:
inline uint32 GetEmoteID() { return emoteid; }
bool HasSpellEffect(int effect_id);
int mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob *caster, uint16 caster_id);
float mod_hit_chance(float chancetohit, EQ::skills::SkillType skillinuse, Mob *attacker);
float mod_riposte_chance(float ripostchance, Mob *attacker);
float mod_block_chance(float blockchance, Mob *attacker);
float mod_parry_chance(float parrychance, Mob *attacker);
float mod_dodge_chance(float dodgechance, Mob *attacker);
float mod_monk_weight(float monkweight, Mob *attacker);
float mod_mitigation_rating(float mitigation_rating, Mob *attacker);
float mod_attack_rating(float attack_rating, Mob *defender);
int64 mod_kick_damage(int64 dmg);
int64 mod_bash_damage(int64 dmg);
int64 mod_frenzy_damage(int64 dmg);
int64 mod_monk_special_damage(int64 ndamage, EQ::skills::SkillType skill_type);
int64 mod_backstab_damage(int64 ndamage);
int64 mod_archery_bonus_chance(int bonuschance, const EQ::ItemInstance *RangeWeapon);
uint64 mod_archery_bonus_damage(uint64 MaxDmg, const EQ::ItemInstance *RangeWeapon);
int64 mod_archery_damage(int64 TotalDmg, bool hasbonus, const EQ::ItemInstance *RangeWeapon);
uint64 mod_throwing_damage(uint64 MaxDmg);
int32 mod_cast_time(int32 cast_time);
int mod_buff_duration(int res, Mob *caster, Mob *target, uint16 spell_id);
int mod_spell_stack(uint16 spellid1, int caster_level1, Mob *caster1, uint16 spellid2, int caster_level2, Mob *caster2);
int mod_spell_resist(
int resist_chance,
int level_mod,
int resist_modifier,
int target_resist,
uint8 resist_type,
uint16 spell_id,
Mob *caster
);
void mod_spell_cast(
uint16 spell_id,
Mob *spelltar,
bool reflect,
bool use_resist_adjust,
int16 resist_adjust,
bool isproc
);
bool mod_will_aggro(Mob *attacker, Mob *on);
//Command #Tune functions
void TuneGetStats(Mob* defender, Mob *attacker);
-188
View File
@@ -1,188 +0,0 @@
#include "client.h"
#include "entity.h"
#include "mob.h"
#include "npc.h"
#include "worldserver.h"
#include "zone.h"
class Spawn2;
struct Consider_Struct;
struct DBTradeskillRecipe_Struct;
namespace EQ
{
class ItemInstance;
}
extern EntityList entity_list;
extern Zone* zone;
extern WorldServer worldserver;
//All functions that modify a value are passed the value as it was computed by default formulas and bonuses. In most cases this should be the final value that will be used.
//These are called when a zone boots or is repopped
void Zone::mod_init() { return; }
void Zone::mod_repop() { return; }
//Pre-spawn hook called from the NPC object to be spawned
void NPC::mod_prespawn(Spawn2 *sp) { return; }
//Base damage from NPC::Attack
int NPC::mod_npc_damage(int64 damage, EQ::skills::SkillType skillinuse, int hand, const EQ::ItemData* weapon, Mob* other) { return(damage); }
//Mob c has been given credit for a kill. This is called after the regular EVENT_KILLED_MERIT event.
void NPC::mod_npc_killed_merit(Mob* c) { return; }
//Mob oos has been given credit for a kill. This is called after the regular EVENT_DEATH event.
void NPC::mod_npc_killed(Mob* oos) { return; }
//Base damage from Client::Attack - can cover myriad skill types
int Client::mod_client_damage(int64 damage, EQ::skills::SkillType skillinuse, int hand, const EQ::ItemInstance* weapon, Mob* other) { return(damage); }
//message is char[4096], don't screw it up. Return true for normal behavior, false to return immediately.
// Channels:
// 0 - Guild Chat
// 2 - Group Chat
// 3 - Shout
// 4 - Auction
// 5 - Out of Character
// 6 - Broadcast
// 7 - Tell
// 8 - Say
// 11 - GMSay
// 15 - Raid Chat
// 20 - UCS Relay for UF client and later
// 22 - Emotes for UF and later
bool Client::mod_client_message(char* message, uint8 chan_num) { return(true); }
//Skillup override. When this is called the regular skillup check has failed. Return false to proceed with default behavior.
//This will NOT allow a client to increase skill past a cap.
bool Client::mod_can_increase_skill(EQ::skills::SkillType skillid, Mob* against_who) { return(false); }
//chance of general skill increase, rolled against 0-99 where higher chance is better.
double Client::mod_increase_skill_chance(double chance, Mob* against_who) { return(chance); }
//Max percent of health you can bind wound starting with default value for class, item, and AA bonuses
int Client::mod_bindwound_percent(int max_percent, Mob* bindmob) { return(max_percent); }
//Final bind HP value after bonuses
int Client::mod_bindwound_hp(int bindhps, Mob* bindmob) { return(bindhps); }
//Client haste as calculated by default formulas - In percent from 0-100
int Client::mod_client_haste(int h) { return(h); }
//Haste cap override
int Client::mod_client_haste_cap(int cap) { return(cap); }
//This is called when a client cons a mob
void Client::mod_consider(Mob* tmob, Consider_Struct* con) { return; }
//Return true to continue with normal behavior, false returns in the parent function
bool Client::mod_saylink(const std::string& response, bool silentsaylink) { return(true); }
//Client pet power as calculated by default formulas and bonuses
int16 Client::mod_pet_power(int16 act_power, uint16 spell_id) { return(act_power); }
//Chance to combine rolled against a random 0-99 where higher is better.
float Client::mod_tradeskill_chance(float chance, DBTradeskillRecipe_Struct *spec) { return(chance); }
//Chance to skillup rolled against a random 0-99 where higher is better.
float Client::mod_tradeskill_skillup(float chance_stage2) { return(chance_stage2); }
//Tribute value override
int32 Client::mod_tribute_item_value(int32 pts, const EQ::ItemInstance* item) { return(pts); }
//Death reporting
void Client::mod_client_death_npc(Mob* killerMob) { return; }
void Client::mod_client_death_duel(Mob* killerMob) { return; }
void Client::mod_client_death_env() { return; }
//Calculated xp before consider modifier, called whenever a client gets XP for killing a mob.
int64 Client::mod_client_xp(int64 in_xp, NPC *npc) { return(in_xp); }
//Client XP formula. Changes here will cause clients to change level after gaining or losing xp.
//Either modify this before your server goes live, or be prepared to write a quest script that fixes levels.
//To adjust how much XP is given per kill, use mod_client_xp
uint32 Client::mod_client_xp_for_level(uint32 xp, uint16 check_level) { return(xp); }
//Food and drink values as computed by consume requests. Return < 0 to abort the request.
int Client::mod_food_value(const EQ::ItemData *item, int change) { return(change); }
int Client::mod_drink_value(const EQ::ItemData *item, int change) { return(change); }
//effect_vallue - Spell effect value as calculated by default formulas. You will want to ignore effects that don't lend themselves to scaling - pet ID's, gate coords, etc.
int Mob::mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob* caster, uint16 caster_id) { return(effect_value); }
//chancetohit - 0 to 100 percent - set over 1000 for a guaranteed hit
float Mob::mod_hit_chance(float chancetohit, EQ::skills::SkillType skillinuse, Mob* attacker) { return(chancetohit); }
//Final riposte chance
float Mob::mod_riposte_chance(float ripostechance, Mob* attacker) { return(ripostechance); }
//Final block chance
float Mob::mod_block_chance(float blockchance, Mob* attacker) { return(blockchance); }
//Final parry chance
float Mob::mod_parry_chance(float parrychance, Mob* attacker) { return(parrychance); }
//Final dodge chance
float Mob::mod_dodge_chance(float dodgechance, Mob* attacker) { return(dodgechance); }
//Usually 15, a monk under this weight threshold gets an AC bonus
float Mob::mod_monk_weight(float monkweight, Mob* attacker) { return(monkweight); }
//Mitigation rating is compared to incoming attack rating. Higher is better.
float Mob::mod_mitigation_rating(float mitigation_rating, Mob* attacker) { return(mitigation_rating); }
float Mob::mod_attack_rating(float attack_rating, Mob* defender) { return(attack_rating); }
//Kick damage after all other bonuses are applied
int64 Mob::mod_kick_damage(int64 dmg) { return(dmg); }
//Slam and bash damage after all other bonuses are applied
int64 Mob::mod_bash_damage(int64 dmg) { return(dmg); }
//Frenzy damage after all other bonuses are applied
int64 Mob::mod_frenzy_damage(int64 dmg) { return(dmg); }
//Special attack damage after all other bonuses are applied.
int64 Mob::mod_monk_special_damage(int64 ndamage, EQ::skills::SkillType skill_type) { return(ndamage); }
//ndamage - Backstab damage as calculated by default formulas
int64 Mob::mod_backstab_damage(int64 ndamage) { return(ndamage); }
//Chance for 50+ archery bonus damage if Combat:UseArcheryBonusRoll is true.
int64 Mob::mod_archery_bonus_chance(int bonuschance, const EQ::ItemInstance* RangeWeapon) { return(bonuschance); }
//Archery bonus damage
uint64 Mob::mod_archery_bonus_damage(uint64 MaxDmg, const EQ::ItemInstance* RangeWeapon) { return(MaxDmg); }
//Final archery damage including bonus if it was applied.
int64 Mob::mod_archery_damage(int64 TotalDmg, bool hasbonus, const EQ::ItemInstance* RangeWeapon) { return(TotalDmg); }
//Thrown weapon damage after all other calcs
uint64 Mob::mod_throwing_damage(uint64 MaxDmg) { return(MaxDmg); }
//Spell cast time in milliseconds - will not sync with client cast time bar, but does work.
int32 Mob::mod_cast_time(int32 cast_time) { return(cast_time); }
//res - Default buff duration formula
int Mob::mod_buff_duration(int res, Mob* caster, Mob* target, uint16 spell_id) { return(res); }
//Spell stack override - If this returns anything < 2, it will ignore all other stacking rules.
// See spells.cpp: Mob::CheckStackConflict
// 0 - No conflict
// 1 - Overwrite, spellid1 is replaced by spellid2
// -1 - Blocked, spellid2 will not land
// 2 - Default stacking behavior
int Mob::mod_spell_stack(uint16 spellid1, int caster_level1, Mob* caster1, uint16 spellid2, int caster_level2, Mob* caster2) { return(2); }
//Sum of various resists rolled against a value of 200.
int Mob::mod_spell_resist(int resist_chance, int level_mod, int resist_modifier, int target_resist, uint8 resist_type, uint16 spell_id, Mob* caster) {
return(resist_chance);
}
//Spell is cast by this on spelltar, called from spellontarget after the event_cast_on NPC event
void Mob::mod_spell_cast(uint16 spell_id, Mob* spelltar, bool reflect, bool use_resist_adjust, int16 resist_adjust, bool isproc) { return; }
//At this point all applicable aggro checks have succeeded. Attacker should aggro unless we return false.
bool Mob::mod_will_aggro(Mob *attacker, Mob *on) { return(true); }
-187
View File
@@ -1,187 +0,0 @@
#include "../common/debug.h"
#include "../common/timer.h"
#include <cmath>
#include <stdlib.h>
#include "spawn2.h"
#include "entity.h"
#include "masterentity.h"
#include "zone.h"
#include "spawngroup.h"
#include "zonedb.h"
#include "../common/zone_store.h"
#include "npc.h"
#include "mob.h"
#include "client.h"
#include "worldserver.h"
#include "quest_parser_collection.h"
#include "event_codes.h"
#include "embparser.h"
#include <string>
#include <iostream>
extern EntityList entity_list;
extern Zone* zone;
extern WorldServer worldserver;
//All functions that modify a value are passed the value as it was computed by default formulas and bonuses. In most cases this should be the final value that will be used.
//These are called when a zone boots or is repopped
void Zone::mod_init() { return; }
void Zone::mod_repop() { return; }
//Pre-spawn hook called from the NPC object to be spawned
void NPC::mod_prespawn(Spawn2 *sp) { return; }
//Base damage from NPC::Attack
int NPC::mod_npc_damage(int64 damage, SkillType skillinuse, int hand, const Item_Struct* weapon, Mob* other) { return(damage); }
//Mob c has been given credit for a kill. This is called after the regular EVENT_KILLED_MERIT event.
void NPC::mod_npc_killed_merit(Mob* c) { return; }
//Mob oos has been given credit for a kill. This is called after the regular EVENT_DEATH event.
void NPC::mod_npc_killed(Mob* oos) { return; }
//Base damage from Client::Attack - can cover myriad skill types
int Client::mod_client_damage(int64 damage, SkillType skillinuse, int hand, const ItemInst* weapon, Mob* other) { return(damage); }
//message is char[4096], don't screw it up. Return true for normal behavior, false to return immediately.
// Channels:
// 0 - Guild Chat
// 2 - Group Chat
// 3 - Shout
// 4 - Auction
// 5 - Out of Character
// 6 - Broadcast
// 7 - Tell
// 8 - Say
// 11 - GMSay
// 15 - Raid Chat
// 20 - UCS Relay for UF client and later
// 22 - Emotes for UF and later
bool Client::mod_client_message(char* message, uint8 chan_num) { return(true); }
//Skillup override. When this is called the regular skillup check has failed. Return false to proceed with default behavior.
//This will NOT allow a client to increase skill past a cap.
bool Client::mod_can_increase_skill(SkillType skillid, Mob* against_who) { return(false); }
//chance of general skill increase, rolled against 0-99 where higher chance is better.
int16 Client::mod_increase_skill_chance(int16 chance, Mob* against_who) { return(chance); }
//Max percent of health you can bind wound starting with default value for class, item, and AA bonuses
int Client::mod_bindwound_percent(int max_percent, Mob* bindmob) { return(max_percent); }
//Final bind HP value after bonuses
int Client::mod_bindwound_hp(int bindhps, Mob* bindmob) { return(bindhps); }
//Client haste as calculated by default formulas - In percent from 0-100
int Client::mod_client_haste(int h) { return(h); }
//Haste cap override
int Client::mod_client_haste_cap(int cap) { return(cap); }
//This is called when a client cons a mob
void Client::mod_consider(Mob* tmob, Consider_Struct* con) { return; }
//Return true to continue with normal behavior, false returns in the parent function
bool Client::mod_saylink(const std::string& response, bool silentsaylink) { return(true); }
//Client pet power as calculated by default formulas and bonuses
int16 Client::mod_pet_power(int16 act_power, uint16 spell_id) { return(act_power); }
//Chance to combine rolled against a random 0-99 where higher is better.
float Client::mod_tradeskill_chance(float chance, DBTradeskillRecipe_Struct *spec) { return(chance); }
//Chance to skillup rolled against a random 0-99 where higher is better.
float Client::mod_tradeskill_skillup(float chance_stage2) { return(chance_stage2); }
//Tribute value override
int32 Client::mod_tribute_item_value(int32 pts, const ItemInst* item) { return(pts); }
//Death reporting
void Client::mod_client_death_npc(Mob* killerMob) { return; }
void Client::mod_client_death_duel(Mob* killerMob) { return; }
void Client::mod_client_death_env() { return; }
//Calculated xp before consider modifier, called whenever a client gets XP for killing a mob.
int64 Client::mod_client_xp(int64 in_xp, NPC *npc) { return(in_xp); }
//Client XP formula. Changes here will cause clients to change level after gaining or losing xp.
//Either modify this before your server goes live, or be prepared to write a quest script that fixes levels.
//To adjust how much XP is given per kill, use mod_client_xp
uint32 Client::mod_client_xp_for_level(uint32 xp, uint16 check_level) { return(xp); }
//effect_vallue - Spell effect value as calculated by default formulas. You will want to ignore effects that don't lend themselves to scaling - pet ID's, gate coords, etc.
int Mob::mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob* caster) { return(effect_value); }
//chancetohit - 0 to 100 percent - set over 1000 for a guaranteed hit
float Mob::mod_hit_chance(float chancetohit, SkillType skillinuse, Mob* attacker) { return(chancetohit); }
//Final riposte chance
float Mob::mod_riposte_chance(float ripostechance, Mob* attacker) { return(ripostechance); }
//Final block chance
float Mob::mod_block_chance(float blockchance, Mob* attacker) { return(blockchance); }
//Final parry chance
float Mob::mod_parry_chance(float parrychance, Mob* attacker) { return(parrychance); }
//Final dodge chance
float Mob::mod_dodge_chance(float dodgechance, Mob* attacker) { return(dodgechance); }
//Usually 15, a monk under this weight threshold gets an AC bonus
float Mob::mod_monk_weight(float monkweight, Mob* attacker) { return(monkweight); }
//Mitigation rating is compared to incoming attack rating. Higher is better.
float Mob::mod_mitigation_rating(float mitigation_rating, Mob* attacker) { return(mitigation_rating); }
float Mob::mod_attack_rating(float attack_rating, Mob* defender) { return(attack_rating); }
//Kick damage after all other bonuses are applied
int64 Mob::mod_kick_damage(int64 dmg) { return(dmg); }
//Slam and bash damage after all other bonuses are applied
int64 Mob::mod_bash_damage(int64 dmg) { return(dmg); }
//Frenzy damage after all other bonuses are applied
int64 Mob::mod_frenzy_damage(int64 dmg) { return(dmg); }
//Special attack damage after all other bonuses are applied.
int64 Mob::mod_monk_special_damage(int64 ndamage, EQ::skills::SkillType skill_type) { return(ndamage); }
//ndamage - Backstab damage as calculated by default formulas
int64 Mob::mod_backstab_damage(int64 ndamage) { return(ndamage); }
//Chance for 50+ archery bonus damage if Combat:UseArcheryBonusRoll is true.
int64 Mob::mod_archery_bonus_chance(int bonuschance, const ItemInst* RangeWeapon) { return(bonuschance); }
//Archery bonus damage
uint64 Mob::mod_archery_bonus_damage(uint64 MaxDmg, const ItemInst* RangeWeapon) { return(MaxDmg); }
//Final archery damage including bonus if it was applied.
int64 Mob::mod_archery_damage(int64 TotalDmg, bool hasbonus, const ItemInst* RangeWeapon) { return(TotalDmg); }
//Thrown weapon damage after all other calcs
uint64 Mob::mod_throwing_damage(uint64 MaxDmg) { return(MaxDmg); }
//Spell cast time in milliseconds - will not sync with client cast time bar, but does work.
int32 Mob::mod_cast_time(int32 cast_time) { return(cast_time); }
//res - Default buff duration formula
int Mob::mod_buff_duration(int res, Mob* caster, Mob* target, uint16 spell_id) { return(res); }
//Spell stack override - If this returns anything < 2, it will ignore all other stacking rules.
// See spells.cpp: Mob::CheckStackConflict
// 0 - No conflict
// 1 - Overwrite, spellid1 is replaced by spellid2
// -1 - Blocked, spellid2 will not land
// 2 - Default stacking behavior
int Mob::mod_spell_stack(uint16 spellid1, int caster_level1, Mob* caster1, uint16 spellid2, int caster_level2, Mob* caster2) { return(2); }
//Sum of various resists rolled against a value of 200.
int Mob::mod_spell_resist(int resist_chance, int level_mod, int resist_modifier, int target_resist, uint8 resist_type, uint16 spell_id, Mob* caster) {
int final = resist_chance + level_mod + resist_modifier + target_resist;
return(final);
}
//Spell is cast by this on spelltar, called from spellontarget after the event_cast_on NPC event
void Mob::mod_spell_cast(uint16 spell_id, Mob* spelltar, bool reflect, bool use_resist_adjust, int16 resist_adjust, bool isproc) { return; }
-4
View File
@@ -477,10 +477,6 @@ public:
uint32 GetSpawnKillCount();
int GetScore();
void mod_prespawn(Spawn2 *sp);
int mod_npc_damage(int64 damage, EQ::skills::SkillType skillinuse, int hand, const EQ::ItemData* weapon, Mob* other);
void mod_npc_killed_merit(Mob* c);
void mod_npc_killed(Mob* oos);
void AISpellsList(Client *c);
uint16 GetInnateProcSpellID() const { return innate_proc_spell_id; }
+45 -8
View File
@@ -71,6 +71,8 @@ Object::Object(uint32 id, uint32 type, uint32 icon, const Object_Struct& object,
m_data.size = object.size;
m_data.tilt_x = object.tilt_x;
m_data.tilt_y = object.tilt_y;
FixZ();
}
//creating a re-ocurring ground spawn.
@@ -100,6 +102,8 @@ Object::Object(const EQ::ItemInstance* inst, char* name,float max_x,float min_x,
strcpy(m_data.object_name, name);
RandomSpawn(false);
FixZ();
// Hardcoded portion for unknown members
m_data.unknown024 = 0x7f001194;
m_data.unknown076 = 0x0000d5fe;
@@ -167,6 +171,8 @@ Object::Object(Client* client, const EQ::ItemInstance* inst)
strcpy(m_data.object_name, DEFAULT_OBJECT_NAME);
}
}
FixZ();
}
Object::Object(const EQ::ItemInstance *inst, float x, float y, float z, float heading, uint32 decay_time)
@@ -223,6 +229,8 @@ Object::Object(const EQ::ItemInstance *inst, float x, float y, float z, float he
strcpy(m_data.object_name, DEFAULT_OBJECT_NAME);
}
}
FixZ();
}
Object::Object(const char *model, float x, float y, float z, float heading, uint8 type, uint32 decay_time)
@@ -247,6 +255,8 @@ Object::Object(const char *model, float x, float y, float z, float heading, uint
m_data.z = z;
m_data.zone_id = zone->GetZoneID();
FixZ();
if (decay_time)
decay_timer.Start();
@@ -493,21 +503,25 @@ void Object::RandomSpawn(bool send_packet) {
bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object)
{
if(m_ground_spawn){//This is a Cool Groundspawn
if(m_ground_spawn) {//This is a Cool Groundspawn
respawn_timer.Start();
}
if (m_type == OT_DROPPEDITEM) {
bool cursordelete = false;
bool duplicate_lore = false;
if (m_inst && sender) {
// if there is a lore conflict, delete the offending item from the server inventory
// the client updates itself and takes care of sending "duplicate lore item" messages
auto item = m_inst->GetItem();
if(sender->CheckLoreConflict(item)) {
if (sender->CheckLoreConflict(item)) {
duplicate_lore = true;
int16 loreslot = sender->GetInv().HasItem(item->ID, 0, invWhereBank);
if (loreslot != INVALID_INDEX) // if the duplicate is in the bank, delete it.
if (loreslot != INVALID_INDEX) { // if the duplicate is in the bank, delete it.
sender->DeleteItemInInventory(loreslot);
else
cursordelete = true; // otherwise, we delete the new one
}
else {
cursordelete = true;
} // otherwise, we delete the new one
}
if (item->RecastDelay) {
@@ -560,8 +574,9 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object)
sender->DiscoverItem(item->ID);
}
if(cursordelete) // delete the item if it's a duplicate lore. We have to do this because the client expects the item packet
sender->DeleteItemInInventory(EQ::invslot::slotCursor);
if (cursordelete) { // delete the item if it's a duplicate lore. We have to do this because the client expects the item packet
sender->DeleteItemInInventory(EQ::invslot::slotCursor, 1, true);
}
sender->DropItemQS(m_inst, true);
@@ -581,8 +596,18 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object)
// Remove object
content_db.DeleteObject(m_id);
if(!m_ground_spawn)
if (!m_ground_spawn) {
entity_list.RemoveEntity(GetID());
}
// we have to delete the entity on click or the client desyncs
// this is a way to immediately respawn the groundspawn after killing it and
// deleting the item from the player
// I believe older clients somehow sent this automatically but we are no longer with ROF2+
if (duplicate_lore) {
sender->Message(Chat::Yellow, "Duplicate lore item detected");
respawn_timer.Trigger();
}
} else {
// Tradeskill item
auto outapp = new EQApplicationPacket(OP_ClickObjectAction, sizeof(ClickObjectAction_Struct));
@@ -1157,3 +1182,15 @@ void Object::SetEntityVariable(std::string variable_name, std::string variable_v
o_EntityVariables[variable_name] = variable_value;
}
void Object::FixZ()
{
float best_z = BEST_Z_INVALID;
if (zone->zonemap != nullptr) {
auto dest = glm::vec3(m_data.x, m_data.y, m_data.z + 5);
best_z = zone->zonemap->FindBestZ(dest, nullptr);
if (best_z != BEST_Z_INVALID) {
m_data.z = best_z;
}
}
}
+1
View File
@@ -204,6 +204,7 @@ protected:
Timer respawn_timer;
Timer decay_timer;
void FixZ();
};
#endif
+3 -1
View File
@@ -898,7 +898,9 @@ void perl_register_npc()
package.add("SetSaveWaypoint", &Perl_NPC_SetSaveWaypoint);
package.add("SetSecSkill", &Perl_NPC_SetSecSkill);
package.add("SetSilver", &Perl_NPC_SetSilver);
package.add("SetSimpleRoamBox", &Perl_NPC_SetSimpleRoamBox);
package.add("SetSimpleRoamBox", (void(*)(NPC*, float))&Perl_NPC_SetSimpleRoamBox);
package.add("SetSimpleRoamBox", (void(*)(NPC*, float, float))&Perl_NPC_SetSimpleRoamBox);
package.add("SetSimpleRoamBox", (void(*)(NPC*, float, float, int))&Perl_NPC_SetSimpleRoamBox);
package.add("SetSp2", &Perl_NPC_SetSp2);
package.add("SetSpellFocusDMG", &Perl_NPC_SetSpellFocusDMG);
package.add("SetSpellFocusHeal", &Perl_NPC_SetSpellFocusHeal);
+2 -3
View File
@@ -194,7 +194,6 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
if (petpower == -1) {
if (IsClient()) {
act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);//Client only
act_power = CastToClient()->mod_pet_power(act_power, spell_id);
}
else if (IsBot())
act_power = CastToBot()->GetFocusEffect(focusPetPower, spell_id);
@@ -575,7 +574,7 @@ void NPC::GetPetState(SpellBuff_Struct *pet_buffs, uint32 *items, char *name) {
//save their buffs.
for (int i=EQ::invslot::EQUIPMENT_BEGIN; i < GetPetMaxTotalSlots(); i++) {
if (buffs[i].spellid != SPELL_UNKNOWN) {
if (IsValidSpell(buffs[i].spellid)) {
pet_buffs[i].spellid = buffs[i].spellid;
pet_buffs[i].effect_type = i+1;
pet_buffs[i].duration = buffs[i].ticsremaining;
@@ -601,7 +600,7 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) {
for (i = 0; i < GetPetMaxTotalSlots(); i++) {
for(int z = 0; z < GetPetMaxTotalSlots(); z++) {
// check for duplicates
if(buffs[z].spellid != SPELL_UNKNOWN && buffs[z].spellid == pet_buffs[i].spellid) {
if(IsValidSpell(buffs[z].spellid) && buffs[z].spellid == pet_buffs[i].spellid) {
buffs[z].spellid = SPELL_UNKNOWN;
pet_buffs[i].spellid = 0xFFFFFFFF;
}
+48 -42
View File
@@ -4024,10 +4024,8 @@ void QuestManager::SendPlayerHandinEvent() {
if (!handin_items.empty()) {
if (Strings::Contains(handin_items, ",")) {
const auto handin_data = Strings::Split(handin_items, ",");
for (const auto &h: handin_data) {
const auto item_data = Strings::Split(h, "-");
const auto item_data = Strings::Split(h, "|");
if (
item_data.size() == 3 &&
Strings::IsNumber(item_data[0]) &&
@@ -4037,22 +4035,22 @@ void QuestManager::SendPlayerHandinEvent() {
const auto item_id = static_cast<uint32>(std::stoul(item_data[0]));
if (item_id != 0) {
const auto *item = database.GetItem(item_id);
hi.emplace_back(
PlayerEvent::HandinEntry{
.item_id = item_id,
.item_name = item->Name,
.charges = static_cast<uint16>(std::stoul(item_data[1])),
.attuned = std::stoi(item_data[2]) ? true : false
}
);
if (item) {
hi.emplace_back(
PlayerEvent::HandinEntry{
.item_id = item_id,
.item_name = item->Name,
.charges = static_cast<uint16>(std::stoul(item_data[1])),
.attuned = std::stoi(item_data[2]) ? true : false
}
);
}
}
}
}
}
else if (Strings::Contains(handin_items, "|")) {
const auto item_data = Strings::Split(handin_items, "|");
if (
item_data.size() == 3 &&
Strings::IsNumber(item_data[0]) &&
@@ -4061,15 +4059,16 @@ void QuestManager::SendPlayerHandinEvent() {
) {
const auto item_id = static_cast<uint32>(std::stoul(item_data[0]));
const auto *item = database.GetItem(item_id);
hi.emplace_back(
PlayerEvent::HandinEntry{
.item_id = item_id,
.item_name = item->Name,
.charges = static_cast<uint16>(std::stoul(item_data[1])),
.attuned = std::stoi(item_data[2]) ? true : false
}
);
if (item) {
hi.emplace_back(
PlayerEvent::HandinEntry{
.item_id = item_id,
.item_name = item->Name,
.charges = static_cast<uint16>(std::stoul(item_data[1])),
.attuned = std::stoi(item_data[2]) ? true : false
}
);
}
}
}
}
@@ -4087,10 +4086,8 @@ void QuestManager::SendPlayerHandinEvent() {
if (!return_items.empty()) {
if (Strings::Contains(return_items, ",")) {
const auto return_data = Strings::Split(return_items, ",");
for (const auto &r: return_data) {
const auto item_data = Strings::Split(r, "|");
if (
item_data.size() == 3 &&
Strings::IsNumber(item_data[0]) &&
@@ -4100,20 +4097,21 @@ void QuestManager::SendPlayerHandinEvent() {
const auto item_id = static_cast<uint32>(std::stoul(item_data[0]));
const auto *item = database.GetItem(item_id);
ri.emplace_back(
PlayerEvent::HandinEntry{
.item_id = item_id,
.item_name = item->Name,
.charges = static_cast<uint16>(std::stoul(item_data[1])),
.attuned = std::stoi(item_data[2]) ? true : false
}
);
if (item) {
ri.emplace_back(
PlayerEvent::HandinEntry{
.item_id = item_id,
.item_name = item->Name,
.charges = static_cast<uint16>(std::stoul(item_data[1])),
.attuned = std::stoi(item_data[2]) ? true : false
}
);
}
}
}
}
else if (Strings::Contains(return_items, "|")) {
const auto item_data = Strings::Split(return_items, "|");
if (
item_data.size() == 3 &&
Strings::IsNumber(item_data[0]) &&
@@ -4123,14 +4121,16 @@ void QuestManager::SendPlayerHandinEvent() {
const auto item_id = static_cast<uint32>(std::stoul(item_data[0]));
const auto *item = database.GetItem(item_id);
ri.emplace_back(
PlayerEvent::HandinEntry{
.item_id = item_id,
.item_name = item->Name,
.charges = static_cast<uint16>(std::stoul(item_data[1])),
.attuned = std::stoi(item_data[2]) ? true : false
}
);
if (item) {
ri.emplace_back(
PlayerEvent::HandinEntry{
.item_id = item_id,
.item_name = item->Name,
.charges = static_cast<uint16>(std::stoul(item_data[1])),
.attuned = std::stoi(item_data[2]) ? true : false
}
);
}
}
}
}
@@ -4149,7 +4149,13 @@ void QuestManager::SendPlayerHandinEvent() {
initiator->DeleteEntityVariable("RETURN_ITEMS");
initiator->DeleteEntityVariable("RETURN_MONEY");
if (player_event_logs.IsEventEnabled(PlayerEvent::NPC_HANDIN)) {
bool handed_in_money = hm.platinum > 0 || hm.gold > 0 || hm.silver > 0 || hm.copper > 0;
bool event_has_data_to_record = (
!hi.empty() || handed_in_money
);
if (player_event_logs.IsEventEnabled(PlayerEvent::NPC_HANDIN) && event_has_data_to_record) {
auto e = PlayerEvent::HandinEvent{
.npc_id = owner->CastToNPC()->GetNPCTypeID(),
.npc_name = owner->GetCleanName(),
-2
View File
@@ -260,8 +260,6 @@ bool Spawn2::Process() {
NPC *npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), GravityBehavior::Water);
npc->mod_prespawn(this);
npcthis = npc;
npc->AddLootTable();
if (npc->DropsGlobalLoot()) {
-5
View File
@@ -332,8 +332,6 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk)
int32 max_dmg = GetBaseSkillDamage(EQ::skills::SkillFrenzy, GetTarget());
DoAnim(anim1HWeapon, 0, false);
max_dmg = mod_frenzy_damage(max_dmg);
if (GetClass() == BERSERKER) {
int chance = GetLevel() * 2 + GetSkill(EQ::skills::SkillFrenzy);
if (zone->random.Roll0(450) < chance)
@@ -528,9 +526,6 @@ int Mob::MonkSpecialAttack(Mob *other, uint8 unchecked_type)
if (max_dmg > 0)
ht = max_dmg;
// This can potentially stack with changes to kick damage
ht = ndamage = mod_monk_special_damage(ndamage, skill_type);
DoSpecialAttackDamage(other, skill_type, max_dmg, min_dmg, ht, reuse);
return reuse;
+20 -20
View File
@@ -27,6 +27,7 @@
#include "../common/data_verification.h"
#include "../common/misc_functions.h"
#include "bot.h"
#include "quest_parser_collection.h"
#include "lua_parser.h"
#include "string_ids.h"
@@ -1078,7 +1079,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
}
int buff_count = GetMaxTotalSlots();
for(int slot = 0; slot < buff_count; slot++) {
if( buffs[slot].spellid != SPELL_UNKNOWN &&
if(IsValidSpell(buffs[slot].spellid) &&
spells[buffs[slot].spellid].dispel_flag == 0 &&
!IsDiscipline(buffs[slot].spellid))
{
@@ -1109,7 +1110,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
int chance = spells[spell_id].base_value[i];
int buff_count = GetMaxTotalSlots();
for(int slot = 0; slot < buff_count; slot++) {
if (buffs[slot].spellid != SPELL_UNKNOWN &&
if (IsValidSpell(buffs[slot].spellid) &&
IsDetrimentalSpell(buffs[slot].spellid) &&
spells[buffs[slot].spellid].dispel_flag == 0)
{
@@ -1136,7 +1137,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
int chance = spells[spell_id].base_value[i];
int buff_count = GetMaxTotalSlots();
for(int slot = 0; slot < buff_count; slot++) {
if (buffs[slot].spellid != SPELL_UNKNOWN &&
if (IsValidSpell(buffs[slot].spellid) &&
IsBeneficialSpell(buffs[slot].spellid) &&
spells[buffs[slot].spellid].dispel_flag == 0)
{
@@ -1335,7 +1336,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
if (BeneficialSpell(spell_id) && spells[spell_id].buff_duration == 0) {
int buff_count = GetMaxBuffSlots();
for (int slot = 0; slot < buff_count; slot++) {
if (buffs[slot].spellid != SPELL_UNKNOWN && IsEffectInSpell(buffs[slot].spellid, SE_Blind)) {
if (IsValidSpell(buffs[slot].spellid) && IsEffectInSpell(buffs[slot].spellid, SE_Blind)) {
if (caster && TryDispel(caster->GetCasterLevel(spell_id), buffs[slot].casterlevel, 1)) {
BuffFadeBySlot(slot);
slot = buff_count;
@@ -3427,8 +3428,6 @@ int64 Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level
}
}
effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effect_id[effect_id], caster, caster_id);
return effect_value;
}
@@ -3756,12 +3755,13 @@ void Mob::BuffProcess()
for (int buffs_i = 0; buffs_i < buff_count; ++buffs_i)
{
if (buffs[buffs_i].spellid != SPELL_UNKNOWN)
if (IsValidSpell(buffs[buffs_i].spellid))
{
DoBuffTic(buffs[buffs_i], buffs_i, entity_list.GetMob(buffs[buffs_i].casterid));
// If the Mob died during DoBuffTic, then the buff we are currently processing will have been removed
if(buffs[buffs_i].spellid == SPELL_UNKNOWN)
if(!IsValidSpell(buffs[buffs_i].spellid)) {
continue;
}
// DF_Permanent uses -1 DF_Aura uses -4 but we need to check negatives for some spells for some reason?
if (spells[buffs[buffs_i].spellid].buff_duration_formula != DF_Permanent &&
@@ -6165,12 +6165,12 @@ void Mob::TryTriggerOnCastFocusEffect(focusType type, uint16 spell_id)
int32 proc_spellid = 0;
// item focus
if (IsClient() && itembonuses.FocusEffects[type]) {
if (IsOfClientBot() && itembonuses.FocusEffects[type]) {
const EQ::ItemData *temp_item = nullptr;
for (int x = EQ::invslot::EQUIPMENT_BEGIN; x <= EQ::invslot::EQUIPMENT_END; x++) {
temp_item = nullptr;
EQ::ItemInstance *ins = CastToClient()->GetInv().GetItem(x);
EQ::ItemInstance const *ins = (IsClient()) ? CastToClient()->GetInv().GetItem(x) : CastToBot()->GetBotItem(x);
if (!ins) {
continue;
}
@@ -6230,7 +6230,7 @@ void Mob::TryTriggerOnCastFocusEffect(focusType type, uint16 spell_id)
}
// Only use one of this focus per AA effect.
if (IsClient() && aabonuses.FocusEffects[type]) {
if (IsOfClientBot() && aabonuses.FocusEffects[type]) {
for (const auto &aa : aa_ranks) {
auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa.first, aa.second.first);
auto ability = ability_rank.first;
@@ -6244,7 +6244,7 @@ void Mob::TryTriggerOnCastFocusEffect(focusType type, uint16 spell_id)
continue;
}
proc_spellid = CastToClient()->CalcAAFocus(type, *rank, spell_id);
proc_spellid = CalcAAFocus(type, *rank, spell_id);
if (proc_spellid) {
TryTriggerOnCastProc(0, spell_id, proc_spellid);
}
@@ -6430,7 +6430,7 @@ int64 Mob::GetFocusEffect(focusType type, uint16 spell_id, Mob *caster, bool fro
}
TempItem = ins->GetItem();
if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) {
if (TempItem && IsValidSpell(TempItem->Focus.Effect)) {
if(rand_effectiveness) {
focus_max = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id, true);
if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) {
@@ -6462,7 +6462,7 @@ int64 Mob::GetFocusEffect(focusType type, uint16 spell_id, Mob *caster, bool fro
aug = ins->GetAugment(y);
if (aug) {
const EQ::ItemData* TempItemAug = aug->GetItem();
if (TempItemAug && TempItemAug->Focus.Effect > 0 && TempItemAug->Focus.Effect != SPELL_UNKNOWN) {
if (TempItemAug && IsValidSpell(TempItemAug->Focus.Effect)) {
if(rand_effectiveness) {
focus_max = CalcFocusEffect(type, TempItemAug->Focus.Effect, spell_id, true);
if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) {
@@ -6502,7 +6502,7 @@ int64 Mob::GetFocusEffect(focusType type, uint16 spell_id, Mob *caster, bool fro
}
TempItem = ins->GetItem();
if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) {
if (TempItem && IsValidSpell(TempItem->Focus.Effect)) {
if (rand_effectiveness) {
focus_max = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id, true);
if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) {
@@ -6712,7 +6712,7 @@ int64 NPC::GetFocusEffect(focusType type, uint16 spell_id, Mob* caster, bool fro
TempItem = cur;
if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) {
if (TempItem && IsValidSpell(TempItem->Focus.Effect)) {
if(rand_effectiveness) {
focus_max = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id, true);
if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) {
@@ -9755,7 +9755,7 @@ bool Mob::PassLimitToSkill(EQ::skills::SkillType skill, int32 spell_id, int proc
if (!aa_id && spellbonuses.LimitToSkill[EQ::skills::HIGHEST_SKILL + 2]) {
if (spell_id == SPELL_UNKNOWN) {
if (!IsValidSpell(spell_id)) {
return false;
}
@@ -10362,13 +10362,13 @@ void Mob::SetBuffDuration(int spell_id, int duration) {
for (int slot = 0; slot < buff_count; slot++) {
if (!adjust_all_buffs) {
if (buffs[slot].spellid != SPELL_UNKNOWN && buffs[slot].spellid == spell_id) {
if (IsValidSpell(buffs[slot].spellid) && buffs[slot].spellid == spell_id) {
SpellOnTarget(buffs[slot].spellid, this, 0, false, 0, false, -1, duration, true);
return;
}
}
else {
if (buffs[slot].spellid != SPELL_UNKNOWN) {
if (IsValidSpell(buffs[slot].spellid)) {
SpellOnTarget(buffs[slot].spellid, this, 0, false, 0, false, -1, duration, true);
}
}
@@ -10410,7 +10410,7 @@ int Mob::GetBuffStatValueBySpell(int32 spell_id, const char* stat_identifier)
int buff_count = GetMaxTotalSlots();
for (int slot = 0; slot < buff_count; slot++) {
if (buffs[slot].spellid != SPELL_UNKNOWN && buffs[slot].spellid == spell_id) {
if (IsValidSpell(buffs[slot].spellid) && buffs[slot].spellid == spell_id) {
return GetBuffStatValueBySlot(slot, stat_identifier);
}
}
+32 -38
View File
@@ -478,8 +478,6 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
return(true);
}
cast_time = mod_cast_time(cast_time);
// ok we know it has a cast time so we can start the timer now
spellend_timer.Start(cast_time);
@@ -1146,8 +1144,9 @@ void Mob::ZeroBardPulseVars()
void Mob::InterruptSpell(uint16 spellid)
{
if (spellid == SPELL_UNKNOWN)
if (!IsValidSpell(spellid)) {
spellid = casting_spell_id;
}
InterruptSpell(0, 0x121, spellid);
}
@@ -1158,7 +1157,7 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
EQApplicationPacket *outapp = nullptr;
uint16 message_other;
bool bard_song_mode = false; //has the bard song gone to auto repeat mode
if (spellid == SPELL_UNKNOWN) {
if (!IsValidSpell(spellid)) {
if(bardsong) {
spellid = bardsong;
bard_song_mode = true;
@@ -2819,9 +2818,6 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste
res = 10000; // ~16h override
}
res = mod_buff_duration(res, caster, target, spell_id);
LogSpells("Spell [{}]: Casting level [{}], formula [{}], base_duration [{}]: result [{}]",
spell_id, castlevel, formula, duration, res);
@@ -2946,9 +2942,6 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
}
}
int modval = mod_spell_stack(spellid1, caster_level1, caster1, spellid2, caster_level2, caster2);
if(modval < 2) { return(modval); }
/*
One of these is a bard song and one isn't and they're both beneficial so they should stack.
*/
@@ -3200,8 +3193,10 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
// 66+ Group Spells 62, Single Target 61
bool Mob::CheckSpellLevelRestriction(Mob *caster, uint16 spell_id)
{
bool check_for_restrictions = false;
bool can_cast = true;
if (spells[spell_id].target_type == ST_Self) {
LogSpells("[CheckSpellLevelRestriction] Self Only spell - no restrictions");
return true;
}
if (!caster) {
LogSpells("[CheckSpellLevelRestriction] No caster");
@@ -3213,6 +3208,9 @@ bool Mob::CheckSpellLevelRestriction(Mob *caster, uint16 spell_id)
return true;
}
bool check_for_restrictions = false;
bool can_cast = true;
// NON GM clients might be restricted by rule setting
if (caster->IsClient()) {
if (IsClient()) { // Only restrict client on client for this rule
@@ -3287,7 +3285,7 @@ uint32 Client::GetLastBuffSlot(bool disc, bool song)
bool Mob::HasDiscBuff()
{
int slot = GetFirstBuffSlot(true, false);
return buffs[slot].spellid != SPELL_UNKNOWN;
return IsValidSpell(buffs[slot].spellid);
}
// returns the slot the buff was added to, -1 if it wasn't added due to
@@ -3332,7 +3330,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
for (buffslot = 0; buffslot < buff_count; buffslot++) {
const Buffs_Struct &curbuf = buffs[buffslot];
if (curbuf.spellid != SPELL_UNKNOWN) {
if (IsValidSpell(curbuf.spellid)) {
// there's a buff in this slot
ret = CheckStackConflict(curbuf.spellid, curbuf.casterlevel, spell_id,
caster_level, entity_list.GetMobID(curbuf.casterid), caster, buffslot);
@@ -3483,7 +3481,7 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite)
const Buffs_Struct &curbuf = buffs[i];
// no buff in this slot
if (curbuf.spellid == SPELL_UNKNOWN)
if (!IsValidSpell(curbuf.spellid))
{
// if we haven't found a free slot, this is the first one so save it
if(firstfree == -2)
@@ -3697,8 +3695,6 @@ bool Mob::SpellOnTarget(
}
}
mod_spell_cast(spell_id, spelltar, reflect_effectiveness, use_resist_adjust, resist_adjust, isproc);
if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) {
safe_delete(action_packet);
return false;
@@ -5067,8 +5063,6 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
resist_chance += resist_modifier;
resist_chance += target_resist;
resist_chance = mod_spell_resist(resist_chance, level_mod, resist_modifier, target_resist, resist_type, spell_id, caster);
//Do our min and max resist checks.
if(resist_chance > spells[spell_id].max_resist && spells[spell_id].max_resist != 0)
{
@@ -5872,7 +5866,7 @@ bool Client::SpellBucketCheck(uint16 spell_id, uint32 character_id) {
int16 Mob::GetBuffSlotFromType(uint16 type) {
uint32 buff_count = GetMaxTotalSlots();
for (int i = 0; i < buff_count; i++) {
if (buffs[i].spellid != SPELL_UNKNOWN) {
if (IsValidSpell(buffs[i].spellid)) {
for (int j = 0; j < EFFECT_COUNT; j++) {
if (spells[buffs[i].spellid].effect_id[j] == type )
return i;
@@ -5884,8 +5878,9 @@ int16 Mob::GetBuffSlotFromType(uint16 type) {
uint16 Mob::GetSpellIDFromSlot(uint8 slot)
{
if (buffs[slot].spellid != SPELL_UNKNOWN)
if (IsValidSpell(buffs[slot].spellid)) {
return buffs[slot].spellid;
}
return 0;
}
@@ -5927,7 +5922,7 @@ bool Mob::IsCombatProc(uint16 spell_id) {
return false;
}
if (spell_id == SPELL_UNKNOWN) {
if (!IsValidSpell(spell_id)) {
return(false);
}
/*
@@ -5955,13 +5950,13 @@ bool Mob::IsCombatProc(uint16 spell_id) {
}
bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 base_spell_id, int level_override, uint32 proc_reuse_time) {
if(spell_id == SPELL_UNKNOWN)
if(!IsValidSpell(spell_id))
return(false);
int i;
if (bPerma) {
for (i = 0; i < MAX_PROCS; i++) {
if (PermaProcs[i].spellID == SPELL_UNKNOWN) {
if (!IsValidSpell(PermaProcs[i].spellID)) {
PermaProcs[i].spellID = spell_id;
PermaProcs[i].chance = iChance;
PermaProcs[i].base_spellID = base_spell_id;
@@ -5993,7 +5988,7 @@ bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 b
// Find a slot and use it as normal.
for (i = 0; i < MAX_PROCS; i++) {
if (SpellProcs[i].spellID == SPELL_UNKNOWN) {
if (!IsValidSpell(SpellProcs[i].spellID)) {
SpellProcs[i].spellID = spell_id;
SpellProcs[i].chance = iChance;
SpellProcs[i].base_spellID = base_spell_id;;
@@ -6024,12 +6019,12 @@ bool Mob::RemoveProcFromWeapon(uint16 spell_id, bool bAll) {
bool Mob::AddDefensiveProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id, uint32 proc_reuse_time)
{
if(spell_id == SPELL_UNKNOWN)
if(!IsValidSpell(spell_id))
return(false);
int i;
for (i = 0; i < MAX_PROCS; i++) {
if (DefensiveProcs[i].spellID == SPELL_UNKNOWN) {
if (!IsValidSpell(DefensiveProcs[i].spellID)) {
DefensiveProcs[i].spellID = spell_id;
DefensiveProcs[i].chance = iChance;
DefensiveProcs[i].base_spellID = base_spell_id;
@@ -6058,12 +6053,12 @@ bool Mob::RemoveDefensiveProc(uint16 spell_id, bool bAll)
bool Mob::AddRangedProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id, uint32 proc_reuse_time)
{
if(spell_id == SPELL_UNKNOWN)
if(!IsValidSpell(spell_id))
return(false);
int i;
for (i = 0; i < MAX_PROCS; i++) {
if (RangedProcs[i].spellID == SPELL_UNKNOWN) {
if (!IsValidSpell(RangedProcs[i].spellID)) {
RangedProcs[i].spellID = spell_id;
RangedProcs[i].chance = iChance;
RangedProcs[i].base_spellID = base_spell_id;
@@ -6094,17 +6089,16 @@ bool Mob::RemoveRangedProc(uint16 spell_id, bool bAll)
// behavior should be used.
bool Mob::UseBardSpellLogic(uint16 spell_id, int slot)
{
if(spell_id == SPELL_UNKNOWN)
if (!IsValidSpell(spell_id)) {
spell_id = casting_spell_id;
if(slot == -1)
}
if (slot == -1) {
slot = static_cast<int>(casting_spell_slot);
}
// should we treat this as a bard singing?
return
(
spell_id != 0 &&
spell_id != SPELL_UNKNOWN &&
IsValidSpell(spell_id) &&
slot != -1 &&
GetClass() == BARD &&
slot <= EQ::spells::SPELL_GEM_COUNT &&
@@ -6200,7 +6194,7 @@ void Mob::SendPetBuffsToClient()
for(int buffslot = 0; buffslot < MaxSlots; buffslot++)
{
if(buffs[buffslot].spellid != SPELL_UNKNOWN) {
if (IsValidSpell(buffs[buffslot].spellid)) {
pbs->spellid[buffslot] = buffs[buffslot].spellid;
pbs->ticsremaining[buffslot] = buffs[buffslot].ticsremaining;
PetBuffCount++;
@@ -6232,7 +6226,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target)
uint32 buff_count = for_target ? GetMaxBuffSlots() : GetMaxTotalSlots();
for(int i = 0; i < buff_count; ++i)
{
if(buffs[i].spellid != SPELL_UNKNOWN)
if (IsValidSpell(buffs[i].spellid))
{
++count;
}
@@ -6265,7 +6259,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target)
uint32 index = 0;
for(int i = 0; i < buff_count; ++i)
{
if(buffs[i].spellid != SPELL_UNKNOWN)
if (IsValidSpell(buffs[i].spellid))
{
buff->entries[index].buff_slot = i;
buff->entries[index].spell_id = buffs[i].spellid;
+3 -2
View File
@@ -383,8 +383,9 @@
#define FORAGE_MASTERY 6012 //Your forage mastery has enabled you to find something else!
#define GUILD_BANK_CANNOT_DEPOSIT 6097 // Cannot deposit this item. Containers must be empty, and only one of each LORE and no NO TRADE or TEMPORARY items may be deposited.
#define GUILD_BANK_FULL 6098 // There is no more room in the Guild Bank.
#define GUILD_BANK_TRANSFERRED 6100 // '%1' transferred to Guild Bank from Deposits.
#define GUILD_BANK_EMPTY_HANDS 6108 // You must empty your hands to withdraw from the Guild Bank.
#define GUILD_BANK_TRANSFERRED 6100 // '%1' transferred to Guild Bank from Deposits.
#define GUILD_BANK_EMPTY_HANDS 6108 // You must empty your hands to withdraw from the Guild Bank.
#define TRADESKILL_COMBINE_LORE 6199 // Combine would result in a LORE item (%1) you already possess.
#define TRANSFORM_FAILED 6326 //This mold cannot be applied to your %1.
#define TRANSFORM_COMPLETE 6327 //You have successfully transformed your %1.
#define DETRANSFORM_FAILED 6341 //%1 has no transformation that can be removed.
+53 -4
View File
@@ -253,6 +253,9 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob
{
if (!user || !in_combine) {
LogError("Client or NewCombine_Struct not set in Object::HandleCombine");
auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
user->QueuePacket(outapp);
safe_delete(outapp);
return;
}
@@ -278,6 +281,9 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob
Chat::Red,
"Error: Server is not aware of the tradeskill container you are attempting to use"
);
auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
user->QueuePacket(outapp);
safe_delete(outapp);
return;
}
c_type = worldo->m_type;
@@ -304,6 +310,9 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob
if (!inst || !inst->IsType(EQ::item::ItemClassBag)) {
user->Message(Chat::Red, "Error: Server does not recognize specified tradeskill container");
auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
user->QueuePacket(outapp);
safe_delete(outapp);
return;
}
@@ -427,22 +436,50 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob
if (spec.tradeskill == EQ::skills::SkillAlchemy) {
if (user_pp.class_ != SHAMAN) {
user->Message(Chat::Red, "This tradeskill can only be performed by a shaman.");
auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
user->QueuePacket(outapp);
safe_delete(outapp);
return;
}
else if (user_pp.level < MIN_LEVEL_ALCHEMY) {
user->Message(Chat::Red, "You cannot perform alchemy until you reach level %i.", MIN_LEVEL_ALCHEMY);
auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
user->QueuePacket(outapp);
safe_delete(outapp);
return;
}
}
else if (spec.tradeskill == EQ::skills::SkillTinkering) {
if (user_pp.race != GNOME) {
user->Message(Chat::Red, "Only gnomes can tinker.");
auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
user->QueuePacket(outapp);
safe_delete(outapp);
return;
}
}
else if (spec.tradeskill == EQ::skills::SkillMakePoison) {
if (user_pp.class_ != ROGUE) {
user->Message(Chat::Red, "Only rogues can mix poisons.");
auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
user->QueuePacket(outapp);
safe_delete(outapp);
return;
}
}
// Check if Combine would result in Lore conflict
for (const auto& e : spec.onsuccess) {
auto success_item_inst = database.GetItem(e.first);
if (user->CheckLoreConflict(success_item_inst)) {
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemData);
linker.SetItemData(success_item_inst);
auto item_link = linker.GenerateLink();
user->MessageString(Chat::Red, TRADESKILL_COMBINE_LORE, item_link.c_str());
auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
user->QueuePacket(outapp);
safe_delete(outapp);
return;
}
}
@@ -687,6 +724,22 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
}
}
DBTradeskillRecipe_Struct recipe_struct;
// Check if Combine would result in Lore conflict
for (const auto& e : recipe_struct.onsuccess) {
auto success_item_inst = database.GetItem(e.first);
if (user->CheckLoreConflict(success_item_inst)) {
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemData);
linker.SetItemData(success_item_inst);
auto item_link = linker.GenerateLink();
user->MessageString(Chat::Red, TRADESKILL_COMBINE_LORE, item_link.c_str());
user->QueuePacket(outapp);
safe_delete(outapp);
return;
}
}
//otherwise, we found it all...
outp->reply_code = 0x00000000; //success for finding it...
user->QueuePacket(outapp);
@@ -1110,8 +1163,6 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
const EQ::ItemData* item = nullptr;
chance = mod_tradeskill_chance(chance, spec);
if (((spec->tradeskill==75) || GetGM() || (chance > res)) || zone->random.Roll(aa_chance)) {
success_modifier = 1;
@@ -1248,8 +1299,6 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float
}
}
chance_stage2 = mod_tradeskill_skillup(chance_stage2);
if (chance_stage2 > zone->random.Real(0, 99)) {
//Only if stage1 and stage2 succeeded you get a skillup.
SetSkill(tradeskill, current_raw_skill + 1);
-2
View File
@@ -247,8 +247,6 @@ int32 Client::TributeItem(uint32 slot, uint32 quantity) {
//figure out what its worth
int32 pts = inst->GetItem()->Favor;
pts = mod_tribute_item_value(pts, m_inv[slot]);
if(pts < 1) {
Message(Chat::Red, "This item is worthless for favor.");
return(0);
-6
View File
@@ -1218,9 +1218,6 @@ bool Zone::Init(bool is_static) {
LoadGrids();
LoadTickItems();
//MODDING HOOK FOR ZONE INIT
mod_init();
// logging origination information
LogSys.origination_info.zone_short_name = zone->short_name;
LogSys.origination_info.zone_long_name = zone->long_name;
@@ -1927,9 +1924,6 @@ void Zone::Repop()
initgrids_timer.Start();
entity_list.UpdateAllTraps(true, true);
//MODDING HOOK FOR REPOP
mod_repop();
}
void Zone::GetTimeSync()
-5
View File
@@ -395,11 +395,6 @@ public:
double GetMaxMovementUpdateRange() const { return max_movement_update_range; }
/**
* Modding hooks
*/
void mod_init();
void mod_repop();
void SetIsHotzone(bool is_hotzone);
private:
+8 -5
View File
@@ -2535,8 +2535,9 @@ void ZoneDatabase::SaveMercBuffs(Merc *merc) {
}
for (int buffCount = 0; buffCount <= BUFF_COUNT; buffCount++) {
if(buffs[buffCount].spellid == 0 || buffs[buffCount].spellid == SPELL_UNKNOWN)
continue;
if (!IsValidSpell(buffs[buffCount].spellid)) {
continue;
}
int IsPersistent = buffs[buffCount].persistant_buff? 1: 0;
@@ -3079,8 +3080,9 @@ void ZoneDatabase::SaveBuffs(Client *client) {
Buffs_Struct *buffs = client->GetBuffs();
for (int index = 0; index < buff_count; index++) {
if(buffs[index].spellid == SPELL_UNKNOWN)
continue;
if (!IsValidSpell(buffs[index].spellid)) {
continue;
}
query = StringFormat("INSERT INTO `character_buffs` (character_id, slot_id, spell_id, "
"caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, "
@@ -3261,8 +3263,9 @@ void ZoneDatabase::SavePetInfo(Client *client)
// pet buffs!
int max_slots = RuleI(Spells, MaxTotalSlotsPET);
for (int index = 0; index < max_slots; index++) {
if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0)
if (!IsValidSpell(petinfo->Buffs[index].spellid)) {
continue;
}
if (query.length() == 0)
query = StringFormat("INSERT INTO `character_pet_buffs` "
"(`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, "