Compare commits

...

112 Commits

Author SHA1 Message Date
Alex King 7d194083aa [Release] 22.41.0 (#3939)
### Bug

* DI Buff Fade ([#3919](https://github.com/EQEmu/Server/pull/3919)) @fryguy503 2024-01-08
* NPCs will now only proc on hit ([#3913](https://github.com/EQEmu/Server/pull/3913)) @fryguy503 2024-01-08
* Pets should not ignore Z axis ([#3912](https://github.com/EQEmu/Server/pull/3912)) @fryguy503 2024-01-08

### Fixes

* Disciplines should show when someone casts them. ([#3901](https://github.com/EQEmu/Server/pull/3901)) @fryguy503 2024-01-08
* Fix Typo in Character Skills loading ([#3937](https://github.com/EQEmu/Server/pull/3937)) @Kinglykrab 2024-01-09
* Fix for HasLockoutByCharacterID ([#3927](https://github.com/EQEmu/Server/pull/3927)) @fryguy503 2024-01-08
* Harm Touch, Improved Harm Touch, and Unholy Touch ([#3904](https://github.com/EQEmu/Server/pull/3904)) @fryguy503 2024-01-08
* Legacy Manaburn should have hard cap. ([#3905](https://github.com/EQEmu/Server/pull/3905)) @fryguy503 2024-01-08
* TGB - Added logic to stop bard errors on group songs. ([#3906](https://github.com/EQEmu/Server/pull/3906)) @fryguy503 2024-01-08
* World Shutdown Filter ([#3930](https://github.com/EQEmu/Server/pull/3930)) @fryguy503 2024-01-08

### Info

* Adding textual feedback when trying to sell alt items back to … ([#3917](https://github.com/EQEmu/Server/pull/3917)) @fryguy503 2024-01-08

### Rules

* Backstab Damage Modifier ([#3908](https://github.com/EQEmu/Server/pull/3908)) @fryguy503 2024-01-08
* Classic Tradeskill Skill Clamp ([#3914](https://github.com/EQEmu/Server/pull/3914)) @fryguy503 2024-01-08
* Classic Triple Attack ([#3903](https://github.com/EQEmu/Server/pull/3903)) @fryguy503 2024-01-08
* Ensure mana taps only effect NPC's that have mana. ([#3907](https://github.com/EQEmu/Server/pull/3907)) @fryguy503 2024-01-08
* Over Taunt Hate ([#3900](https://github.com/EQEmu/Server/pull/3900)) @fryguy503 2024-01-08
* Stun Chance Percent Rule ([#3922](https://github.com/EQEmu/Server/pull/3922)) @fryguy503 2024-01-08
2024-01-08 20:31:13 -06:00
Alex King dd41fc5fcd [Bug Fix] Fix Typo in Character Skills loading (#3937) 2024-01-08 21:18:55 -05:00
Fryguy 6bf36f3e77 [Bug Fix] World Shutdown Filter (#3930)
Moved World Shutdown to the System filter from base yellow.
2024-01-08 12:14:08 -05:00
Fryguy dfb06db17b [Rule] Over Taunt Hate (#3900)
* [Rule] Over taunt hate rule

Rule to add additional hate when taunt succeeds. This can help tune snap aggro on taunt classes. Only works when taunt succeeds and not already top hate.

* Requested Adjustments

* Add default +1 on rule

* Revert back to 0 default on rule with +1 on the standard formula

* formatting fix
2024-01-08 12:11:04 -05:00
Fryguy 3e958c575b [Info] Adding textual feedback when trying to sell alt items back to … (#3917)
* [Info] Adding textual feedback when trying to sell alt items back to vendor when disabled.

* requested changes
2024-01-08 11:34:58 -05:00
Fryguy e5db19965f [Rule] Ensure mana taps only effect NPC's that have mana. (#3907)
* [Rule] Ensure mana taps only effect NPC's that have mana.

* Requested Changes

* fail at the beginning of a cast.

* Fixes
2024-01-08 06:23:54 -05:00
Fryguy ce73f6bfe1 [Bug Fix] Fix for HasLockoutByCharacterID (#3927)
Per HG, this will resolve issues with HasLockoutByCharacterID
2024-01-08 02:45:19 -06:00
Fryguy 700f4645e2 [Rule] Stun Chance Percent Rule (#3922)
Added a rule for server operators to tune the Stun Chance from players when mobs are behind.

DEFAULT: 12
2024-01-08 02:40:01 -06:00
Fryguy 5a7d544c5b [Bug] DI Buff Fade (#3919)
Mistakenly removed BuffFadeBySlot(buffSlot);
2024-01-08 02:06:41 -06:00
Fryguy 0f7f71334a [Rules] Backstab Damage Modifier (#3908)
* [Rules] Backstab Damage Modifier

Added NPCBackstabMod - Default 1.9 - Lower mod = higher backstab.

* Update special_attacks.cpp

corrected math to npc base_damage
2024-01-08 01:58:48 -06:00
Fryguy c731f3f560 [Rule] Classic Tradeskill Skill Clamp (#3914)
Legacy Tradeskills had a 252 clamp regardless of modifier and skill.

Default to 0 to bypass clamp
2024-01-08 01:48:40 -06:00
Fryguy b30fbc70a3 [Bug] NPCs will now only proc on hit (#3913) 2024-01-08 01:47:55 -06:00
Fryguy 195cb80d56 [Bug] Pets should not ignore Z axis (#3912)
Ignoring the Z axis allows you to attack mobs on target from wildly inappropriate Z distances.

Example you can pull mobs to the cy in bothunder from the towers you are under when many 1000's of units away by Z only.
2024-01-08 01:41:51 -06:00
Fryguy b2d5007466 [Bug Fix] TGB - Added logic to stop bard errors on group songs. (#3906)
* [Bug Fix] TGB - Added logic to stop bard errors on group songs.

Occasionally bards would get an error when singing songs with another group member targetted.

* Logic Fix

* Fixed missing brace
2024-01-08 01:27:45 -06:00
Fryguy 47e2eb0acf [Bug Fix] Harm Touch, Improved Harm Touch, and Unholy Touch (#3904)
* [bug] HT / Imp HT / Unholy Touch

Adjusted logic order for HT/Improved HT/Unholy Touch

* Requested Changes
2024-01-07 22:57:34 -05:00
Fryguy c36b3f030b [Bug Fix] Disciplines should show when someone casts them. (#3901)
* [Bug] Discs should show when someone casts them.

* Correction to location

* requested changes

* Additional fixes
2024-01-07 22:17:30 -05:00
Fryguy 2b821e50ff [Rule] Classic Triple Attack (#3903)
* [Rule] Classic Triple Attack

Classic Triple attack pre-dates skill based triple attack.

Originally it was only for a few classes but was expanded to Warrior, Monks, Berserkers and finally rangers for Dragons of Norrath. After which it was converted to a skill based feature.

These were innate starting level 60 and had a flat % to trigger.

* Requested Changes
2024-01-07 22:06:13 -05:00
Fryguy 0aa07e9529 [Bug Fix] Legacy Manaburn should have hard cap. (#3905)
Legacy Manaburn can crit, however normal or crit has same hard cap.

Created a rule to better manage the cap for server owners.
2024-01-07 21:54:14 -05:00
Chris Miles 7ddafd9ed8 [Release] 22.40.0 (#3899)
* [Release] 22.40.0

* Update CHANGELOG.md
2024-01-07 16:13:55 -06:00
Fryguy 5488e9bf22 [Bug Fix] Amplification should not benefit from instrument mods (#3898) 2024-01-07 17:01:08 -05:00
Fryguy 6db6d7dca9 [Bug Fix] Depop Charm Pet and Detach Debuffs on Evacuate (#3888)
* [Bug Fix] depop charm pet and detach debuffs on evac.

This will depop charm pets and deteach debuffs to prevent some social aggro issues and exploitable conditions with charming and pulling a mob across the zone with no aggro concerns.

* Added Rules
2024-01-07 16:44:15 -05:00
Fryguy 066b762e73 [Rule] RequireMnemonicRetention for Spells 9-12 Rule (#3876)
* [Rule] RequireMnemonicRetention for Spells 9-12 Rule
Rule is default false to maintain current server setup.

Enabling will require clients to have the required ranks of Mnemonic Retention in order to cast.

This is a stopgap as on live, the buttons are not even available to use. This will mimic the functionality but denying the cast.

* Change to default true

* Cleanup Logic - Thank you @KinglyKrab

* To aggressive on the cleanup

* True != true

* fix duplicated rule after rebase.
2024-01-07 16:28:03 -05:00
Fryguy cd85a8524a [Bug Fix] Target Locked Pet Taunt (#3894)
* [Bug Fix] Target Locked pet taunt

Target locked pets shouldn't taunt

* Update mob.h

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2024-01-07 16:22:37 -05:00
Fryguy 4490a53ba0 [Bug Fix] Disciplines Getting Focuses Fix (#3884)
* [Bug Fix] Discs Getting Focuses Fix

Focus effects shouldn't extend discipline timers except for War Cries

* Update to Logic
2024-01-07 16:02:17 -05:00
Fryguy 00eb462d47 [Bug Fix] Snare and DOT Stacking (#3897)
Dots won't overwrite regen but regen won't stack with dots.

Sow type spells won't stack if a snare effect is already in place.
2024-01-07 16:01:33 -05:00
Fryguy 2df7d19f97 [Bug Fix] Rune Invis Break (#3893)
Rune doesn't stop invis breaking
2024-01-07 15:57:09 -05:00
Fryguy 48c6db3a9c [Bug Fix] Buff Sync (#3896)
Align client and server on buff position (easiest fix to sync client/server)
2024-01-07 15:56:27 -05:00
Fryguy cce368d94c [Bug Fix] Rez in zone clear aggro (#3895)
Clear client to be rezzed aggro when rezzing in zone.
2024-01-07 15:56:21 -05:00
Fryguy 84b8bdd2b4 [Bug Fix] Clear Ramp when Clearing hate (#3892)
* [Bug Fix] Clear Ramp when Clearing hate

When clearing a client from the hatelist, also clear them from ramp

* Add additional calls missed

* requested changes

* extra tabs
2024-01-07 15:27:12 -05:00
Fryguy fc8ace91cb [Bug Fix] Cancel Magic SE fix (#3890)
* [Bug Fix] Cancel Magic SE fix

Cancel Magic should always attempt the first slot for dispell.

* Add back TryDispell
2024-01-07 15:02:43 -05:00
Fryguy 2d6c9f881a [Bug Fix] Swimming Rules Adjustment and Racial (#3889)
* [Bug Fix] Swiming rules adjustment and racial

We need to set all character default swiming prior to working through racials. Added racial check so if starting rule is over the racial default it will not over-ride the starting rule.

* operator fix
2024-01-07 15:02:21 -05:00
Fryguy 9b72c07a54 [Bug Fix] Add locations where melee can be bound outside of a city. (#3887)
* [Bug Fix] Add locations where melee can be bound outside of a city.

* new line
2024-01-07 15:01:54 -05:00
Fryguy 9d5d13fbd0 [Rule] Casting Charm on over level = Aggro (#3886)
Casting charm on target with level above max level of spell will give resist message and aggro
2024-01-07 14:53:42 -05:00
Fryguy bc3e9e8fba [Rules] Evac Aggro Wipe (#3880)
clear aggro on evac spells, special case of wiping aggro for clients
2024-01-07 13:51:07 -05:00
Fryguy d9cfc3a858 [Feature] Legacy Manaburn Rule (#3872)
* [Feature] Legacy Manaburn Rule

Enabling this rule allows the legacy style Manaburn and LifeBurn early 2003 and earlier.

* Requested Changes

* Requested Change
2024-01-07 12:34:43 -05:00
Fryguy 51dc62dfb1 [Bug Fix] Swim Skillup and Underwater Fall Damage Fix (#3885)
* [Bug Fix] Swim Skillup and Underwater Fall Damage Fix

No fall damage underwater, need to move to skill up swimming

* Requested Changes
2024-01-07 12:34:25 -05:00
Fryguy e52e4d5b3f [Bug Fix] Bard Caster Level Fixes (#3883)
Bards do not gain benefits from spells like Intellectual Superiority
2024-01-07 12:13:44 -05:00
Fryguy 0c3149a6e5 [Bug Fix] Rez Effects Stacking (#3882)
Nothing conflicts with stacking with resurrection effects
2024-01-07 12:13:36 -05:00
Fryguy 611122833d [Rule] Undead Aggro (#3881)
* [Rule] Undead Aggro

Add rule for undead aggro and check for undead at aggro check time

* Add Rule

* Requested Change
2024-01-07 12:13:30 -05:00
Fryguy b83373491e [Rules] PC Push and NPCtoNPC Push (#3879)
* [Rules] PC Push and NPCtoNPC Push

Added knobs to increase/decrease push for players/clients by percentage.

Added toggle to enable or disable (disabled by default) NPCtoNPC push (2013 patch)

* Requested Changes

* Fix build errors
2024-01-07 12:13:17 -05:00
Fryguy dfa349492c [Rule] Classic Invite Requires Target (#3878)
* [Rule] Classic Invite Requires Target

Default is false, when enabled, group invites will require a hard target to invite.

`/invite charname` will no longer function when enabled.

* Updates including raid errors

* String Suggestion
2024-01-07 12:12:57 -05:00
Fryguy c08200188f [Bug Fix] Harm Touch Unholy Disc Type (#3874)
* [Bug Fix] Harm Touch Unholy Disc Type

Added an small if block in the ResistSpell() function to set the correct resist type, disease,when HT is casted while the Unholy Disc is up.

* Remove Extra Space
2024-01-07 12:12:21 -05:00
Fryguy 41d9a15c74 [Bug Fix] Vampiric Embrace Fixes (#3873)
* [Bug Fix] Vampiric Embrace Fixes

Fixed the difference of procs for Vampiric Embrace between Necros and Shadow Knights.

* Space

* Cleanup

* Update spdat.h

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2024-01-07 12:12:15 -05:00
Fryguy 2ed4effbe3 [Bug Fix] Prevent QS Crashes (#3877) 2024-01-07 04:47:29 -05:00
Fryguy 6ec09f300a [Bug Fix] Dire Charm Reset (#3875)
* [Bug Fix] Dire Charm Reset

Added Druid and Necro Dire Charm to Failure Check

* [Rule] RequireMnemonicRetention for Spells 9-12 Rule

Rule is default false to maintain current server setup.

Enabling will require clients to have the required ranks of Mnemonic Retention in order to cast.

This is a stopgap as on live, the buttons are not even available to use. This will mimic the functionality but denying the cast.

* Revert "[Rule] RequireMnemonicRetention for Spells 9-12 Rule"

This reverts commit c65e86223d.
2024-01-07 04:47:09 -05:00
Fryguy 079f612730 [Feature] Legacy Fizzle Code (#3868)
* [Feature] Legacy Fizzle Code
Enabling UseLegacyFizzleCode will enable the legacy fizzle code system.

New fizzle codes modeled on extensive testing in 2001/2002 (thank you druid's grove and graffe)

* requested adjustments

* Requested Changes
2024-01-07 04:46:27 -05:00
Fryguy 2c971fb2de [Rules] Resist Softcap rules (#3863)
* [Rules] Resist Softcap rules

This rule will allow you to adjust the Resist softcap to help tune resists as you need.

* Fix naming

* Cleanup
2024-01-07 04:46:14 -05:00
Fryguy 20e9a8b2d2 [Bug Fix] Class Trainers dont steal your money! (#3864)
* [Bug Fix] Class Trainers dont steal your money!

Class trainers log an error when you have reached your max train in the specific skill. They will continue to "Appear" to eat money and practices however they will be returned on logout/in or zone.

* Remove un-needed logic due to legacy code.

* more cleanup
2024-01-07 01:16:57 -06:00
Fryguy 8fa6a0b496 [Rules] Restrict Finishing Blow to only Fleeing NPC's. (#3869)
* [Rules] Restrict Finishing Blow to only Fleeing NPC's.

Default is false to maintain current design

* Cleanup
2024-01-07 01:11:49 -06:00
Fryguy d6e1c3f187 [Rule] Mounts will wear off on zone (#3865)
* [Rule] Mounts will wear off on zone

Allows server admins to toggle if they want to prevent players from retaining mounts on zone. false is default to maintain current feature set.

* Added Date for mount zoning added
2024-01-07 01:08:16 -06:00
Fryguy 70a4b6a2b1 [Bug Fix] DI/Death Pact Fix (#3867)
* [Bug Fix] DI/Death Pact Fix

Divine Intervention/Death Pact buffs were always fading the moment a person dropped below 16% regardless if it healed or not.  It will now only fade if it succeeds in healing, and it will only check to heal when a person drops below 16%

* Requested Updates
2024-01-07 01:05:16 -06:00
Fryguy 7923d7bc6c [Bugfix] Negative Aggro Fix (#3866)
If aggro goes negative, it resets the counter from the max value, so having a negative aggro will give massive aggro.  This was happening with bane attacks from monk special abilities being set to -5 damage so they don't hit, and since aggro was set to the same, instant aggro from monks on bane mobs.  Set all monk special attacks to generate 0 aggro if it is less than 0.
2024-01-07 01:03:15 -06:00
Fryguy 6007ba454c [Feature] Formula Addition (40+ Harm Touch) (#3870) 2024-01-07 01:01:13 -06:00
Fryguy d4a78f4799 [Feature] Break Trader if moved (#3862)
* [Feature] Break Trader if moved

If the player moves while in trader mode, it will end the mode, this prevents traders from moving outside of the trader area after entering trader mode.

* Fix spacing
2024-01-07 00:55:33 -06:00
Fryguy 0ada53aa96 [Tuning] FD and Sneak break when cast on adjustments. (#3861)
* [Tuning] FD and Sneak break when cast on adjustments.
         Rules added:
         RULE_REAL(Spells, BreakFeignDeathWhenCastOn, 50.0) // percentage that fd will break when you resist a spell
         RULE_REAL(Spells, BreakSneakWhenCastOn, 2.0) // percentage that sneak will break when you resist a spell

* Fix default value to be peq default and spacing
2024-01-07 00:55:15 -06:00
Alex King 7803170d6b [Hotfix] Fix typo on ZoneDatabase::LoadPetInfo (#3871) 2024-01-07 00:43:10 -06:00
Alex King 20778ad7d9 [Character] Convert NoRentExpired to Repositories (#3860)
* [Character] Convert NoRentExpired to Repositories

- Create a custom `GetSecondsSinceLastLogin` repository method to use in `NoRentExpired`.

* Update character_data_repository.h
2024-01-07 00:25:58 -06:00
Alex King 44d63c47d9 [Account] Convert UpdateGMStatus to Repositories (#3859)
- Convert `UpdateGMStatus` to repositories.
- Convert parameter of status to `int`.
2024-01-07 00:24:02 -06:00
Alex King b288202c96 [Account] Convert Get/Update Account Karma to Repositories (#3858)
* [Account] Convert Get/Update Account Karma to Repositories

- Convert `GetKarma` and `UpdateKarma` to repositories.

* Update zonedb.cpp
2024-01-07 00:23:18 -06:00
Alex King a724e92638 [Character] Convert Clear/Delete/Get/Update of Character Item Recast to Repositories (#3857)
* [Character] Convert Clear/Delete/Get/Update of Character Item Recast to Repositories

- Convert `ClearOldRecastTimestamps`, `DeleteItemRecast`, `GetItemRecastTimeStamp`, `GetItemRecastTimestamps`, and `UpdateItemRecast` to repositories.

* Update shareddb.cpp

* Update shareddb.cpp

* Update zonedb.cpp
2024-01-07 00:21:14 -06:00
Alex King 0ea825e9a4 [Character] Convert Load/Update of Character Alternate Currencies to Repositories (#3856)
- Convert `LoadAltCurrencyValues` and `UpdateAltCurrencyValue` to repositories.
- Cleanup some other code and logic as well.
- Add `AlternateCurrencyMode` namespace for `AltCurrencyPopulate_Struct` opcode magic numbers.
2024-01-07 00:11:15 -06:00
Alex King 2ca50b339d [Characters] Convert Load/Save of Character Auras to Repositories (#3854)
* [Characters] Convert Load/Save of Character Auras to Repositories

- Convert `LoadAuras` and `SaveAuras` to repositories.

* Update zonedb.cpp
2024-01-07 00:08:56 -06:00
Alex King 164fe31fa8 [Character] Convert Load/Save of Character Buffs to Repositories (#3855)
* [Character] Convert Load/Save of Character Buffs to Repositories

# Notes
- Convert `LoadBuffs` to repositories.
- `SaveBuffs` was already using repositories, cleanup logic.

# Images
## Load

## Save

* Update repository.

* Update zonedb.cpp
2024-01-07 00:05:27 -06:00
Alex King f3de3e8c31 [Pets] Convert Load/Save of Pet Info to Repositories (#3853)
* [Pets] Convert Save/Load of Pet Info to Repositories

- Convert `LoadPetInfo` to repositories.
- `SavePetInfo` already used repositories, cleanup logic.

* Update repositories.

* Update zonedb.cpp
2024-01-06 23:59:02 -06:00
Alex King 1227f35382 [Cleanup] Remove bot-based saylink method (#3852)
# Notes
- With the new saylink functionality, bots no longer need their own saylink method.
2024-01-06 23:48:37 -06:00
Alex King c0769a9c29 [Character] Convert Load/Save of Character Bind to Repositories (#3851)
# Notes
- Convert `LoadCharacterBindPoint` to repositories.
- `SaveCharacterBinds` was already using repositories, cleanup logic.

# Images
## Load

## Save
2024-01-06 23:46:24 -06:00
Alex King 2dd0e51936 [Character] Convert Delete/Load/Save of Character Disciplines to Repositories (#3850)
* [Character] Convert Delete/Load/Save of Character Disciplines to Repositories

- Convert `DeleteCharacterDiscipline` and `SaveCharacterDiscipline` to repositories.
- `LoadCharacterDiscipline` already used repositories, cleaned up the logic.

* Update effects.cpp

* Update client.cpp

* Update effects.cpp

* Update client.cpp

* Update zonedb.cpp

* Update client.cpp
2024-01-06 23:41:01 -06:00
Alex King fd787af53a [Character] Convert Delete/Load/Remove/Save of Character AA to Repositories (#3849)
* [Character] Convert Delete/Load/Remove/Save of Character AA to Repositories

- Convert `DeleteCharacterAAs`, `LoadAlternateAdvancement`, `RemoveExpendedAA` and `SaveAA` to repositories.
- Add `AACategory` namespace for AA Categories.
- Cleanup some logic/formatting in modified methods.

* Move namespace.
2024-01-06 23:30:04 -06:00
Alex King aa39ac8023 [Character] Convert Load/Save of Character Currency to Repositories (#3848)
* [Character] Convert Load/Save of Character Currency to Repositories

- Convert `LoadCharacterCurrency` and `SaveCharacterCurrency` to repositories.

* Update zonedb.cpp
2024-01-06 23:28:37 -06:00
Alex King 397096996c [Character] Convert Delete/Load/Save of Character Bandolier to Repositories (#3845)
* [Character] Convert Delete/Load/Save of Character Bandolier to Repositories

- Converts `DeleteCharacterBandolier`, `LoadCharacterBandolier`, and `SaveCharacterBandolier` to repositories.

* Update zonedb.cpp

* Update zonedb.cpp

* Update zonedb.cpp

* Update zonedb.cpp

* Update zonedb.cpp
2024-01-06 23:25:13 -06:00
Alex King f8de9b9167 [Character] Convert Load/Save of Character Potion Belt to Repositories (#3844)
* [Character] Convert Load/Save of Character Potion Belt to Repositories

- Converts `LoadCharacterPotionBelt` and `SaveCharacterPotionBelt` to repositories.

* Update zonedb.cpp

* Update zonedb.cpp
2024-01-06 23:15:58 -06:00
Alex King 9d48cbcd29 [Character] Convert Load/Save of Character Skills to Repositories (#3843)
* [Character] Convert Load/Save of Character Skills to Repositories

- Converts `LoadCharacterSkills` and `SaveCharacterSkill` to repositories.

* Update zonedb.cpp
2024-01-06 23:13:39 -06:00
Alex King eb5eb0ca30 [Character] Convert Delete/Load/Save of Character Spells to Repositories (#3842)
* [Character] Convert Delete/Load/Save of Character Spells to Repositories

- Converts `DeleteCharacterSpell`, `LoadCharacterSpellBook`, and `SaveCharacterSpell` to repositories.

* Update zonedb.cpp
2024-01-06 23:12:40 -06:00
Alex King bc4bebb4a9 [Character] Convert Delete/Save of Character Memmed Spells to Repositories (#3841)
* [Character] Convert Delete/Save of Memmed Spells to Repositories

- Converts `DeleteCharacterMemorizedSpell` and `LoadCharacterMemmedSpells` to repositories.

* Update zonedb.cpp

* Update zonedb.cpp
2024-01-06 23:09:12 -06:00
Alex King 05f09b56e6 [Commands] Cleanup #petname Command (#3829)
# Notes
- Cleanup messages and logic.
2024-01-06 23:08:24 -06:00
Alex King 411fe3d95d [Character] Convert Load/Save of Character Data to Repositories (#3839)
* [Character] Convert Load/Save of Characters to Repositories

 # Notes
- `LoadCharacterData` and `SaveCharacterData` now use repositories.

* Update zonedb.cpp
2024-01-06 23:05:44 -06:00
Alex King c1e984dfc1 [Character] Convert Delete/Load/Save of Character Material to Repositories (#3846)
* [Character] Convert Delete/Load/Save of Character Material to Repositories

- Convert `DeleteCharacterMaterialColor`, `LoadCharacterMaterialColor`, and `SaveCharacterMaterialColor` to repositories.

* Cleanup
2024-01-06 23:03:54 -06:00
Alex King a5d9a8596a [Character] Convert Delete/Load/Save of Character Leadership Abilities to Repositories (#3847)
# Notes
- Converts `DeleteCharacterLeadershipAbilities`, `LoadCharacterLeadershipAbilities`, and `SaveCharacterLeadershipAbilities` to repositories.

# Images
## Load

## Save

## Delete
2024-01-06 22:42:56 -06:00
Alex King 7e23d798d5 [Commands] Cleanup #fixmob Command (#3828)
# Notes
- Cleanup messages and logic.
2024-01-06 22:29:40 -06:00
Alex King 47ab8910a3 [Commands] Cleanup #shutdown Command (#3830)
# Notes
- Cleanup messages and logic.
- Add a confirmation so operators don't accidentally shutdown the zone they're in thinking this is `#worldshutdown` instead.
2024-01-06 22:29:07 -06:00
Alex King 259add68f5 [Commands] Cleanup #appearance Command (#3827)
* [Commands] Cleanup #appearance Command

# Notes
- Cleanup messages and logic.
- Cleanup appearance type constants to use a namespace with constexpr instead.
- Cleanup animation constants to use a namespace with constexpr instead.

* Update emu_constants.cpp

* Cleanup
2024-01-06 22:24:32 -06:00
Alex King d2f5dc43a6 [Commands] Remove #zopp Command (#3831)
# Notes
- This command seems to be unused.
2024-01-06 22:18:47 -06:00
Alex King 99d2e3a8b1 [Commands] Add #clearxtargets Command (#3833)
# Perl
- Add `$client->ClearXTargets()`.

# Lua
- Add `client:ClearXTargets()`.

# Notes
- Add `#clearxtargets` command for players.
- Allows operators/players to clear their XTargets if something get stuck on it.
2024-01-06 22:18:21 -06:00
Alex King 122fe398b4 [Languages] Cleanup language constants, use repositories (#3838)
* [Languages] Cleanup languages constants

# Notes
- Cleanup formatting and logic where necessary.
- Cleaned up constants to use a namespace with `constexpr` instead.
- Changed `LoadCharacterLanguages` to use a repository instead.

* Lua GroupMessage uint8/language_id

* Lua More uint8/language_id
2024-01-06 22:17:10 -06:00
Alex King 43c4b13978 [Commands] Add scoped buckets and editing to #databuckets (#3826)
* [Commands] Cleanup #databuckets Command

# Notes
- Cleanup messages and logic.
- Utilize keyed buckets functionality optionally.
- Use repositories instead of a regular query.

* Add edit.

* Remove | in message
2024-01-06 22:10:00 -06:00
Chris Miles a3a707adae [UCS] Consolidate configuration block (#3768)
* [UCS] Consolidate configuration block

* Update eqemu_config.cpp

* Single indent config

* reload config internally

* Make sure port is a string to be consistent with previous fields

* Move conversion logic to function CheckUcsConfigConversion
2024-01-06 21:55:51 -06:00
dariusuknuis c4da9766a4 [Bots] Resist Spell Fix (#3840)
Bots were not casting the highest level spell with ^resist. Base_value was being used to compare resist spells, but this is typically the same value for all resist spells. This fix should be easier than calculating the caster bots level adjusted "resist_total".
2024-01-06 21:04:33 -05:00
Aeadoin a8eb2832ce [Crash] Fix crash where Raid invite could be accepted after forming group with the Raid invitor. (#3837)
* [Crash] Fix a crash where a raid invite could be accepted affter joining a group with the invitor.

* [Crash] Fix a crash where a raid invite could be accepted after joining a group with the invitor.
2024-01-06 16:59:35 -05:00
hg 076aab50e8 Ignore optionals when finding task step (#3836)
This fixes optional elements from being required to unlock the next step
when using the step system.
2024-01-05 17:05:56 -05:00
Alex King 1e8889a9fc [Bug Fix] Fix issue with 9th/10th inventory slot (#3835)
# Notes
- Code was not checking for a `-1` value and was assuming everyone used valid bitmasks.
2024-01-03 17:34:24 -05:00
JJ 7c3481daf9 [Release] 22.39.1 (#3834) 2023-12-31 18:39:07 -05:00
Alex King b93dec357f [Quest API] Fix issue with death events. (#3823)
# Notes
- Death events were happening too early so stuff checking for dead NPCs was non-functional.
2023-12-31 17:37:26 -05:00
Alex King 2f4af4f0c2 [Bug Fix] Fix crash in Client::Handle_OP_GMGoto (#3832)
# Notes
- Logic was incorrect.
2023-12-31 15:45:08 -05:00
Chris Miles a0f2a8a743 [Database] Database update improvements, content db and terminal checks (#3814)
* [Database] Database update improvements, content db and terminal checks

* Update manifest entries with content flag

* Update database_update_manifest.cpp
2023-12-30 22:15:01 -06:00
Chris Miles 4c7016bd7b [Repositories] Protected extended repositories from being overwritten if exists (#3815) 2023-12-30 22:14:46 -06:00
Alex King 6c18cd0bee [Quest API] Add HasItemOnCorpse() to Perl/Lua (#3824)
# Perl
- Add `$client->HasItemOnCorpse(item_id)`.

# Lua
- Add `client:HasItemOnCorpse(item_id)`.

# Notes
- Allows operators to see if a player has an item on any of their corpses.
- May need to address having to allocate and deallocate memory for every corpse that could possibly exist for a player.
2023-12-30 22:09:48 -06:00
Alex King 87e63e1e36 [Cleanup] Remove unused PlotPosition methods from mob.cpp/mob.h (#3820)
# Notes
- These are unused.
2023-12-30 22:07:02 -06:00
Alex King a771882cff [Cleanup] Remove SendStunAppearance from mob.cpp/mob.h (#3818)
# Notes
- This is unused.
2023-12-30 22:06:46 -06:00
Alex King b3f6a8c55f [Cleanup] Remove MakeSpawnUpdateNoDelta from mob.cpp/mob.h (#3816)
# Notes
- This is unused.
2023-12-30 22:06:33 -06:00
Fryguy 605502cd9d [Bug Fix] Client:SetBucket Overload Incorrectly Named (#3825)
There was an issue with Client:SetBucket when attempting to use an expiration date. Looks like a lua def was mistakenly named SetBucketExpires.
2023-12-30 15:31:04 -05:00
Alex King 4712ca471b [Cleanup] Gender constants cleanup (#3817)
* [Cleanup] Gender constants cleanup

# Notes
- Convert to a `Gender` namespace using `constexpr`.
- Cleanup spots where we were using magic numbers for gender values.

* Cleanup
2023-12-30 11:22:09 -05:00
Alex King 836c3d6596 [Cleanup] Appearance not appearence (#3819)
# Notes
- These were misspelled.
2023-12-30 11:22:02 -05:00
Alex King 53169ae217 [Cleanup] Delete errmsg.h in common and zone (#3821)
* [Cleanup] Delete errmsg.h in common and zone

# Notes
- These files were mostly unused.
- Moved the stuff that was used to the `dbcore.h` file since it's already used.

* Remove references
2023-12-30 11:21:57 -05:00
Chris Miles fd31915fae [Release] 22.39.0 (#3812) 2023-12-27 20:09:21 -06:00
Chris Miles 52763b6dd2 [Character] Fix character copier due to schema change (#3805)
* [Character] Fix character copier due to schema change

* Tweak
2023-12-27 20:04:46 -06:00
Chris Miles 51cd43b4ea [Player Events] Bulk replace settings on boot (#3806) 2023-12-27 20:04:29 -06:00
Chris Miles 2db84f5a4f [MySQL] Fix MySQL Query error formatting (#3808) 2023-12-27 20:04:18 -06:00
Chris Miles 79cc2d5351 [Logs] Reclassify unhelpful Info message (#3809) 2023-12-27 20:04:11 -06:00
Chris Miles d31cb09214 [Logs] Bulk insert new log settings (#3810) 2023-12-27 20:04:01 -06:00
Chris Miles 8bedcd8751 [Tasks] Add enabled column (#3804)
* [Tasks] Add enabled column

* Update task_manager.cpp

* Update task_manager.cpp

* Update base_tasks_repository.h
2023-12-27 20:03:53 -06:00
Chris Miles 473c5096f6 [Objects] Remove "No objects to load for zone" error message (#3807) 2023-12-27 21:03:08 -05:00
Paul Coene 0b181d5048 [Combat] Disarm was not dropping item to ground due to bug (#3811) 2023-12-27 10:19:09 -05:00
155 changed files with 6585 additions and 3585 deletions
+208
View File
@@ -1,3 +1,211 @@
## [22.41.0] - 1/8/2024
### Bug
* DI Buff Fade ([#3919](https://github.com/EQEmu/Server/pull/3919)) @fryguy503 2024-01-08
* NPCs will now only proc on hit ([#3913](https://github.com/EQEmu/Server/pull/3913)) @fryguy503 2024-01-08
* Pets should not ignore Z axis ([#3912](https://github.com/EQEmu/Server/pull/3912)) @fryguy503 2024-01-08
### Fixes
* Disciplines should show when someone casts them. ([#3901](https://github.com/EQEmu/Server/pull/3901)) @fryguy503 2024-01-08
* Fix Typo in Character Skills loading ([#3937](https://github.com/EQEmu/Server/pull/3937)) @Kinglykrab 2024-01-09
* Fix for HasLockoutByCharacterID ([#3927](https://github.com/EQEmu/Server/pull/3927)) @fryguy503 2024-01-08
* Harm Touch, Improved Harm Touch, and Unholy Touch ([#3904](https://github.com/EQEmu/Server/pull/3904)) @fryguy503 2024-01-08
* Legacy Manaburn should have hard cap. ([#3905](https://github.com/EQEmu/Server/pull/3905)) @fryguy503 2024-01-08
* TGB - Added logic to stop bard errors on group songs. ([#3906](https://github.com/EQEmu/Server/pull/3906)) @fryguy503 2024-01-08
* World Shutdown Filter ([#3930](https://github.com/EQEmu/Server/pull/3930)) @fryguy503 2024-01-08
### Info
* Adding textual feedback when trying to sell alt items back to … ([#3917](https://github.com/EQEmu/Server/pull/3917)) @fryguy503 2024-01-08
### Rules
* Backstab Damage Modifier ([#3908](https://github.com/EQEmu/Server/pull/3908)) @fryguy503 2024-01-08
* Classic Tradeskill Skill Clamp ([#3914](https://github.com/EQEmu/Server/pull/3914)) @fryguy503 2024-01-08
* Classic Triple Attack ([#3903](https://github.com/EQEmu/Server/pull/3903)) @fryguy503 2024-01-08
* Ensure mana taps only effect NPC's that have mana. ([#3907](https://github.com/EQEmu/Server/pull/3907)) @fryguy503 2024-01-08
* Over Taunt Hate ([#3900](https://github.com/EQEmu/Server/pull/3900)) @fryguy503 2024-01-08
* Stun Chance Percent Rule ([#3922](https://github.com/EQEmu/Server/pull/3922)) @fryguy503 2024-01-08
## [22.40.0] - 1/7/2024
### Account
* Convert Get/Update Account Karma to Repositories ([#3858](https://github.com/EQEmu/Server/pull/3858)) @Kinglykrab 2024-01-07
* Convert UpdateGMStatus to Repositories ([#3859](https://github.com/EQEmu/Server/pull/3859)) @Kinglykrab 2024-01-07
### Bots
* Resist Spell Fix ([#3840](https://github.com/EQEmu/Server/pull/3840)) @dariusuknuis 2024-01-07
### Bugfix
* Negative Aggro Fix ([#3866](https://github.com/EQEmu/Server/pull/3866)) @fryguy503 2024-01-07
### Character
* Convert Clear/Delete/Get/Update of Character Item Recast to Repositories ([#3857](https://github.com/EQEmu/Server/pull/3857)) @Kinglykrab 2024-01-07
* Convert Delete/Load/Remove/Save of Character AA to Repositories ([#3849](https://github.com/EQEmu/Server/pull/3849)) @Kinglykrab 2024-01-07
* Convert Delete/Load/Save of Character Bandolier to Repositories ([#3845](https://github.com/EQEmu/Server/pull/3845)) @Kinglykrab 2024-01-07
* Convert Delete/Load/Save of Character Disciplines to Repositories ([#3850](https://github.com/EQEmu/Server/pull/3850)) @Kinglykrab 2024-01-07
* Convert Delete/Load/Save of Character Leadership Abilities to Repositories ([#3847](https://github.com/EQEmu/Server/pull/3847)) @Kinglykrab 2024-01-07
* Convert Delete/Load/Save of Character Material to Repositories ([#3846](https://github.com/EQEmu/Server/pull/3846)) @Kinglykrab 2024-01-07
* Convert Delete/Load/Save of Character Spells to Repositories ([#3842](https://github.com/EQEmu/Server/pull/3842)) @Kinglykrab 2024-01-07
* Convert Delete/Save of Character Memmed Spells to Repositories ([#3841](https://github.com/EQEmu/Server/pull/3841)) @Kinglykrab 2024-01-07
* Convert Load/Save of Character Bind to Repositories ([#3851](https://github.com/EQEmu/Server/pull/3851)) @Kinglykrab 2024-01-07
* Convert Load/Save of Character Buffs to Repositories ([#3855](https://github.com/EQEmu/Server/pull/3855)) @Kinglykrab 2024-01-07
* Convert Load/Save of Character Currency to Repositories ([#3848](https://github.com/EQEmu/Server/pull/3848)) @Kinglykrab 2024-01-07
* Convert Load/Save of Character Data to Repositories ([#3839](https://github.com/EQEmu/Server/pull/3839)) @Kinglykrab 2024-01-07
* Convert Load/Save of Character Potion Belt to Repositories ([#3844](https://github.com/EQEmu/Server/pull/3844)) @Kinglykrab 2024-01-07
* Convert Load/Save of Character Skills to Repositories ([#3843](https://github.com/EQEmu/Server/pull/3843)) @Kinglykrab 2024-01-07
* Convert Load/Update of Character Alternate Currencies to Repositories ([#3856](https://github.com/EQEmu/Server/pull/3856)) @Kinglykrab 2024-01-07
* Convert NoRentExpired to Repositories ([#3860](https://github.com/EQEmu/Server/pull/3860)) @Kinglykrab 2024-01-07
### Characters
* Convert Load/Save of Character Auras to Repositories ([#3854](https://github.com/EQEmu/Server/pull/3854)) @Kinglykrab 2024-01-07
### Code
* Remove bot-based saylink method ([#3852](https://github.com/EQEmu/Server/pull/3852)) @Kinglykrab 2024-01-07
### Commands
* Add #clearxtargets Command ([#3833](https://github.com/EQEmu/Server/pull/3833)) @Kinglykrab 2024-01-07
* Add scoped buckets and editing to #databuckets ([#3826](https://github.com/EQEmu/Server/pull/3826)) @Kinglykrab 2024-01-07
* Cleanup #appearance Command ([#3827](https://github.com/EQEmu/Server/pull/3827)) @Kinglykrab 2024-01-07
* Cleanup #fixmob Command ([#3828](https://github.com/EQEmu/Server/pull/3828)) @Kinglykrab 2024-01-07
* Cleanup #petname Command ([#3829](https://github.com/EQEmu/Server/pull/3829)) @Kinglykrab 2024-01-07
* Cleanup #shutdown Command ([#3830](https://github.com/EQEmu/Server/pull/3830)) @Kinglykrab 2024-01-07
* Remove #zopp Command ([#3831](https://github.com/EQEmu/Server/pull/3831)) @Kinglykrab 2024-01-07
### Crash
* Fix crash where Raid invite could be accepted after forming group with the Raid invitor. ([#3837](https://github.com/EQEmu/Server/pull/3837)) @Aeadoin 2024-01-06
### Feature
* Break Trader if moved ([#3862](https://github.com/EQEmu/Server/pull/3862)) @fryguy503 2024-01-07
* Formula Addition (40+ Harm Touch) ([#3870](https://github.com/EQEmu/Server/pull/3870)) @fryguy503 2024-01-07
* Legacy Fizzle Code ([#3868](https://github.com/EQEmu/Server/pull/3868)) @fryguy503 2024-01-07
* Legacy Manaburn Rule ([#3872](https://github.com/EQEmu/Server/pull/3872)) @fryguy503 2024-01-07
### Fixes
* Add locations where melee can be bound outside of a city. ([#3887](https://github.com/EQEmu/Server/pull/3887)) @fryguy503 2024-01-07
* Amplification should not benefit from instrument mods ([#3898](https://github.com/EQEmu/Server/pull/3898)) @fryguy503 2024-01-07
* Bard Caster Level Fixes ([#3883](https://github.com/EQEmu/Server/pull/3883)) @fryguy503 2024-01-07
* Buff Sync ([#3896](https://github.com/EQEmu/Server/pull/3896)) @fryguy503 2024-01-07
* Cancel Magic SE fix ([#3890](https://github.com/EQEmu/Server/pull/3890)) @fryguy503 2024-01-07
* Class Trainers dont steal your money! ([#3864](https://github.com/EQEmu/Server/pull/3864)) @fryguy503 2024-01-07
* Clear Ramp when Clearing hate ([#3892](https://github.com/EQEmu/Server/pull/3892)) @fryguy503 2024-01-07
* DI/Death Pact Fix ([#3867](https://github.com/EQEmu/Server/pull/3867)) @fryguy503 2024-01-07
* Depop Charm Pet and Detach Debuffs on Evacuate ([#3888](https://github.com/EQEmu/Server/pull/3888)) @fryguy503 2024-01-07
* Dire Charm Reset ([#3875](https://github.com/EQEmu/Server/pull/3875)) @fryguy503 2024-01-07
* Disciplines Getting Focuses Fix ([#3884](https://github.com/EQEmu/Server/pull/3884)) @fryguy503 2024-01-07
* Fix issue with 9th/10th inventory slot ([#3835](https://github.com/EQEmu/Server/pull/3835)) @Kinglykrab 2024-01-03
* Fix typo on ZoneDatabase::LoadPetInfo ([#3871](https://github.com/EQEmu/Server/pull/3871)) @Kinglykrab 2024-01-07
* Harm Touch Unholy Disc Type ([#3874](https://github.com/EQEmu/Server/pull/3874)) @fryguy503 2024-01-07
* Prevent QS Crashes ([#3877](https://github.com/EQEmu/Server/pull/3877)) @fryguy503 2024-01-07
* Rez Effects Stacking ([#3882](https://github.com/EQEmu/Server/pull/3882)) @fryguy503 2024-01-07
* Rez in zone clear aggro ([#3895](https://github.com/EQEmu/Server/pull/3895)) @fryguy503 2024-01-07
* Rune Invis Break ([#3893](https://github.com/EQEmu/Server/pull/3893)) @fryguy503 2024-01-07
* Snare and DOT Stacking ([#3897](https://github.com/EQEmu/Server/pull/3897)) @fryguy503 2024-01-07
* Swim Skillup and Underwater Fall Damage Fix ([#3885](https://github.com/EQEmu/Server/pull/3885)) @fryguy503 2024-01-07
* Swimming Rules Adjustment and Racial ([#3889](https://github.com/EQEmu/Server/pull/3889)) @fryguy503 2024-01-07
* Target Locked Pet Taunt ([#3894](https://github.com/EQEmu/Server/pull/3894)) @fryguy503 2024-01-07
* Vampiric Embrace Fixes ([#3873](https://github.com/EQEmu/Server/pull/3873)) @fryguy503 2024-01-07
### Languages
* Cleanup language constants, use repositories ([#3838](https://github.com/EQEmu/Server/pull/3838)) @Kinglykrab 2024-01-07
### Pets
* Convert Load/Save of Pet Info to Repositories ([#3853](https://github.com/EQEmu/Server/pull/3853)) @Kinglykrab 2024-01-07
### Rules
* Casting Charm on over level = Aggro ([#3886](https://github.com/EQEmu/Server/pull/3886)) @fryguy503 2024-01-07
* Classic Invite Requires Target ([#3878](https://github.com/EQEmu/Server/pull/3878)) @fryguy503 2024-01-07
* Evac Aggro Wipe ([#3880](https://github.com/EQEmu/Server/pull/3880)) @fryguy503 2024-01-07
* Mounts will wear off on zone ([#3865](https://github.com/EQEmu/Server/pull/3865)) @fryguy503 2024-01-07
* PC Push and NPCtoNPC Push ([#3879](https://github.com/EQEmu/Server/pull/3879)) @fryguy503 2024-01-07
* RequireMnemonicRetention for Spells 9-12 Rule ([#3876](https://github.com/EQEmu/Server/pull/3876)) @fryguy503 2024-01-07
* Resist Softcap rules ([#3863](https://github.com/EQEmu/Server/pull/3863)) @fryguy503 2024-01-07
* Restrict Finishing Blow to only Fleeing NPC's. ([#3869](https://github.com/EQEmu/Server/pull/3869)) @fryguy503 2024-01-07
* Undead Aggro ([#3881](https://github.com/EQEmu/Server/pull/3881)) @fryguy503 2024-01-07
### Tuning
* FD and Sneak break when cast on adjustments. ([#3861](https://github.com/EQEmu/Server/pull/3861)) @fryguy503 2024-01-07
### UCS
* Consolidate configuration block ([#3768](https://github.com/EQEmu/Server/pull/3768)) @Akkadius 2024-01-07
## [22.39.1] - 12/31/2023
### Code
* Appearance not appearence ([#3819](https://github.com/EQEmu/Server/pull/3819)) @Kinglykrab 2023-12-30
* Delete errmsg.h in common and zone ([#3821](https://github.com/EQEmu/Server/pull/3821)) @Kinglykrab 2023-12-30
* Gender constants cleanup ([#3817](https://github.com/EQEmu/Server/pull/3817)) @Kinglykrab 2023-12-30
* Remove MakeSpawnUpdateNoDelta from mob.cpp/mob.h ([#3816](https://github.com/EQEmu/Server/pull/3816)) @Kinglykrab 2023-12-31
* Remove SendStunAppearance from mob.cpp/mob.h ([#3818](https://github.com/EQEmu/Server/pull/3818)) @Kinglykrab 2023-12-31
* Remove unused PlotPosition methods from mob.cpp/mob.h ([#3820](https://github.com/EQEmu/Server/pull/3820)) @Kinglykrab 2023-12-31
### Database
* Database update improvements, content db and terminal checks ([#3814](https://github.com/EQEmu/Server/pull/3814)) @Akkadius 2023-12-31
### Fixes
* Client:SetBucket Overload Incorrectly Named ([#3825](https://github.com/EQEmu/Server/pull/3825)) @fryguy503 2023-12-30
* Fix crash in Client::Handle_OP_GMGoto ([#3832](https://github.com/EQEmu/Server/pull/3832)) @Kinglykrab 2023-12-31
### Quest API
* Add HasItemOnCorpse() to Perl/Lua ([#3824](https://github.com/EQEmu/Server/pull/3824)) @Kinglykrab 2023-12-31
* Fix issue with death events. ([#3823](https://github.com/EQEmu/Server/pull/3823)) @Kinglykrab 2023-12-31
### Repositories
* Protected extended repositories from being overwritten if exists ([#3815](https://github.com/EQEmu/Server/pull/3815)) @Akkadius 2023-12-31
## [22.39.0] - 12/27/2023
### Character
* Fix character copier due to schema change ([#3805](https://github.com/EQEmu/Server/pull/3805)) @Akkadius 2023-12-28
### Combat
* Disarm was not dropping item to ground due to bug ([#3811](https://github.com/EQEmu/Server/pull/3811)) @noudess 2023-12-27
### Logs
* Bulk insert new log settings ([#3810](https://github.com/EQEmu/Server/pull/3810)) @Akkadius 2023-12-28
* Reclassify unhelpful Info message ([#3809](https://github.com/EQEmu/Server/pull/3809)) @Akkadius 2023-12-28
### MySQL
* Fix MySQL Query error formatting ([#3808](https://github.com/EQEmu/Server/pull/3808)) @Akkadius 2023-12-28
### Objects
* Remove "No objects to load for zone" error message ([#3807](https://github.com/EQEmu/Server/pull/3807)) @Akkadius 2023-12-28
### Player Events
* Bulk replace settings on boot ([#3806](https://github.com/EQEmu/Server/pull/3806)) @Akkadius 2023-12-28
### Tasks
* Add enabled column ([#3804](https://github.com/EQEmu/Server/pull/3804)) @Akkadius 2023-12-28
## [22.38.0] - 12/26/2023
### Bots
-3
View File
@@ -13,7 +13,6 @@ SET(common_sources
crc32.cpp
database/database_dump_service.cpp
database.cpp
database_conversions.cpp
database_instances.cpp
database/database_update_manifest.cpp
database/database_update_manifest_bots.cpp
@@ -541,7 +540,6 @@ SET(common_headers
events/player_event_logs.h
events/player_event_discord_formatter.h
events/player_events.h
errmsg.h
event_sub.h
expedition_lockout_timer.h
extprofile.h
@@ -563,7 +561,6 @@ SET(common_headers
item_fieldlist.h
item_instance.h
json_config.h
languages.h
light_source.h
linked_list.h
loottable.h
+9 -1
View File
@@ -2246,6 +2246,11 @@ bool Database::CopyCharacter(
row = results.begin();
std::string new_character_id = row[0];
std::vector<std::string> tables_to_zero_id = {
"keyring",
"data_buckets",
};
TransactionBegin();
for (const auto &iter : DatabaseSchema::GetCharacterTables()) {
std::string table_name = iter.first;
@@ -2279,6 +2284,10 @@ bool Database::CopyCharacter(
std::string column = columns[column_index];
std::string value = row[column_index] ? row[column_index] : "null";
if (column == "id" && Strings::Contains(tables_to_zero_id, table_name)) {
value = "0";
}
if (column == character_id_column_name) {
value = new_character_id;
}
@@ -2326,7 +2335,6 @@ bool Database::CopyCharacter(
if (!insert.ErrorMessage().empty()) {
TransactionRollback();
return false;
break;
}
}
}
-5
View File
@@ -226,11 +226,6 @@ public:
void PurgeAllDeletedDataBuckets();
/* Database Conversions 'database_conversions.cpp' */
bool CheckDatabaseConversions();
bool CheckDatabaseConvertCorpseDeblob();
bool CheckDatabaseConvertPPDeblob();
/* Database Variables */
+49 -25
View File
@@ -76,9 +76,9 @@ void DatabaseUpdate::CheckDbUpdates()
}
}
std::string DatabaseUpdate::GetQueryResult(std::string query)
std::string DatabaseUpdate::GetQueryResult(const ManifestEntry& e)
{
auto results = m_database->QueryDatabase(query);
auto results = (e.content_schema_update ? m_content_database : m_database)->QueryDatabase(e.check);
std::vector<std::string> result_lines = {};
@@ -121,6 +121,16 @@ bool DatabaseUpdate::ShouldRunMigration(ManifestEntry &e, std::string query_resu
return false;
}
// check if we are running in a terminal
bool is_atty()
{
#ifdef _WINDOWS
return ::_isatty(_fileno(stdin));
#else
return isatty(fileno(stdin));
#endif
}
// return true if we ran updates
bool DatabaseUpdate::UpdateManifest(
std::vector<ManifestEntry> entries,
@@ -136,7 +146,7 @@ bool DatabaseUpdate::UpdateManifest(
for (auto &e: entries) {
if (e.version == version) {
bool has_migration = true;
std::string r = GetQueryResult(e.check);
std::string r = GetQueryResult(e);
if (ShouldRunMigration(e, r)) {
has_migration = false;
missing_migrations.emplace_back(e.version);
@@ -179,7 +189,7 @@ bool DatabaseUpdate::UpdateManifest(
if (e.version == m) {
bool errored_migration = false;
auto r = m_database->QueryDatabaseMulti(e.sql);
auto r = (e.content_schema_update ? m_content_database : m_database)->QueryDatabaseMulti(e.sql);
// ignore empty query result "errors"
if (r.ErrorNumber() != 1065 && !r.ErrorMessage().empty()) {
@@ -187,31 +197,38 @@ bool DatabaseUpdate::UpdateManifest(
errored_migration = true;
LogInfo("Required database update failed. This could be a problem");
LogInfo("Would you like to skip this update? [y/n] (Timeout 60s)");
// user input
std::string input;
bool gave_input = false;
time_t start_time = time(nullptr);
time_t wait_time_seconds = 60;
// if terminal attached then prompt for skip
if (is_atty()) {
LogInfo("Would you like to skip this update? [y/n] (Timeout 60s)");
// spawn a concurrent thread that waits for input from std::cin
std::thread t1(
[&]() {
std::cin >> input;
gave_input = true;
// user input
std::string input;
bool gave_input = false;
time_t start_time = time(nullptr);
time_t wait_time_seconds = 60;
// spawn a concurrent thread that waits for input from std::cin
std::thread t1(
[&]() {
std::cin >> input;
gave_input = true;
}
);
t1.detach();
// check the inputReceived flag once every 50ms for 10 seconds
while (time(nullptr) < start_time + wait_time_seconds && !gave_input) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
);
t1.detach();
// check the inputReceived flag once every 50ms for 10 seconds
while (time(nullptr) < start_time + wait_time_seconds && !gave_input) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
// prompt for user skip
if (Strings::Trim(input) == "y") {
errored_migration = false;
// prompt for user skip
if (Strings::Trim(input) == "y") {
errored_migration = false;
LogInfo("Skipping update [{}] [{}]", e.version, e.description);
}
} else {
errored_migration = true;
LogInfo("Skipping update [{}] [{}]", e.version, e.description);
}
}
@@ -247,6 +264,13 @@ DatabaseUpdate *DatabaseUpdate::SetDatabase(Database *db)
return this;
}
DatabaseUpdate *DatabaseUpdate::SetContentDatabase(Database *db)
{
m_content_database = db;
return this;
}
bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b)
{
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
+4 -1
View File
@@ -10,6 +10,7 @@ struct ManifestEntry {
std::string condition{}; // condition or "match_type" - Possible values [contains|match|missing|empty|not_empty]
std::string match{}; // match field that is not always used, but works in conjunction with "condition" values [missing|match|contains]
std::string sql{}; // the SQL DDL that gets ran when the condition is true
bool content_schema_update{}; // if true, this migration is a content schema update and should be ran against the content database
};
struct DatabaseVersion {
@@ -22,14 +23,16 @@ public:
DatabaseVersion GetDatabaseVersions();
DatabaseVersion GetBinaryDatabaseVersions();
void CheckDbUpdates();
std::string GetQueryResult(std::string query);
std::string GetQueryResult(const ManifestEntry& e);
static bool ShouldRunMigration(ManifestEntry &e, std::string query_result);
bool UpdateManifest(std::vector<ManifestEntry> entries, int version_low, int version_high);
DatabaseUpdate *SetDatabase(Database *db);
DatabaseUpdate *SetContentDatabase(Database *db);
bool HasPendingUpdates();
private:
Database *m_database;
Database *m_content_database;
static bool CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b);
void InjectBotsVersionColumn();
};
+25 -7
View File
@@ -5016,7 +5016,7 @@ CREATE TABLE `spawn2_disabled` (
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
INSERT INTO spawn2_disabled (spawn2_id, disabled) SELECT id, 1 FROM spawn2 WHERE enabled = 0;
ALTER TABLE `spawn2` DROP COLUMN `enabled`;
)"
)",
},
ManifestEntry{
.version = 9242,
@@ -5028,7 +5028,8 @@ ALTER TABLE `spawn2` DROP COLUMN `enabled`;
ALTER TABLE `spawnentry`
ADD COLUMN `min_time` smallint(4) NOT NULL DEFAULT 0 AFTER `condition_value_filter`,
ADD COLUMN `max_time` smallint(4) NOT NULL DEFAULT 0 AFTER `min_time`;
)"
)",
.content_schema_update = true
},
ManifestEntry{
.version = 9243,
@@ -5082,7 +5083,8 @@ INSERT INTO
DROP TABLE `starting_items`;
RENAME TABLE `starting_items_new` TO `starting_items`;
)"
)",
.content_schema_update = true
},
ManifestEntry{
.version = 9244,
@@ -5092,7 +5094,8 @@ RENAME TABLE `starting_items_new` TO `starting_items`;
.match = "0000-00-00 00:00:00",
.sql = R"(
ALTER TABLE `items` MODIFY COLUMN `updated` datetime NULL DEFAULT NULL;
)"
)",
.content_schema_update = true
},
ManifestEntry{
.version = 9245,
@@ -5104,7 +5107,8 @@ ALTER TABLE `items` MODIFY COLUMN `updated` datetime NULL DEFAULT NULL;
ALTER TABLE `object` CHANGE COLUMN `unknown08` `size_percentage` float NOT NULL DEFAULT 0 AFTER `icon`;
ALTER TABLE `object` CHANGE COLUMN `unknown10` `solid_type` mediumint(5) NOT NULL DEFAULT 0 AFTER `size`;
ALTER TABLE `object` CHANGE COLUMN `unknown20` `incline` int(11) NOT NULL DEFAULT 0 AFTER `solid_type`;
)"
)",
.content_schema_update = true
},
ManifestEntry{
.version = 9246,
@@ -5133,7 +5137,8 @@ CHANGE COLUMN `slot` `inventory_slot` mediumint(9) NOT NULL DEFAULT -1 AFTER `st
ALTER TABLE `starting_items`
CHANGE COLUMN `temporary` `class_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `id`;
)"
)",
.content_schema_update = true
},
ManifestEntry{
.version = 9248,
@@ -5143,7 +5148,20 @@ CHANGE COLUMN `temporary` `class_list` text CHARACTER SET latin1 COLLATE latin1_
.match = "",
.sql = R"(
ALTER TABLE `npc_emotes` DROP INDEX `emoteid`;
)"
)",
.content_schema_update = true
},
ManifestEntry{
.version = 9249,
.description = "2023_12_26_add_tasks_enabled_column.sql",
.check = "SHOW COLUMNS FROM `tasks` LIKE 'enabled'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `tasks`
ADD COLUMN `enabled` smallint NULL DEFAULT 1 AFTER `faction_amount`
)",
.content_schema_update = true
}
// -- template; copy/paste this when you need to create a new entry
-39
View File
@@ -1,39 +0,0 @@
#include "../common/global_define.h"
#include "../common/rulesys.h"
#include "../common/strings.h"
#include "database.h"
#include "database/database_update.h"
// Disgrace: for windows compile
#ifdef _WINDOWS
#include <windows.h>
#define snprintf _snprintf
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#else
#include "unix.h"
#include <netinet/in.h>
#include <sys/time.h>
#endif
#pragma pack(1)
DatabaseUpdate database_update;
bool Database::CheckDatabaseConversions()
{
auto *r = RuleManager::Instance();
r->LoadRules(this, "default", false);
if (!RuleB(Bots, Enabled) && DoesTableExist("bot_data")) {
LogInfo("Bot tables found but rule not enabled, enabling");
r->SetRule("Bots:Enabled", "true", this, true, true);
}
database_update.SetDatabase(this)->CheckDbUpdates();
return true;
}
+1 -1
View File
@@ -71,7 +71,7 @@ namespace DatabaseSchema {
{"character_tasks", "charid"},
{"character_tribute", "character_id"},
{"completed_tasks", "charid"},
{"data_buckets", "id"},
{"data_buckets", "character_id"},
{"faction_values", "char_id"},
{"friends", "charid"},
{"guild_members", "char_id"},
+1 -2
View File
@@ -8,7 +8,6 @@
#include "dbcore.h"
#include <errmsg.h>
#include <fstream>
#include <iostream>
#include <mysqld_error.h>
@@ -138,7 +137,7 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
* Error logging
*/
if (mysql_errno(mysql) > 0 && query[0] != '\0') {
LogMySQLError("[{}] [{}]\n[{}]", mysql_errno(mysql), mysql_error(mysql), query);
LogMySQLError("MySQL Error ({}) [{}] Query [{}]", mysql_errno(mysql), mysql_error(mysql), query);
}
return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(mysql), errorBuffer);
+3
View File
@@ -14,6 +14,9 @@
#include <string.h>
#include <mutex>
#define CR_SERVER_GONE_ERROR 2006
#define CR_SERVER_LOST 2013
class DBcore {
public:
enum eStatus {
+92 -34
View File
@@ -22,7 +22,6 @@
#include "data_verification.h"
#include "eqemu_logsys.h"
#include "eqemu_logsys_log_aliases.h"
#include "languages.h"
#include "rulesys.h"
int16 EQ::invtype::GetInvTypeSize(int16 inv_type) {
@@ -159,45 +158,45 @@ int EQ::constants::ConvertStanceTypeToIndex(StanceType stance_type) {
return 0;
}
const std::map<int, std::string>& EQ::constants::GetLanguageMap()
const std::map<uint8, std::string>& EQ::constants::GetLanguageMap()
{
static const std::map<int, std::string> language_map = {
{ LANG_COMMON_TONGUE, "Common Tongue" },
{ LANG_BARBARIAN, "Barbarian" },
{ LANG_ERUDIAN, "Erudian" },
{ LANG_ELVISH, "Elvish" },
{ LANG_DARK_ELVISH, "Dark Elvish" },
{ LANG_DWARVISH, "Dwarvish" },
{ LANG_TROLL, "Troll" },
{ LANG_OGRE, "Ogre" },
{ LANG_GNOMISH, "Gnomish" },
{ LANG_HALFLING, "Halfling" },
{ LANG_THIEVES_CANT, "Thieves Cant" },
{ LANG_OLD_ERUDIAN, "Old Erudian" },
{ LANG_ELDER_ELVISH, "Elder Elvish" },
{ LANG_FROGLOK, "Froglok" },
{ LANG_GOBLIN, "Goblin" },
{ LANG_GNOLL, "Gnoll" },
{ LANG_COMBINE_TONGUE, "Combine Tongue" },
{ LANG_ELDER_TEIRDAL, "Elder Teirdal" },
{ LANG_LIZARDMAN, "Lizardman" },
{ LANG_ORCISH, "Orcish" },
{ LANG_FAERIE, "Faerie" },
{ LANG_DRAGON, "Dragon" },
{ LANG_ELDER_DRAGON, "Elder Dragon" },
{ LANG_DARK_SPEECH, "Dark Speech" },
{ LANG_VAH_SHIR, "Vah Shir" },
{ LANG_ALARAN, "Alaran" },
{ LANG_HADAL, "Hadal" },
{ LANG_UNKNOWN, "Unknown" }
static const std::map<uint8, std::string> language_map = {
{ Language::CommonTongue, "Common Tongue" },
{ Language::Barbarian, "Barbarian" },
{ Language::Erudian, "Erudian" },
{ Language::Elvish, "Elvish" },
{ Language::DarkElvish, "Dark Elvish" },
{ Language::Dwarvish, "Dwarvish" },
{ Language::Troll, "Troll" },
{ Language::Ogre, "Ogre" },
{ Language::Gnomish, "Gnomish" },
{ Language::Halfling, "Halfling" },
{ Language::ThievesCant, "Thieves Cant" },
{ Language::OldErudian, "Old Erudian" },
{ Language::ElderElvish, "Elder Elvish" },
{ Language::Froglok, "Froglok" },
{ Language::Goblin, "Goblin" },
{ Language::Gnoll, "Gnoll" },
{ Language::CombineTongue, "Combine Tongue" },
{ Language::ElderTeirDal, "Elder Teir'Dal" },
{ Language::Lizardman, "Lizardman" },
{ Language::Orcish, "Orcish" },
{ Language::Faerie, "Faerie" },
{ Language::Dragon, "Dragon" },
{ Language::ElderDragon, "Elder Dragon" },
{ Language::DarkSpeech, "Dark Speech" },
{ Language::VahShir, "Vah Shir" },
{ Language::Alaran, "Alaran" },
{ Language::Hadal, "Hadal" },
{ Language::Unknown27, "Unknown" }
};
return language_map;
}
std::string EQ::constants::GetLanguageName(int language_id)
std::string EQ::constants::GetLanguageName(uint8 language_id)
{
if (!EQ::ValueWithin(language_id, LANG_COMMON_TONGUE, LANG_UNKNOWN)) {
if (!EQ::ValueWithin(language_id, Language::CommonTongue, Language::Unknown27)) {
return std::string();
}
@@ -509,7 +508,6 @@ std::string EQ::constants::GetObjectTypeName(int object_type)
{
if (!EQ::ValueWithin(object_type, ObjectTypes::SmallBag, ObjectTypes::NoDeposit)) {
return std::string();
}
return EQ::constants::GetObjectTypeMap().find(object_type)->second;
@@ -580,3 +578,63 @@ std::string EQ::constants::GetEmoteTypeName(uint8 emote_type)
return EQ::constants::GetEmoteTypeMap().find(emote_type)->second;
}
const std::map<uint32, std::string>& EQ::constants::GetAppearanceTypeMap()
{
static const std::map<uint32, std::string> appearance_type_map = {
{ AppearanceType::Die, "Die" },
{ AppearanceType::WhoLevel, "Who Level" },
{ AppearanceType::MaxHealth, "Max Health" },
{ AppearanceType::Invisibility, "Invisibility" },
{ AppearanceType::PVP, "PVP" },
{ AppearanceType::Light, "Light" },
{ AppearanceType::Animation, "Animation" },
{ AppearanceType::Sneak, "Sneak" },
{ AppearanceType::SpawnID, "Spawn ID" },
{ AppearanceType::Health, "Health" },
{ AppearanceType::Linkdead, "Linkdead" },
{ AppearanceType::FlyMode, "Fly Mode" },
{ AppearanceType::GM, "GM" },
{ AppearanceType::Anonymous, "Anonymous" },
{ AppearanceType::GuildID, "Guild ID" },
{ AppearanceType::GuildRank, "Guild Rank" },
{ AppearanceType::AFK, "AFK" },
{ AppearanceType::Pet, "Pet" },
{ AppearanceType::Summoned, "Summoned" },
{ AppearanceType::Split, "Split" },
{ AppearanceType::Size, "Size" },
{ AppearanceType::SetType, "Set Type" },
{ AppearanceType::NPCName, "NPCName" },
{ AppearanceType::AARank, "AARank" },
{ AppearanceType::CancelSneakHide, "Cancel Sneak Hide" },
{ AppearanceType::AreaHealthRegen, "Area Health Regeneration" },
{ AppearanceType::AreaManaRegen, "Area Mana Regeneration" },
{ AppearanceType::AreaEnduranceRegen, "Area Endurance Regeneration" },
{ AppearanceType::FreezeBeneficialBuffs, "Freeze Beneficial Buffs" },
{ AppearanceType::NPCTintIndex, "NPC Tint Index" },
{ AppearanceType::GroupAutoConsent, "Group Auto Consent" },
{ AppearanceType::RaidAutoConsent, "Raid Auto Consent" },
{ AppearanceType::GuildAutoConsent, "Guild Auto Consent" },
{ AppearanceType::ShowHelm, "Show Helm" },
{ AppearanceType::DamageState, "Damage State" },
{ AppearanceType::EQPlayers, "EQ Players" },
{ AppearanceType::FindBits, "Find Bits" },
{ AppearanceType::TextureType, "Texture Type" },
{ AppearanceType::FacePick, "Face Pick" },
{ AppearanceType::AntiCheat, "Anti Cheat" },
{ AppearanceType::GuildShow, "Guild Show" },
{ AppearanceType::OfflineMode, "Offline Mode" }
};
return appearance_type_map;
}
std::string EQ::constants::GetAppearanceTypeName(uint32 appearance_type)
{
const auto& a = EQ::constants::GetAppearanceTypeMap().find(appearance_type);
if (a != EQ::constants::GetAppearanceTypeMap().end()) {
return a->second;
}
return std::string();
}
+5 -2
View File
@@ -358,8 +358,8 @@ namespace EQ
const char *GetStanceName(StanceType stance_type);
int ConvertStanceTypeToIndex(StanceType stance_type);
extern const std::map<int, std::string>& GetLanguageMap();
std::string GetLanguageName(int language_id);
extern const std::map<uint8, std::string>& GetLanguageMap();
std::string GetLanguageName(uint8 language_id);
extern const std::map<uint32, std::string>& GetLDoNThemeMap();
std::string GetLDoNThemeName(uint32 theme_id);
@@ -397,6 +397,9 @@ namespace EQ
extern const std::map<uint8, std::string>& GetEmoteTypeMap();
std::string GetEmoteTypeName(uint8 emote_type);
extern const std::map<uint32, std::string>& GetAppearanceTypeMap();
std::string GetAppearanceTypeName(uint32 animation_type);
const int STANCE_TYPE_FIRST = stancePassive;
const int STANCE_TYPE_LAST = stanceBurnAE;
const int STANCE_TYPE_COUNT = stanceBurnAE;
+99 -54
View File
@@ -23,61 +23,59 @@
#include "skills.h"
#include "types.h"
namespace AppearanceType {
constexpr uint32 Die = 0; // Causes the client to keel over and zone to bind point (default action)
constexpr uint32 WhoLevel = 1; // Level that shows up on /who
constexpr uint32 MaxHealth = 2;
constexpr uint32 Invisibility = 3; // 0 = Visible, 1 = Invisible
constexpr uint32 PVP = 4; // 0 = Non-PVP, 1 = PVP
constexpr uint32 Light = 5; // Light type emitted by player (lightstone, shiny shield)
constexpr uint32 Animation = 14; // 100 = Standing, 102 = Freeze, 105 = Looting, 110 = Sitting, 111 = Crouching, 115 = Lying
constexpr uint32 Sneak = 15; // 0 = Normal, 1 = Sneaking
constexpr uint32 SpawnID = 16; // Server -> Client, sets player spawn ID
constexpr uint32 Health = 17; // Client->Server, my HP has changed (like regen tic)
constexpr uint32 Linkdead = 18; // 0 = Normal, 1 = Linkdead
constexpr uint32 FlyMode = 19; // 0 = Off, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating while Running
constexpr uint32 GM = 20; // 0 = Non-GM, 1 = GM
constexpr uint32 Anonymous = 21; // 0 = Non-Anonymous, 1 = Anonymous, 2 = Roleplaying
constexpr uint32 GuildID = 22;
constexpr uint32 GuildRank = 23;
constexpr uint32 AFK = 24; // 0 = Non-AFK, 1 = AFK
constexpr uint32 Pet = 25; // Parameter is Entity ID of owner, or 0 for when charm breaks
constexpr uint32 Summoned = 27;
constexpr uint32 Split = 28; // 0 = No Split, 1 = Auto Split
constexpr uint32 Size = 29; // Spawn's Size
constexpr uint32 SetType = 30; // 0 = PC, 1 = NPC, 2 = Corpse
constexpr uint32 NPCName = 31; // Change PC name color to NPC name color
constexpr uint32 AARank = 32; // AA Rank Title ID, title in /who?
constexpr uint32 CancelSneakHide = 33; // Turns off Hide and Sneak
constexpr uint32 AreaHealthRegen = 35; // Guild Hall Regeneration Pool sets to value * 0.001
constexpr uint32 AreaManaRegen = 36; // Guild Hall Regeneration Pool sets to value * 0.001
constexpr uint32 AreaEnduranceRegen = 37; // Guild Hall Regeneration Pool sets to value * 0.001
constexpr uint32 FreezeBeneficialBuffs = 38; // Freezes beneficial buff timers for PCs
constexpr uint32 NPCTintIndex = 39;
constexpr uint32 GroupAutoConsent = 40; // Auto Consent Group
constexpr uint32 RaidAutoConsent = 41; // Auto Consent Raid
constexpr uint32 GuildAutoConsent = 42; // Auto Consent Guild
constexpr uint32 ShowHelm = 43; // 0 = Hide, 1 = Show
constexpr uint32 DamageState = 44; // The damage state of a destructible object (0 through 10) plays sound IDs, most only have 2 or 4 states though
constexpr uint32 EQPlayers = 45; // EQ Players Update
constexpr uint32 FindBits = 46; // Set Find Bits?
constexpr uint32 TextureType = 48; // Texture Type?
constexpr uint32 FacePick = 49; // Turns off face pick window?
constexpr uint32 AntiCheat = 51; // Sent by the client randomly telling the server how long since last action has occurred
constexpr uint32 GuildShow = 52;
constexpr uint32 OfflineMode = 53; // Offline Mode
}
//SpawnAppearance types: (compared two clients for server-originating types: SoF & RoF2)
#define AT_Die 0 // this causes the client to keel over and zone to bind point (default action)
#define AT_WhoLevel 1 // the level that shows up on /who
#define AT_HPMax 2 // idk
#define AT_Invis 3 // 0 = visible, 1 = invisible
#define AT_PVP 4 // 0 = blue, 1 = pvp (red)
#define AT_Light 5 // light type emitted by player (lightstone, shiny shield)
#define AT_Anim 14 // 100=standing, 110=sitting, 111=ducking, 115=feigned, 105=looting
#define AT_Sneak 15 // 0 = normal, 1 = sneaking
#define AT_SpawnID 16 // server to client, sets player spawn id
#define AT_HP 17 // Client->Server, my HP has changed (like regen tic)
#define AT_Linkdead 18 // 0 = normal, 1 = linkdead
#define AT_Levitate 19 // 0=off, 1=flymode, 2=levitate max 5, see GravityBehavior enum
#define AT_GM 20 // 0 = normal, 1 = GM - all odd numbers seem to make it GM
#define AT_Anon 21 // 0 = normal, 1 = anon, 2 = roleplay
#define AT_GuildID 22
#define AT_GuildRank 23 // 0=member, 1=officer, 2=leader
#define AT_AFK 24 // 0 = normal, 1 = afk
#define AT_Pet 25 // Param is EntityID of owner, or 0 for when charm breaks
#define AT_Summoned 27 // Unsure
#define AT_Split 28 // 0 = normal, 1 = autosplit on (not showing in SoF+) (client-to-server only)
#define AT_Size 29 // spawn's size (present: SoF, absent: RoF2)
#define AT_SetType 30 // 0 = PC, 1 = NPC, 2 <= = corpse
#define AT_NPCName 31 // change PC's name's color to NPC color 0 = normal, 1 = npc name, Trader on RoF2?
#define AT_AARank 32 // AA Rank Title ID thingy, does is this the title in /who?
#define AT_CancelSneakHide 33 // Turns off Hide and Sneak
//#define AT_34 34 // unknown (present: SoF, absent: RoF2)
#define AT_AreaHPRegen 35 // guild hall regen pool sets to value * 0.001
#define AT_AreaManaRegen 36 // guild hall regen pool sets to value * 0.001
#define AT_AreaEndRegen 37 // guild hall regen pool sets to value * 0.001
#define AT_FreezeBuffs 38 // Freezes beneficial buff timers
#define AT_NpcTintIndex 39 // not 100% sure
#define AT_GroupConsent 40 // auto consent group
#define AT_RaidConsent 41 // auto consent raid
#define AT_GuildConsent 42 // auto consent guild
#define AT_ShowHelm 43 // 0 = hide graphic, 1 = show graphic
#define AT_DamageState 44 // The damage state of a destructible object (0 through 10) plays soundids most only have 2 or 4 states though
#define AT_EQPlayers 45 // /eqplayersupdate
#define AT_FindBits 46 // set FindBits, whatever those are!
#define AT_TextureType 48 // TextureType
#define AT_FacePick 49 // Turns off face pick window? maybe ...
#define AT_AntiCheat 51 // sent by the client randomly telling the server how long since last action has occured
#define AT_GuildShow 52 // this is what MQ2 call sit, not sure
#define AT_Offline 53 // Offline mode
//#define AT_Trader 300 // Bazaar Trader Mode (not present in SoF or RoF2)
// animations for AT_Anim
#define ANIM_FREEZE 102
#define ANIM_STAND 0x64
#define ANIM_SIT 0x6e
#define ANIM_CROUCH 0x6f
#define ANIM_DEATH 0x73
#define ANIM_LOOT 0x69
namespace Animation {
constexpr uint32 Standing = 100;
constexpr uint32 Freeze = 102;
constexpr uint32 Looting = 105;
constexpr uint32 Sitting = 110;
constexpr uint32 Crouching = 111;
constexpr uint32 Lying = 115;
}
constexpr int16 RECAST_TYPE_UNLINKED_ITEM = -1;
@@ -686,6 +684,53 @@ namespace Zones {
constexpr uint16 APPRENTICE = 999; // Designer Apprentice
}
namespace Language {
constexpr uint8 CommonTongue = 0;
constexpr uint8 Barbarian = 1;
constexpr uint8 Erudian = 2;
constexpr uint8 Elvish = 3;
constexpr uint8 DarkElvish = 4;
constexpr uint8 Dwarvish = 5;
constexpr uint8 Troll = 6;
constexpr uint8 Ogre = 7;
constexpr uint8 Gnomish = 8;
constexpr uint8 Halfling = 9;
constexpr uint8 ThievesCant = 10;
constexpr uint8 OldErudian = 11;
constexpr uint8 ElderElvish = 12;
constexpr uint8 Froglok = 13;
constexpr uint8 Goblin = 14;
constexpr uint8 Gnoll = 15;
constexpr uint8 CombineTongue = 16;
constexpr uint8 ElderTeirDal = 17;
constexpr uint8 Lizardman = 18;
constexpr uint8 Orcish = 19;
constexpr uint8 Faerie = 20;
constexpr uint8 Dragon = 21;
constexpr uint8 ElderDragon = 22;
constexpr uint8 DarkSpeech = 23;
constexpr uint8 VahShir = 24;
constexpr uint8 Alaran = 25;
constexpr uint8 Hadal = 26;
constexpr uint8 Unknown27 = 27;
constexpr uint8 MaxValue = 100;
}
namespace PetInfoType {
constexpr int Current = 0;
constexpr int Suspended = 1;
}
namespace BuffEffectType {
constexpr uint8 None = 0;
constexpr uint8 Buff = 2;
constexpr uint8 InverseBuff = 4;
}
namespace AlternateCurrencyMode {
constexpr uint32 Update = 7;
constexpr uint32 Populate = 8;
}
typedef enum {
FilterNone = 0,
-2
View File
@@ -5134,8 +5134,6 @@ struct GroupMakeLeader_Struct
//ex for a blank crowns window you would send:
//999999|1|999999|0
//any items come after in much the same way adventure merchant items do except there is no theme included
#define ALT_CURRENCY_OP_POPULATE 8
#define ALT_CURRENCY_OP_UPDATE 7
//Server -> Client
//Populates the initial Alternate Currency Window
+110 -15
View File
@@ -20,9 +20,12 @@
#include "eqemu_config.h"
#include "misc_functions.h"
#include "strings.h"
#include "eqemu_logsys.h"
#include "json/json.hpp"
#include <iostream>
#include <sstream>
#include <filesystem>
std::string EQEmuConfig::ConfigFile = "eqemu_config.json";
EQEmuConfig *EQEmuConfig::_config = nullptr;
@@ -111,13 +114,12 @@ void EQEmuConfig::parse_config()
DisableConfigChecks = true;
}
/**
* UCS
*/
ChatHost = _root["server"]["chatserver"].get("host", "eqchat.eqemulator.net").asString();
ChatPort = Strings::ToUnsignedInt(_root["server"]["chatserver"].get("port", "7778").asString());
MailHost = _root["server"]["mailserver"].get("host", "eqmail.eqemulator.net").asString();
MailPort = Strings::ToUnsignedInt(_root["server"]["mailserver"].get("port", "7778").asString());
CheckUcsConfigConversion();
m_ucs_host = _root["server"]["ucs"].get("host", "eqchat.eqemulator.net").asString();
m_ucs_port = Strings::ToUnsignedInt(_root["server"]["ucs"].get("port", "7778").asString());
/**
* Database
@@ -246,16 +248,16 @@ std::string EQEmuConfig::GetByName(const std::string &var_name) const
return (WorldHTTPEnabled ? "true" : "false");
}
if (var_name == "ChatHost") {
return (ChatHost);
return (m_ucs_host);
}
if (var_name == "ChatPort") {
return (itoa(ChatPort));
return (itoa(m_ucs_port));
}
if (var_name == "MailHost") {
return (MailHost);
return (m_ucs_host);
}
if (var_name == "MailPort") {
return (itoa(MailPort));
return (itoa(m_ucs_port));
}
if (var_name == "DatabaseHost") {
return (DatabaseHost);
@@ -362,10 +364,8 @@ void EQEmuConfig::Dump() const
std::cout << "WorldHTTPPort = " << WorldHTTPPort << std::endl;
std::cout << "WorldHTTPMimeFile = " << WorldHTTPMimeFile << std::endl;
std::cout << "WorldHTTPEnabled = " << WorldHTTPEnabled << std::endl;
std::cout << "ChatHost = " << ChatHost << std::endl;
std::cout << "ChatPort = " << ChatPort << std::endl;
std::cout << "MailHost = " << MailHost << std::endl;
std::cout << "MailPort = " << MailPort << std::endl;
std::cout << "UCSHost = " << m_ucs_host << std::endl;
std::cout << "UCSPort = " << m_ucs_port << std::endl;
std::cout << "DatabaseHost = " << DatabaseHost << std::endl;
std::cout << "DatabaseUsername = " << DatabaseUsername << std::endl;
std::cout << "DatabasePassword = " << DatabasePassword << std::endl;
@@ -392,3 +392,98 @@ void EQEmuConfig::Dump() const
std::cout << "DefaultStatus = " << (int) DefaultStatus << std::endl;
// std::cout << "DynamicCount = " << DynamicCount << std::endl;
}
const std::string &EQEmuConfig::GetUCSHost() const
{
return m_ucs_host;
}
uint16 EQEmuConfig::GetUCSPort() const
{
return m_ucs_port;
}
void EQEmuConfig::CheckUcsConfigConversion()
{
std::string chat_host = _root["server"]["chatserver"].get("host", "").asString();
uint32 chat_port = Strings::ToUnsignedInt(_root["server"]["chatserver"].get("port", "0").asString());
std::string mail_host = _root["server"]["mailserver"].get("host", "").asString();
uint32 mail_port = Strings::ToUnsignedInt(_root["server"]["mailserver"].get("port", "0").asString());
std::string ucs_host = _root["server"]["ucs"].get("host", "").asString();
// automatic ucs legacy configuration migration
// if old configuration values are set, let's backup the existing configuration
// and migrate to to use the new fields and write the new config
if ((!chat_host.empty() || !mail_host.empty()) && ucs_host.empty()) {
LogInfo("Migrating old [eqemu_config] UCS configuration to new configuration");
std::string config_file_path = std::filesystem::path{
path.GetServerPath() + "/eqemu_config.json"
}.string();
std::string config_file_bak_path = std::filesystem::path{
path.GetServerPath() + "/eqemu_config.ucs-migrate-json.bak"
}.string();
// copy eqemu_config.json to eqemu_config.json.bak
std::ifstream src(config_file_path, std::ios::binary);
std::ofstream dst(config_file_bak_path, std::ios::binary);
dst << src.rdbuf();
src.close();
LogInfo("Old configuration backed up to [{}]", config_file_bak_path);
// read eqemu_config.json, transplant new fields and write to eqemu_config.json
Json::Value root;
Json::Reader reader;
std::ifstream file(config_file_path);
if (!reader.parse(file, root)) {
LogError("Failed to parse configuration file");
return;
}
file.close();
// get old fields
std::string host = !chat_host.empty() ? chat_host : mail_host;
if (host.empty()) {
host = "eqchat.eqemulator.net";
}
std::string port = chat_port > 0 ? std::to_string(chat_port) : std::to_string(mail_port);
if (port.empty()) {
port = "7778";
}
// set new fields
root["server"]["ucs"]["host"] = host;
root["server"]["ucs"]["port"] = port;
// unset old fields
root["server"].removeMember("chatserver");
root["server"].removeMember("mailserver");
// get Json::Value raw string
std::string config = root.toStyledString();
// format using more modern json library
nlohmann::json data = nlohmann::json::parse(config);
// write to file
std::ofstream o(config_file_path);
o << std::setw(1) << data << std::endl;
o.close();
// write new config
LogInfo("New configuration written to [{}]", config_file_path);
LogInfo("Migration complete, please review the new configuration file");
// reload config internally
try {
std::ifstream fconfig(config_file_path, std::ifstream::binary);
fconfig >> _config->_root;
_config->parse_config();
}
catch (std::exception &) {
return;
}
}
}
+7 -8
View File
@@ -62,14 +62,6 @@ class EQEmuConfig
std::string SharedKey;
bool DisableConfigChecks;
// From <chatserver/>
std::string ChatHost;
uint16 ChatPort;
// From <mailserver/>
std::string MailHost;
uint16 MailPort;
// From <database/>
std::string DatabaseHost;
std::string DatabaseUsername;
@@ -122,12 +114,18 @@ class EQEmuConfig
bool auto_database_updates;
const std::string &GetUCSHost() const;
uint16 GetUCSPort() const;
// uint16 DynamicCount;
// map<string,uint16> StaticZones;
protected:
std::string m_ucs_host;
uint16 m_ucs_port;
static EQEmuConfig *_config;
Json::Value _root;
static std::string ConfigFile;
@@ -186,6 +184,7 @@ class EQEmuConfig
}
void Dump() const;
void CheckUcsConfigConversion();
};
#endif
+11 -7
View File
@@ -651,6 +651,9 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
}
// Auto inject categories that don't exist in the database...
std::vector<LogsysCategoriesRepository::LogsysCategories> db_categories_to_add{};
for (int i = Logs::AA; i != Logs::MaxCategoryID; i++) {
bool is_missing_in_database = std::find(db_categories.begin(), db_categories.end(), i) == db_categories.end();
@@ -665,11 +668,7 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
}
if (is_missing_in_database && !is_deprecated_category) {
LogInfo(
"Automatically adding new log category [{}] ({})",
Logs::LogCategoryName[i],
i
);
LogInfo("Automatically adding new log category [{}] ({})", Logs::LogCategoryName[i], i);
auto new_category = LogsysCategoriesRepository::NewEntity();
new_category.log_category_id = i;
@@ -678,11 +677,16 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
new_category.log_to_gmsay = log_settings[i].log_to_gmsay;
new_category.log_to_file = log_settings[i].log_to_file;
new_category.log_to_discord = log_settings[i].log_to_discord;
LogsysCategoriesRepository::InsertOne(*m_database, new_category);
db_categories_to_add.emplace_back(new_category);
}
}
if (!db_categories_to_add.empty()) {
LogsysCategoriesRepository::ReplaceMany(*m_database, db_categories_to_add);
LoadLogDatabaseSettings();
return this;
}
LogInfo("Loaded [{}] log categories", categories.size());
auto webhooks = DiscordWebhooksRepository::GetWhere(*m_database, fmt::format("id < {}", MAX_DISCORD_WEBHOOK_ID));
-59
View File
@@ -1,59 +0,0 @@
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA */
/* Error messages for mysql clients */
/* error messages for the demon is in share/language/errmsg.sys */
#ifdef __cplusplus
extern "C" {
#endif
void init_client_errs(void);
extern const char *client_errors[]; /* Error messages */
#ifdef __cplusplus
}
#endif
#define CR_MIN_ERROR 2000 /* For easier client code */
#define CR_MAX_ERROR 2999
#if defined(OS2) && defined( MYSQL_SERVER)
#define CER(X) client_errors[(X)-CR_MIN_ERROR]
#else
#define ER(X) client_errors[(X)-CR_MIN_ERROR]
#endif
#define CLIENT_ERRMAP 2 /* Errormap used by my_error() */
#define CR_UNKNOWN_ERROR 2000
#define CR_SOCKET_CREATE_ERROR 2001
#define CR_CONNECTION_ERROR 2002
#define CR_CONN_HOST_ERROR 2003
#define CR_IPSOCK_ERROR 2004
#define CR_UNKNOWN_HOST 2005
#define CR_SERVER_GONE_ERROR 2006
#define CR_VERSION_ERROR 2007
#define CR_OUT_OF_MEMORY 2008
#define CR_WRONG_HOST_INFO 2009
#define CR_LOCALHOST_CONNECTION 2010
#define CR_TCP_CONNECTION 2011
#define CR_SERVER_HANDSHAKE_ERR 2012
#define CR_SERVER_LOST 2013
#define CR_COMMANDS_OUT_OF_SYNC 2014
#define CR_NAMEDPIPE_CONNECTION 2015
#define CR_NAMEDPIPEWAIT_ERROR 2016
#define CR_NAMEDPIPEOPEN_ERROR 2017
#define CR_NAMEDPIPESETSTATE_ERROR 2018
#define CR_CANT_READ_CHARSET 2019
#define CR_NET_PACKET_TOO_LARGE 2020
+9 -7
View File
@@ -37,6 +37,8 @@ void PlayerEventLogs::Init()
db.emplace_back(e.id);
}
std::vector<PlayerEventLogSettingsRepository::PlayerEventLogSettings> settings_to_insert{};
// insert entries that don't exist in database
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
bool is_in_database = std::find(db.begin(), db.end(), i) != db.end();
@@ -56,21 +58,21 @@ void PlayerEventLogs::Init()
bool is_missing_in_database = std::find(db.begin(), db.end(), i) == db.end();
if (is_missing_in_database && is_implemented && !is_deprecated) {
LogInfo(
"[New] PlayerEvent [{}] ({})",
PlayerEvent::EventName[i],
i
);
LogInfo("[New] PlayerEvent [{}] ({})", PlayerEvent::EventName[i], i);
auto c = PlayerEventLogSettingsRepository::NewEntity();
c.id = i;
c.event_name = PlayerEvent::EventName[i];
c.event_enabled = m_settings[i].event_enabled;
c.retention_days = m_settings[i].retention_days;
PlayerEventLogSettingsRepository::InsertOne(*m_database, c);
settings_to_insert.emplace_back(c);
}
}
if (!settings_to_insert.empty()) {
PlayerEventLogSettingsRepository::ReplaceMany(*m_database, settings_to_insert);
}
bool processing_in_world = !RuleB(Logging, PlayerEventsQSProcess) && IsWorld();
bool processing_in_qs = RuleB(Logging, PlayerEventsQSProcess) && IsQueryServ();
@@ -611,7 +613,7 @@ void PlayerEventLogs::Process()
void PlayerEventLogs::ProcessRetentionTruncation()
{
LogInfo("Running truncation");
LogPlayerEvents("Running truncation");
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
if (m_settings[i].retention_days > 0) {
+1 -1
View File
@@ -801,7 +801,7 @@ int16 EQ::InventoryProfile::HasItemByLoreGroup(uint32 loregroup, uint8 where)
// Returns slot_id when there's one available, else SLOT_INVALID
int16 EQ::InventoryProfile::FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size, bool is_arrow)
{
const int16 last_bag_slot = RuleI(World, ExpansionSettings) & EQ::expansions::bitHoT ? EQ::invslot::slotGeneral10 : EQ::invslot::slotGeneral8;
const int16 last_bag_slot = (RuleI(World, ExpansionSettings) == -1 || RuleI(World, ExpansionSettings) & EQ::expansions::bitHoT) ? EQ::invslot::slotGeneral10 : EQ::invslot::slotGeneral8;
for (int16 i = invslot::GENERAL_BEGIN; i <= last_bag_slot; i++) { // Check basic inventory
if ((((uint64) 1 << i) & m_lookup->PossessionsBitmask) == 0) {
-54
View File
@@ -1,54 +0,0 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemulator.org)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef LANGUAGES_H
#define LANGUAGES_H
#include "../common/types.h"
#define LANG_COMMON_TONGUE 0
#define LANG_BARBARIAN 1
#define LANG_ERUDIAN 2
#define LANG_ELVISH 3
#define LANG_DARK_ELVISH 4
#define LANG_DWARVISH 5
#define LANG_TROLL 6
#define LANG_OGRE 7
#define LANG_GNOMISH 8
#define LANG_HALFLING 9
#define LANG_THIEVES_CANT 10
#define LANG_OLD_ERUDIAN 11
#define LANG_ELDER_ELVISH 12
#define LANG_FROGLOK 13
#define LANG_GOBLIN 14
#define LANG_GNOLL 15
#define LANG_COMBINE_TONGUE 16
#define LANG_ELDER_TEIRDAL 17
#define LANG_LIZARDMAN 18
#define LANG_ORCISH 19
#define LANG_FAERIE 20
#define LANG_DRAGON 21
#define LANG_ELDER_DRAGON 22
#define LANG_DARK_SPEECH 23
#define LANG_VAH_SHIR 24
#define LANG_ALARAN 25
#define LANG_HADAL 26
#define LANG_UNKNOWN 27
#define MAX_LANGUAGE_SKILL 100
#endif
+3 -3
View File
@@ -202,7 +202,7 @@ namespace RoF
unsigned char *emu_buffer = in->pBuffer;
uint32 opcode = *((uint32*)emu_buffer);
if (opcode == 8) {
if (opcode == AlternateCurrencyMode::Populate) {
AltCurrencyPopulate_Struct *populate = (AltCurrencyPopulate_Struct*)emu_buffer;
auto outapp = new EQApplicationPacket(
@@ -2621,7 +2621,7 @@ namespace RoF
general->parameter = RaidCommandAcceptInvite;
strn0cpy(general->leader_name, emu->leader_name, sizeof(emu->leader_name));
strn0cpy(general->player_name, emu->leader_name, sizeof(emu->leader_name));
dest->FastQueuePacket(&outapp);
safe_delete(inapp);
@@ -3165,7 +3165,7 @@ namespace RoF
SpawnAppearance_Struct *sas = (SpawnAppearance_Struct *)emu_buffer;
if (sas->type != AT_Size)
if (sas->type != AppearanceType::Size)
{
dest->FastQueuePacket(&in, ack_req);
return;
+2 -2
View File
@@ -274,7 +274,7 @@ namespace RoF2
unsigned char *emu_buffer = in->pBuffer;
uint32 opcode = *((uint32*)emu_buffer);
if (opcode == 8) {
if (opcode == AlternateCurrencyMode::Populate) {
AltCurrencyPopulate_Struct *populate = (AltCurrencyPopulate_Struct*)emu_buffer;
auto outapp = new EQApplicationPacket(
@@ -3218,7 +3218,7 @@ namespace RoF2
SpawnAppearance_Struct *sas = (SpawnAppearance_Struct *)emu_buffer;
if (sas->type != AT_Size)
if (sas->type != AppearanceType::Size)
{
dest->FastQueuePacket(&in, ack_req);
return;
+2 -2
View File
@@ -195,7 +195,7 @@ namespace UF
unsigned char *emu_buffer = in->pBuffer;
uint32 opcode = *((uint32*)emu_buffer);
if (opcode == 8) {
if (opcode == AlternateCurrencyMode::Populate) {
AltCurrencyPopulate_Struct *populate = (AltCurrencyPopulate_Struct*)emu_buffer;
auto outapp = new EQApplicationPacket(
@@ -2339,7 +2339,7 @@ namespace UF
SpawnAppearance_Struct *sas = (SpawnAppearance_Struct *)emu_buffer;
if (sas->type != AT_Size)
if (sas->type != AppearanceType::Size)
{
dest->FastQueuePacket(&in, ack_req);
return;
+3 -2
View File
@@ -44,9 +44,10 @@ enum : int { //values for pTimerType
pTimerBeggingPickPocket = 27,
pTimerLinkedSpellReuseStart = 28,
pTimerLinkedSpellReuseEnd = 48,
pTimerClearXTarget = 50,
pTimerShieldAbility = 86,
pTimerShieldAbility = 86,
pTimerLayHands = 87, //these IDs are used by client too
pTimerHarmTouch = 89, //so dont change them
+36 -36
View File
@@ -1598,7 +1598,7 @@ float GetRaceGenderDefaultHeight(int race, int gender)
return 6.0f;
}
if (gender == FEMALE) {
if (gender == Gender::Female) {
return female_height[race];
}
@@ -1606,38 +1606,38 @@ float GetRaceGenderDefaultHeight(int race, int gender)
}
// PlayerAppearance prep
#define HUMAN_MALE ((HUMAN << 8) | MALE)
#define HUMAN_FEMALE ((HUMAN << 8) | FEMALE)
#define BARBARIAN_MALE ((BARBARIAN << 8) | MALE)
#define BARBARIAN_FEMALE ((BARBARIAN << 8) | FEMALE)
#define ERUDITE_MALE ((ERUDITE << 8) | MALE)
#define ERUDITE_FEMALE ((ERUDITE << 8) | FEMALE)
#define WOOD_ELF_MALE ((WOOD_ELF << 8) | MALE)
#define WOOD_ELF_FEMALE ((WOOD_ELF << 8) | FEMALE)
#define HIGH_ELF_MALE ((HIGH_ELF << 8) | MALE)
#define HIGH_ELF_FEMALE ((HIGH_ELF << 8) | FEMALE)
#define DARK_ELF_MALE ((DARK_ELF << 8) | MALE)
#define DARK_ELF_FEMALE ((DARK_ELF << 8) | FEMALE)
#define HALF_ELF_MALE ((HALF_ELF << 8) | MALE)
#define HALF_ELF_FEMALE ((HALF_ELF << 8) | FEMALE)
#define DWARF_MALE ((DWARF << 8) | MALE)
#define DWARF_FEMALE ((DWARF << 8) | FEMALE)
#define TROLL_MALE ((TROLL << 8) | MALE)
#define TROLL_FEMALE ((TROLL << 8) | FEMALE)
#define OGRE_MALE ((OGRE << 8) | MALE)
#define OGRE_FEMALE ((OGRE << 8) | FEMALE)
#define HALFLING_MALE ((HALFLING << 8) | MALE)
#define HALFLING_FEMALE ((HALFLING << 8) | FEMALE)
#define GNOME_MALE ((GNOME << 8) | MALE)
#define GNOME_FEMALE ((GNOME << 8) | FEMALE)
#define IKSAR_MALE ((IKSAR << 8) | MALE)
#define IKSAR_FEMALE ((IKSAR << 8) | FEMALE)
#define VAHSHIR_MALE ((VAHSHIR << 8) | MALE)
#define VAHSHIR_FEMALE ((VAHSHIR << 8) | FEMALE)
#define FROGLOK_MALE ((FROGLOK << 8) | MALE)
#define FROGLOK_FEMALE ((FROGLOK << 8) | FEMALE)
#define DRAKKIN_MALE ((DRAKKIN << 8) | MALE)
#define DRAKKIN_FEMALE ((DRAKKIN << 8) | FEMALE)
#define HUMAN_MALE ((HUMAN << 8) | Gender::Male)
#define HUMAN_FEMALE ((HUMAN << 8) | Gender::Female)
#define BARBARIAN_MALE ((BARBARIAN << 8) | Gender::Male)
#define BARBARIAN_FEMALE ((BARBARIAN << 8) | Gender::Female)
#define ERUDITE_MALE ((ERUDITE << 8) | Gender::Male)
#define ERUDITE_FEMALE ((ERUDITE << 8) | Gender::Female)
#define WOOD_ELF_MALE ((WOOD_ELF << 8) | Gender::Male)
#define WOOD_ELF_FEMALE ((WOOD_ELF << 8) | Gender::Female)
#define HIGH_ELF_MALE ((HIGH_ELF << 8) | Gender::Male)
#define HIGH_ELF_FEMALE ((HIGH_ELF << 8) | Gender::Female)
#define DARK_ELF_MALE ((DARK_ELF << 8) | Gender::Male)
#define DARK_ELF_FEMALE ((DARK_ELF << 8) | Gender::Female)
#define HALF_ELF_MALE ((HALF_ELF << 8) | Gender::Male)
#define HALF_ELF_FEMALE ((HALF_ELF << 8) | Gender::Female)
#define DWARF_MALE ((DWARF << 8) | Gender::Male)
#define DWARF_FEMALE ((DWARF << 8) | Gender::Female)
#define TROLL_MALE ((TROLL << 8) | Gender::Male)
#define TROLL_FEMALE ((TROLL << 8) | Gender::Female)
#define OGRE_MALE ((OGRE << 8) | Gender::Male)
#define OGRE_FEMALE ((OGRE << 8) | Gender::Female)
#define HALFLING_MALE ((HALFLING << 8) | Gender::Male)
#define HALFLING_FEMALE ((HALFLING << 8) | Gender::Female)
#define GNOME_MALE ((GNOME << 8) | Gender::Male)
#define GNOME_FEMALE ((GNOME << 8) | Gender::Female)
#define IKSAR_MALE ((IKSAR << 8) | Gender::Male)
#define IKSAR_FEMALE ((IKSAR << 8) | Gender::Female)
#define VAHSHIR_MALE ((VAHSHIR << 8) | Gender::Male)
#define VAHSHIR_FEMALE ((VAHSHIR << 8) | Gender::Female)
#define FROGLOK_MALE ((FROGLOK << 8) | Gender::Male)
#define FROGLOK_FEMALE ((FROGLOK << 8) | Gender::Female)
#define DRAKKIN_MALE ((DRAKKIN << 8) | Gender::Male)
#define DRAKKIN_FEMALE ((DRAKKIN << 8) | Gender::Female)
#define BINDRG(r, g) (((int)r << 8) | g)
@@ -2238,11 +2238,11 @@ bool PlayerAppearance::IsValidWoad(uint16 race_id, uint8 gender_id, uint8 woad_v
const char* GetGenderName(uint32 gender_id) {
const char* gender_name = "Unknown";
if (gender_id == MALE) {
if (gender_id == Gender::Male) {
gender_name = "Male";
} else if (gender_id == FEMALE) {
} else if (gender_id == Gender::Female) {
gender_name = "Female";
} else if (gender_id == NEUTER) {
} else if (gender_id == Gender::Neuter) {
gender_name = "Neuter";
}
return gender_name;
+5 -3
View File
@@ -21,9 +21,11 @@
#include "../common/types.h"
#include <string>
#define MALE 0
#define FEMALE 1
#define NEUTER 2
namespace Gender {
constexpr uint8 Male = 0;
constexpr uint8 Female = 1;
constexpr uint8 Neuter = 2;
}
//theres a big list straight from the client below.
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_ACCOUNT_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseAccountRepository {
public:
struct Account {
@@ -196,8 +197,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
account_id
)
);
@@ -547,6 +549,108 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const Account &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back("'" + Strings::Escape(e.name) + "'");
v.push_back("'" + Strings::Escape(e.charname) + "'");
v.push_back(std::to_string(e.sharedplat));
v.push_back("'" + Strings::Escape(e.password) + "'");
v.push_back(std::to_string(e.status));
v.push_back("'" + Strings::Escape(e.ls_id) + "'");
v.push_back(std::to_string(e.lsaccount_id));
v.push_back(std::to_string(e.gmspeed));
v.push_back(std::to_string(e.invulnerable));
v.push_back(std::to_string(e.flymode));
v.push_back(std::to_string(e.ignore_tells));
v.push_back(std::to_string(e.revoked));
v.push_back(std::to_string(e.karma));
v.push_back("'" + Strings::Escape(e.minilogin_ip) + "'");
v.push_back(std::to_string(e.hideme));
v.push_back(std::to_string(e.rulesflag));
v.push_back("FROM_UNIXTIME(" + (e.suspendeduntil > 0 ? std::to_string(e.suspendeduntil) : "null") + ")");
v.push_back(std::to_string(e.time_creation));
v.push_back("'" + Strings::Escape(e.ban_reason) + "'");
v.push_back("'" + Strings::Escape(e.suspend_reason) + "'");
v.push_back("'" + Strings::Escape(e.crc_eqgame) + "'");
v.push_back("'" + Strings::Escape(e.crc_skillcaps) + "'");
v.push_back("'" + Strings::Escape(e.crc_basedata) + "'");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<Account> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back("'" + Strings::Escape(e.name) + "'");
v.push_back("'" + Strings::Escape(e.charname) + "'");
v.push_back(std::to_string(e.sharedplat));
v.push_back("'" + Strings::Escape(e.password) + "'");
v.push_back(std::to_string(e.status));
v.push_back("'" + Strings::Escape(e.ls_id) + "'");
v.push_back(std::to_string(e.lsaccount_id));
v.push_back(std::to_string(e.gmspeed));
v.push_back(std::to_string(e.invulnerable));
v.push_back(std::to_string(e.flymode));
v.push_back(std::to_string(e.ignore_tells));
v.push_back(std::to_string(e.revoked));
v.push_back(std::to_string(e.karma));
v.push_back("'" + Strings::Escape(e.minilogin_ip) + "'");
v.push_back(std::to_string(e.hideme));
v.push_back(std::to_string(e.rulesflag));
v.push_back("FROM_UNIXTIME(" + (e.suspendeduntil > 0 ? std::to_string(e.suspendeduntil) : "null") + ")");
v.push_back(std::to_string(e.time_creation));
v.push_back("'" + Strings::Escape(e.ban_reason) + "'");
v.push_back("'" + Strings::Escape(e.suspend_reason) + "'");
v.push_back("'" + Strings::Escape(e.crc_eqgame) + "'");
v.push_back("'" + Strings::Escape(e.crc_skillcaps) + "'");
v.push_back("'" + Strings::Escape(e.crc_basedata) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_ACCOUNT_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_ALT_CURRENCY_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterAltCurrencyRepository {
public:
struct CharacterAltCurrency {
@@ -112,8 +113,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_alt_currency_id
)
);
@@ -338,6 +340,66 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterAltCurrency &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.currency_id));
v.push_back(std::to_string(e.amount));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterAltCurrency> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.currency_id));
v.push_back(std::to_string(e.amount));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_ALT_CURRENCY_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_ALTERNATE_ABILITIES_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterAlternateAbilitiesRepository {
public:
struct CharacterAlternateAbilities {
@@ -116,8 +117,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_alternate_abilities_id
)
);
@@ -348,6 +350,68 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterAlternateAbilities &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.aa_id));
v.push_back(std::to_string(e.aa_value));
v.push_back(std::to_string(e.charges));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterAlternateAbilities> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.aa_id));
v.push_back(std::to_string(e.aa_value));
v.push_back(std::to_string(e.charges));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_ALTERNATE_ABILITIES_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_AURAS_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterAurasRepository {
public:
struct CharacterAuras {
@@ -112,8 +113,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_auras_id
)
);
@@ -338,6 +340,66 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterAuras &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.spell_id));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterAuras> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.spell_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_AURAS_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_BANDOLIER_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterBandolierRepository {
public:
struct CharacterBandolier {
@@ -124,8 +125,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_bandolier_id
)
);
@@ -368,6 +370,72 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterBandolier &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.bandolier_id));
v.push_back(std::to_string(e.bandolier_slot));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.icon));
v.push_back("'" + Strings::Escape(e.bandolier_name) + "'");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterBandolier> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.bandolier_id));
v.push_back(std::to_string(e.bandolier_slot));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.icon));
v.push_back("'" + Strings::Escape(e.bandolier_name) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_BANDOLIER_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_BIND_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterBindRepository {
public:
struct CharacterBind {
@@ -132,8 +133,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_bind_id
)
);
@@ -387,6 +389,76 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterBind &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.zone_id));
v.push_back(std::to_string(e.instance_id));
v.push_back(std::to_string(e.x));
v.push_back(std::to_string(e.y));
v.push_back(std::to_string(e.z));
v.push_back(std::to_string(e.heading));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterBind> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.zone_id));
v.push_back(std::to_string(e.instance_id));
v.push_back(std::to_string(e.x));
v.push_back(std::to_string(e.y));
v.push_back(std::to_string(e.z));
v.push_back(std::to_string(e.heading));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_BIND_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_BUFFS_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterBuffsRepository {
public:
struct CharacterBuffs {
@@ -168,8 +169,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_buffs_id
)
);
@@ -478,6 +480,94 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterBuffs &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.slot_id));
v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.caster_level));
v.push_back("'" + Strings::Escape(e.caster_name) + "'");
v.push_back(std::to_string(e.ticsremaining));
v.push_back(std::to_string(e.counters));
v.push_back(std::to_string(e.numhits));
v.push_back(std::to_string(e.melee_rune));
v.push_back(std::to_string(e.magic_rune));
v.push_back(std::to_string(e.persistent));
v.push_back(std::to_string(e.dot_rune));
v.push_back(std::to_string(e.caston_x));
v.push_back(std::to_string(e.caston_y));
v.push_back(std::to_string(e.caston_z));
v.push_back(std::to_string(e.ExtraDIChance));
v.push_back(std::to_string(e.instrument_mod));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterBuffs> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.slot_id));
v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.caster_level));
v.push_back("'" + Strings::Escape(e.caster_name) + "'");
v.push_back(std::to_string(e.ticsremaining));
v.push_back(std::to_string(e.counters));
v.push_back(std::to_string(e.numhits));
v.push_back(std::to_string(e.melee_rune));
v.push_back(std::to_string(e.magic_rune));
v.push_back(std::to_string(e.persistent));
v.push_back(std::to_string(e.dot_rune));
v.push_back(std::to_string(e.caston_x));
v.push_back(std::to_string(e.caston_y));
v.push_back(std::to_string(e.caston_z));
v.push_back(std::to_string(e.ExtraDIChance));
v.push_back(std::to_string(e.instrument_mod));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_BUFFS_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_CURRENCY_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterCurrencyRepository {
public:
struct CharacterCurrency {
@@ -168,8 +169,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_currency_id
)
);
@@ -478,6 +480,94 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterCurrency &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.platinum));
v.push_back(std::to_string(e.gold));
v.push_back(std::to_string(e.silver));
v.push_back(std::to_string(e.copper));
v.push_back(std::to_string(e.platinum_bank));
v.push_back(std::to_string(e.gold_bank));
v.push_back(std::to_string(e.silver_bank));
v.push_back(std::to_string(e.copper_bank));
v.push_back(std::to_string(e.platinum_cursor));
v.push_back(std::to_string(e.gold_cursor));
v.push_back(std::to_string(e.silver_cursor));
v.push_back(std::to_string(e.copper_cursor));
v.push_back(std::to_string(e.radiant_crystals));
v.push_back(std::to_string(e.career_radiant_crystals));
v.push_back(std::to_string(e.ebon_crystals));
v.push_back(std::to_string(e.career_ebon_crystals));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterCurrency> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.platinum));
v.push_back(std::to_string(e.gold));
v.push_back(std::to_string(e.silver));
v.push_back(std::to_string(e.copper));
v.push_back(std::to_string(e.platinum_bank));
v.push_back(std::to_string(e.gold_bank));
v.push_back(std::to_string(e.silver_bank));
v.push_back(std::to_string(e.copper_bank));
v.push_back(std::to_string(e.platinum_cursor));
v.push_back(std::to_string(e.gold_cursor));
v.push_back(std::to_string(e.silver_cursor));
v.push_back(std::to_string(e.copper_cursor));
v.push_back(std::to_string(e.radiant_crystals));
v.push_back(std::to_string(e.career_radiant_crystals));
v.push_back(std::to_string(e.ebon_crystals));
v.push_back(std::to_string(e.career_ebon_crystals));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_CURRENCY_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_DATA_REPOSITORY_H
@@ -1339,6 +1339,266 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterData &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.account_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
v.push_back("'" + Strings::Escape(e.last_name) + "'");
v.push_back("'" + Strings::Escape(e.title) + "'");
v.push_back("'" + Strings::Escape(e.suffix) + "'");
v.push_back(std::to_string(e.zone_id));
v.push_back(std::to_string(e.zone_instance));
v.push_back(std::to_string(e.y));
v.push_back(std::to_string(e.x));
v.push_back(std::to_string(e.z));
v.push_back(std::to_string(e.heading));
v.push_back(std::to_string(e.gender));
v.push_back(std::to_string(e.race));
v.push_back(std::to_string(e.class_));
v.push_back(std::to_string(e.level));
v.push_back(std::to_string(e.deity));
v.push_back(std::to_string(e.birthday));
v.push_back(std::to_string(e.last_login));
v.push_back(std::to_string(e.time_played));
v.push_back(std::to_string(e.level2));
v.push_back(std::to_string(e.anon));
v.push_back(std::to_string(e.gm));
v.push_back(std::to_string(e.face));
v.push_back(std::to_string(e.hair_color));
v.push_back(std::to_string(e.hair_style));
v.push_back(std::to_string(e.beard));
v.push_back(std::to_string(e.beard_color));
v.push_back(std::to_string(e.eye_color_1));
v.push_back(std::to_string(e.eye_color_2));
v.push_back(std::to_string(e.drakkin_heritage));
v.push_back(std::to_string(e.drakkin_tattoo));
v.push_back(std::to_string(e.drakkin_details));
v.push_back(std::to_string(e.ability_time_seconds));
v.push_back(std::to_string(e.ability_number));
v.push_back(std::to_string(e.ability_time_minutes));
v.push_back(std::to_string(e.ability_time_hours));
v.push_back(std::to_string(e.exp));
v.push_back(std::to_string(e.exp_enabled));
v.push_back(std::to_string(e.aa_points_spent));
v.push_back(std::to_string(e.aa_exp));
v.push_back(std::to_string(e.aa_points));
v.push_back(std::to_string(e.group_leadership_exp));
v.push_back(std::to_string(e.raid_leadership_exp));
v.push_back(std::to_string(e.group_leadership_points));
v.push_back(std::to_string(e.raid_leadership_points));
v.push_back(std::to_string(e.points));
v.push_back(std::to_string(e.cur_hp));
v.push_back(std::to_string(e.mana));
v.push_back(std::to_string(e.endurance));
v.push_back(std::to_string(e.intoxication));
v.push_back(std::to_string(e.str));
v.push_back(std::to_string(e.sta));
v.push_back(std::to_string(e.cha));
v.push_back(std::to_string(e.dex));
v.push_back(std::to_string(e.int_));
v.push_back(std::to_string(e.agi));
v.push_back(std::to_string(e.wis));
v.push_back(std::to_string(e.zone_change_count));
v.push_back(std::to_string(e.toxicity));
v.push_back(std::to_string(e.hunger_level));
v.push_back(std::to_string(e.thirst_level));
v.push_back(std::to_string(e.ability_up));
v.push_back(std::to_string(e.ldon_points_guk));
v.push_back(std::to_string(e.ldon_points_mir));
v.push_back(std::to_string(e.ldon_points_mmc));
v.push_back(std::to_string(e.ldon_points_ruj));
v.push_back(std::to_string(e.ldon_points_tak));
v.push_back(std::to_string(e.ldon_points_available));
v.push_back(std::to_string(e.tribute_time_remaining));
v.push_back(std::to_string(e.career_tribute_points));
v.push_back(std::to_string(e.tribute_points));
v.push_back(std::to_string(e.tribute_active));
v.push_back(std::to_string(e.pvp_status));
v.push_back(std::to_string(e.pvp_kills));
v.push_back(std::to_string(e.pvp_deaths));
v.push_back(std::to_string(e.pvp_current_points));
v.push_back(std::to_string(e.pvp_career_points));
v.push_back(std::to_string(e.pvp_best_kill_streak));
v.push_back(std::to_string(e.pvp_worst_death_streak));
v.push_back(std::to_string(e.pvp_current_kill_streak));
v.push_back(std::to_string(e.pvp2));
v.push_back(std::to_string(e.pvp_type));
v.push_back(std::to_string(e.show_helm));
v.push_back(std::to_string(e.group_auto_consent));
v.push_back(std::to_string(e.raid_auto_consent));
v.push_back(std::to_string(e.guild_auto_consent));
v.push_back(std::to_string(e.leadership_exp_on));
v.push_back(std::to_string(e.RestTimer));
v.push_back(std::to_string(e.air_remaining));
v.push_back(std::to_string(e.autosplit_enabled));
v.push_back(std::to_string(e.lfp));
v.push_back(std::to_string(e.lfg));
v.push_back("'" + Strings::Escape(e.mailkey) + "'");
v.push_back(std::to_string(e.xtargets));
v.push_back(std::to_string(e.firstlogon));
v.push_back(std::to_string(e.e_aa_effects));
v.push_back(std::to_string(e.e_percent_to_aa));
v.push_back(std::to_string(e.e_expended_aa_spent));
v.push_back(std::to_string(e.aa_points_spent_old));
v.push_back(std::to_string(e.aa_points_old));
v.push_back(std::to_string(e.e_last_invsnapshot));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterData> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.account_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
v.push_back("'" + Strings::Escape(e.last_name) + "'");
v.push_back("'" + Strings::Escape(e.title) + "'");
v.push_back("'" + Strings::Escape(e.suffix) + "'");
v.push_back(std::to_string(e.zone_id));
v.push_back(std::to_string(e.zone_instance));
v.push_back(std::to_string(e.y));
v.push_back(std::to_string(e.x));
v.push_back(std::to_string(e.z));
v.push_back(std::to_string(e.heading));
v.push_back(std::to_string(e.gender));
v.push_back(std::to_string(e.race));
v.push_back(std::to_string(e.class_));
v.push_back(std::to_string(e.level));
v.push_back(std::to_string(e.deity));
v.push_back(std::to_string(e.birthday));
v.push_back(std::to_string(e.last_login));
v.push_back(std::to_string(e.time_played));
v.push_back(std::to_string(e.level2));
v.push_back(std::to_string(e.anon));
v.push_back(std::to_string(e.gm));
v.push_back(std::to_string(e.face));
v.push_back(std::to_string(e.hair_color));
v.push_back(std::to_string(e.hair_style));
v.push_back(std::to_string(e.beard));
v.push_back(std::to_string(e.beard_color));
v.push_back(std::to_string(e.eye_color_1));
v.push_back(std::to_string(e.eye_color_2));
v.push_back(std::to_string(e.drakkin_heritage));
v.push_back(std::to_string(e.drakkin_tattoo));
v.push_back(std::to_string(e.drakkin_details));
v.push_back(std::to_string(e.ability_time_seconds));
v.push_back(std::to_string(e.ability_number));
v.push_back(std::to_string(e.ability_time_minutes));
v.push_back(std::to_string(e.ability_time_hours));
v.push_back(std::to_string(e.exp));
v.push_back(std::to_string(e.exp_enabled));
v.push_back(std::to_string(e.aa_points_spent));
v.push_back(std::to_string(e.aa_exp));
v.push_back(std::to_string(e.aa_points));
v.push_back(std::to_string(e.group_leadership_exp));
v.push_back(std::to_string(e.raid_leadership_exp));
v.push_back(std::to_string(e.group_leadership_points));
v.push_back(std::to_string(e.raid_leadership_points));
v.push_back(std::to_string(e.points));
v.push_back(std::to_string(e.cur_hp));
v.push_back(std::to_string(e.mana));
v.push_back(std::to_string(e.endurance));
v.push_back(std::to_string(e.intoxication));
v.push_back(std::to_string(e.str));
v.push_back(std::to_string(e.sta));
v.push_back(std::to_string(e.cha));
v.push_back(std::to_string(e.dex));
v.push_back(std::to_string(e.int_));
v.push_back(std::to_string(e.agi));
v.push_back(std::to_string(e.wis));
v.push_back(std::to_string(e.zone_change_count));
v.push_back(std::to_string(e.toxicity));
v.push_back(std::to_string(e.hunger_level));
v.push_back(std::to_string(e.thirst_level));
v.push_back(std::to_string(e.ability_up));
v.push_back(std::to_string(e.ldon_points_guk));
v.push_back(std::to_string(e.ldon_points_mir));
v.push_back(std::to_string(e.ldon_points_mmc));
v.push_back(std::to_string(e.ldon_points_ruj));
v.push_back(std::to_string(e.ldon_points_tak));
v.push_back(std::to_string(e.ldon_points_available));
v.push_back(std::to_string(e.tribute_time_remaining));
v.push_back(std::to_string(e.career_tribute_points));
v.push_back(std::to_string(e.tribute_points));
v.push_back(std::to_string(e.tribute_active));
v.push_back(std::to_string(e.pvp_status));
v.push_back(std::to_string(e.pvp_kills));
v.push_back(std::to_string(e.pvp_deaths));
v.push_back(std::to_string(e.pvp_current_points));
v.push_back(std::to_string(e.pvp_career_points));
v.push_back(std::to_string(e.pvp_best_kill_streak));
v.push_back(std::to_string(e.pvp_worst_death_streak));
v.push_back(std::to_string(e.pvp_current_kill_streak));
v.push_back(std::to_string(e.pvp2));
v.push_back(std::to_string(e.pvp_type));
v.push_back(std::to_string(e.show_helm));
v.push_back(std::to_string(e.group_auto_consent));
v.push_back(std::to_string(e.raid_auto_consent));
v.push_back(std::to_string(e.guild_auto_consent));
v.push_back(std::to_string(e.leadership_exp_on));
v.push_back(std::to_string(e.RestTimer));
v.push_back(std::to_string(e.air_remaining));
v.push_back(std::to_string(e.autosplit_enabled));
v.push_back(std::to_string(e.lfp));
v.push_back(std::to_string(e.lfg));
v.push_back("'" + Strings::Escape(e.mailkey) + "'");
v.push_back(std::to_string(e.xtargets));
v.push_back(std::to_string(e.firstlogon));
v.push_back(std::to_string(e.e_aa_effects));
v.push_back(std::to_string(e.e_percent_to_aa));
v.push_back(std::to_string(e.e_expended_aa_spent));
v.push_back(std::to_string(e.aa_points_spent_old));
v.push_back(std::to_string(e.aa_points_old));
v.push_back(std::to_string(e.e_last_invsnapshot));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_DATA_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_DISCIPLINES_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterDisciplinesRepository {
public:
struct CharacterDisciplines {
@@ -112,8 +113,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_disciplines_id
)
);
@@ -338,6 +340,66 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterDisciplines &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot_id));
v.push_back(std::to_string(e.disc_id));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterDisciplines> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot_id));
v.push_back(std::to_string(e.disc_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_DISCIPLINES_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_ITEM_RECAST_REPOSITORY_H
@@ -16,11 +16,12 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterItemRecastRepository {
public:
struct CharacterItemRecast {
uint32_t id;
uint16_t recast_type;
uint32_t recast_type;
uint32_t timestamp;
};
@@ -112,8 +113,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_item_recast_id
)
);
@@ -123,7 +125,7 @@ public:
CharacterItemRecast e{};
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.recast_type = static_cast<uint16_t>(strtoul(row[1], nullptr, 10));
e.recast_type = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.timestamp = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
return e;
@@ -251,7 +253,7 @@ public:
CharacterItemRecast e{};
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.recast_type = static_cast<uint16_t>(strtoul(row[1], nullptr, 10));
e.recast_type = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.timestamp = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
all_entries.push_back(e);
@@ -278,7 +280,7 @@ public:
CharacterItemRecast e{};
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.recast_type = static_cast<uint16_t>(strtoul(row[1], nullptr, 10));
e.recast_type = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.timestamp = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
all_entries.push_back(e);
@@ -338,6 +340,66 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterItemRecast &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.recast_type));
v.push_back(std::to_string(e.timestamp));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterItemRecast> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.recast_type));
v.push_back(std::to_string(e.timestamp));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_ITEM_RECAST_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_LEADERSHIP_ABILITIES_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterLeadershipAbilitiesRepository {
public:
struct CharacterLeadershipAbilities {
@@ -112,8 +113,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_leadership_abilities_id
)
);
@@ -338,6 +340,66 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterLeadershipAbilities &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.rank));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterLeadershipAbilities> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.rank));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_LEADERSHIP_ABILITIES_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_MATERIAL_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterMaterialRepository {
public:
struct CharacterMaterial {
@@ -128,8 +129,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_material_id
)
);
@@ -377,6 +379,74 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterMaterial &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.blue));
v.push_back(std::to_string(e.green));
v.push_back(std::to_string(e.red));
v.push_back(std::to_string(e.use_tint));
v.push_back(std::to_string(e.color));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterMaterial> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.blue));
v.push_back(std::to_string(e.green));
v.push_back(std::to_string(e.red));
v.push_back(std::to_string(e.use_tint));
v.push_back(std::to_string(e.color));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_MATERIAL_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_MEMMED_SPELLS_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterMemmedSpellsRepository {
public:
struct CharacterMemmedSpells {
@@ -112,8 +113,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_memmed_spells_id
)
);
@@ -338,6 +340,66 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterMemmedSpells &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot_id));
v.push_back(std::to_string(e.spell_id));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterMemmedSpells> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot_id));
v.push_back(std::to_string(e.spell_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_MEMMED_SPELLS_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_PET_BUFFS_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterPetBuffsRepository {
public:
struct CharacterPetBuffs {
@@ -144,8 +145,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_pet_buffs_id
)
);
@@ -418,6 +420,82 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterPetBuffs &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.pet));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.caster_level));
v.push_back("'" + Strings::Escape(e.castername) + "'");
v.push_back(std::to_string(e.ticsremaining));
v.push_back(std::to_string(e.counters));
v.push_back(std::to_string(e.numhits));
v.push_back(std::to_string(e.rune));
v.push_back(std::to_string(e.instrument_mod));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterPetBuffs> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.pet));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.caster_level));
v.push_back("'" + Strings::Escape(e.castername) + "'");
v.push_back(std::to_string(e.ticsremaining));
v.push_back(std::to_string(e.counters));
v.push_back(std::to_string(e.numhits));
v.push_back(std::to_string(e.rune));
v.push_back(std::to_string(e.instrument_mod));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_PET_BUFFS_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_PET_INFO_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterPetInfoRepository {
public:
struct CharacterPetInfo {
@@ -136,8 +137,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_pet_info_id
)
);
@@ -398,6 +400,78 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterPetInfo &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.pet));
v.push_back("'" + Strings::Escape(e.petname) + "'");
v.push_back(std::to_string(e.petpower));
v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.hp));
v.push_back(std::to_string(e.mana));
v.push_back(std::to_string(e.size));
v.push_back(std::to_string(e.taunting));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterPetInfo> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.pet));
v.push_back("'" + Strings::Escape(e.petname) + "'");
v.push_back(std::to_string(e.petpower));
v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.hp));
v.push_back(std::to_string(e.mana));
v.push_back(std::to_string(e.size));
v.push_back(std::to_string(e.taunting));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_PET_INFO_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_PET_INVENTORY_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterPetInventoryRepository {
public:
struct CharacterPetInventory {
@@ -116,8 +117,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_pet_inventory_id
)
);
@@ -348,6 +350,68 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterPetInventory &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.pet));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.item_id));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterPetInventory> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.pet));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.item_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_PET_INVENTORY_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_POTIONBELT_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterPotionbeltRepository {
public:
struct CharacterPotionbelt {
@@ -116,8 +117,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_potionbelt_id
)
);
@@ -348,6 +350,68 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterPotionbelt &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.potion_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.icon));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterPotionbelt> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.potion_id));
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.icon));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_POTIONBELT_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_SKILLS_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterSkillsRepository {
public:
struct CharacterSkills {
@@ -112,8 +113,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_skills_id
)
);
@@ -337,6 +339,66 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterSkills &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.skill_id));
v.push_back(std::to_string(e.value));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterSkills> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.skill_id));
v.push_back(std::to_string(e.value));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_SKILLS_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_SPELLS_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseCharacterSpellsRepository {
public:
struct CharacterSpells {
@@ -112,8 +113,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_spells_id
)
);
@@ -337,6 +339,66 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterSpells &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot_id));
v.push_back(std::to_string(e.spell_id));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterSpells> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.slot_id));
v.push_back(std::to_string(e.spell_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_SPELLS_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_LOGSYS_CATEGORIES_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseLogsysCategoriesRepository {
public:
struct LogsysCategories {
@@ -128,8 +129,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
logsys_categories_id
)
);
@@ -378,6 +380,74 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const LogsysCategories &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.log_category_id));
v.push_back("'" + Strings::Escape(e.log_category_description) + "'");
v.push_back(std::to_string(e.log_to_console));
v.push_back(std::to_string(e.log_to_file));
v.push_back(std::to_string(e.log_to_gmsay));
v.push_back(std::to_string(e.log_to_discord));
v.push_back(std::to_string(e.discord_webhook_id));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<LogsysCategories> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.log_category_id));
v.push_back("'" + Strings::Escape(e.log_category_description) + "'");
v.push_back(std::to_string(e.log_to_console));
v.push_back(std::to_string(e.log_to_file));
v.push_back(std::to_string(e.log_to_gmsay));
v.push_back(std::to_string(e.log_to_discord));
v.push_back(std::to_string(e.discord_webhook_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_LOGSYS_CATEGORIES_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BasePlayerEventLogSettingsRepository {
public:
struct PlayerEventLogSettings {
@@ -359,6 +360,70 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const PlayerEventLogSettings &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back("'" + Strings::Escape(e.event_name) + "'");
v.push_back(std::to_string(e.event_enabled));
v.push_back(std::to_string(e.retention_days));
v.push_back(std::to_string(e.discord_webhook_id));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<PlayerEventLogSettings> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back("'" + Strings::Escape(e.event_name) + "'");
v.push_back(std::to_string(e.event_enabled));
v.push_back(std::to_string(e.retention_days));
v.push_back(std::to_string(e.discord_webhook_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_PLAYER_EVENT_LOGS_REPOSITORY_H
@@ -240,8 +240,8 @@ public:
v.push_back(columns[7] + " = " + std::to_string(e.z));
v.push_back(columns[8] + " = " + std::to_string(e.heading));
v.push_back(columns[9] + " = " + std::to_string(e.event_type_id));
v.push_back(columns[10] + " = '" + db.Escape(e.event_type_name) + "'");
v.push_back(columns[11] + " = '" + db.Escape(e.event_data) + "'");
v.push_back(columns[10] + " = '" + Strings::Escape(e.event_type_name) + "'");
v.push_back(columns[11] + " = '" + Strings::Escape(e.event_data) + "'");
v.push_back(columns[12] + " = FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
auto results = db.QueryDatabase(
@@ -274,8 +274,8 @@ public:
v.push_back(std::to_string(e.z));
v.push_back(std::to_string(e.heading));
v.push_back(std::to_string(e.event_type_id));
v.push_back("'" + db.Escape(e.event_type_name) + "'");
v.push_back("'" + db.Escape(e.event_data) + "'");
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
v.push_back("'" + Strings::Escape(e.event_data) + "'");
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
auto results = db.QueryDatabase(
@@ -316,8 +316,8 @@ public:
v.push_back(std::to_string(e.z));
v.push_back(std::to_string(e.heading));
v.push_back(std::to_string(e.event_type_id));
v.push_back("'" + db.Escape(e.event_type_name) + "'");
v.push_back("'" + db.Escape(e.event_data) + "'");
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
v.push_back("'" + Strings::Escape(e.event_data) + "'");
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
@@ -460,6 +460,86 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const PlayerEventLogs &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.account_id));
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.zone_id));
v.push_back(std::to_string(e.instance_id));
v.push_back(std::to_string(e.x));
v.push_back(std::to_string(e.y));
v.push_back(std::to_string(e.z));
v.push_back(std::to_string(e.heading));
v.push_back(std::to_string(e.event_type_id));
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
v.push_back("'" + Strings::Escape(e.event_data) + "'");
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<PlayerEventLogs> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.account_id));
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.zone_id));
v.push_back(std::to_string(e.instance_id));
v.push_back(std::to_string(e.x));
v.push_back(std::to_string(e.y));
v.push_back(std::to_string(e.z));
v.push_back(std::to_string(e.heading));
v.push_back(std::to_string(e.event_type_id));
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
v.push_back("'" + Strings::Escape(e.event_data) + "'");
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_PLAYER_EVENT_LOGS_REPOSITORY_H
@@ -6,7 +6,7 @@
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_TASKS_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseTasksRepository {
public:
struct Tasks {
@@ -47,6 +48,7 @@ public:
uint32_t dz_template_id;
int32_t lock_activity_id;
int32_t faction_amount;
int16_t enabled;
};
static std::string PrimaryKey()
@@ -85,6 +87,7 @@ public:
"dz_template_id",
"lock_activity_id",
"faction_amount",
"enabled",
};
}
@@ -119,6 +122,7 @@ public:
"dz_template_id",
"lock_activity_id",
"faction_amount",
"enabled",
};
}
@@ -187,6 +191,7 @@ public:
e.dz_template_id = 0;
e.lock_activity_id = -1;
e.faction_amount = 0;
e.enabled = 1;
return e;
}
@@ -212,8 +217,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
tasks_id
)
);
@@ -250,6 +256,7 @@ public:
e.dz_template_id = static_cast<uint32_t>(strtoul(row[25], nullptr, 10));
e.lock_activity_id = static_cast<int32_t>(atoi(row[26]));
e.faction_amount = static_cast<int32_t>(atoi(row[27]));
e.enabled = static_cast<int16_t>(atoi(row[28]));
return e;
}
@@ -311,6 +318,7 @@ public:
v.push_back(columns[25] + " = " + std::to_string(e.dz_template_id));
v.push_back(columns[26] + " = " + std::to_string(e.lock_activity_id));
v.push_back(columns[27] + " = " + std::to_string(e.faction_amount));
v.push_back(columns[28] + " = " + std::to_string(e.enabled));
auto results = db.QueryDatabase(
fmt::format(
@@ -360,6 +368,7 @@ public:
v.push_back(std::to_string(e.dz_template_id));
v.push_back(std::to_string(e.lock_activity_id));
v.push_back(std::to_string(e.faction_amount));
v.push_back(std::to_string(e.enabled));
auto results = db.QueryDatabase(
fmt::format(
@@ -417,6 +426,7 @@ public:
v.push_back(std::to_string(e.dz_template_id));
v.push_back(std::to_string(e.lock_activity_id));
v.push_back(std::to_string(e.faction_amount));
v.push_back(std::to_string(e.enabled));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -478,6 +488,7 @@ public:
e.dz_template_id = static_cast<uint32_t>(strtoul(row[25], nullptr, 10));
e.lock_activity_id = static_cast<int32_t>(atoi(row[26]));
e.faction_amount = static_cast<int32_t>(atoi(row[27]));
e.enabled = static_cast<int16_t>(atoi(row[28]));
all_entries.push_back(e);
}
@@ -530,6 +541,7 @@ public:
e.dz_template_id = static_cast<uint32_t>(strtoul(row[25], nullptr, 10));
e.lock_activity_id = static_cast<int32_t>(atoi(row[26]));
e.faction_amount = static_cast<int32_t>(atoi(row[27]));
e.enabled = static_cast<int16_t>(atoi(row[28]));
all_entries.push_back(e);
}
@@ -588,6 +600,118 @@ public:
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const Tasks &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.type));
v.push_back(std::to_string(e.duration));
v.push_back(std::to_string(e.duration_code));
v.push_back("'" + Strings::Escape(e.title) + "'");
v.push_back("'" + Strings::Escape(e.description) + "'");
v.push_back("'" + Strings::Escape(e.reward_text) + "'");
v.push_back("'" + Strings::Escape(e.reward_id_list) + "'");
v.push_back(std::to_string(e.cash_reward));
v.push_back(std::to_string(e.exp_reward));
v.push_back(std::to_string(e.reward_method));
v.push_back(std::to_string(e.reward_points));
v.push_back(std::to_string(e.reward_point_type));
v.push_back(std::to_string(e.min_level));
v.push_back(std::to_string(e.max_level));
v.push_back(std::to_string(e.level_spread));
v.push_back(std::to_string(e.min_players));
v.push_back(std::to_string(e.max_players));
v.push_back(std::to_string(e.repeatable));
v.push_back(std::to_string(e.faction_reward));
v.push_back("'" + Strings::Escape(e.completion_emote) + "'");
v.push_back(std::to_string(e.replay_timer_group));
v.push_back(std::to_string(e.replay_timer_seconds));
v.push_back(std::to_string(e.request_timer_group));
v.push_back(std::to_string(e.request_timer_seconds));
v.push_back(std::to_string(e.dz_template_id));
v.push_back(std::to_string(e.lock_activity_id));
v.push_back(std::to_string(e.faction_amount));
v.push_back(std::to_string(e.enabled));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<Tasks> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.type));
v.push_back(std::to_string(e.duration));
v.push_back(std::to_string(e.duration_code));
v.push_back("'" + Strings::Escape(e.title) + "'");
v.push_back("'" + Strings::Escape(e.description) + "'");
v.push_back("'" + Strings::Escape(e.reward_text) + "'");
v.push_back("'" + Strings::Escape(e.reward_id_list) + "'");
v.push_back(std::to_string(e.cash_reward));
v.push_back(std::to_string(e.exp_reward));
v.push_back(std::to_string(e.reward_method));
v.push_back(std::to_string(e.reward_points));
v.push_back(std::to_string(e.reward_point_type));
v.push_back(std::to_string(e.min_level));
v.push_back(std::to_string(e.max_level));
v.push_back(std::to_string(e.level_spread));
v.push_back(std::to_string(e.min_players));
v.push_back(std::to_string(e.max_players));
v.push_back(std::to_string(e.repeatable));
v.push_back(std::to_string(e.faction_reward));
v.push_back("'" + Strings::Escape(e.completion_emote) + "'");
v.push_back(std::to_string(e.replay_timer_group));
v.push_back(std::to_string(e.replay_timer_seconds));
v.push_back(std::to_string(e.request_timer_group));
v.push_back(std::to_string(e.request_timer_seconds));
v.push_back(std::to_string(e.dz_template_id));
v.push_back(std::to_string(e.lock_activity_id));
v.push_back(std::to_string(e.faction_amount));
v.push_back(std::to_string(e.enabled));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_TASKS_REPOSITORY_H
@@ -46,6 +46,24 @@ public:
*/
// Custom extended repository methods here
static uint32 GetSecondsSinceLastLogin(Database &db, const std::string& name)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT (UNIX_TIMESTAMP(NOW()) - last_login) FROM {} WHERE name = '{}'",
TableName(),
Strings::Escape(name)
)
);
if (!results.RowCount() || !results.Success()) {
return 0;
}
auto row = results.begin();
return Strings::ToUnsignedInt(row[0]);
}
};
#endif //EQEMU_CHARACTER_DATA_REPOSITORY_H
+29
View File
@@ -216,6 +216,9 @@ RULE_BOOL(Character, EnableRaidMemberEXPModifier, true, "Enable or disable the r
RULE_BOOL(Character, LeaveCursorMoneyOnCorpse, false, "Enable or disable leaving cursor money on player corpses")
RULE_BOOL(Character, ItemExtraSkillDamageCalcAsPercent, false, "If enabled, apply Item Extra Skill Damage as Percentage-based modifiers")
RULE_BOOL(Character, UseForageCommonFood, true, "If enabled, use the common foods specified in the code.")
RULE_INT(Character, ClearXTargetDelay, 10, "Seconds between uses of the #clearxtargets command (Set to 0 to disable)")
RULE_BOOL(Character, PreventMountsFromZoning, false, "Enable to prevent mounts from zoning - Prior to December 15, 2004 this is enabled.")
RULE_BOOL(Character, GroupInvitesRequireTarget, false, "Enable to require players to have invitee on target (Disables /invite name) - Classic Style")
RULE_CATEGORY_END()
RULE_CATEGORY(Mercs)
@@ -251,6 +254,7 @@ RULE_BOOL(Skills, TrainSenseHeading, false, "Switch whether SenseHeading is trai
RULE_INT(Skills, SenseHeadingStartValue, 200, "Start value of sense heading skill")
RULE_BOOL(Skills, SelfLanguageLearning, true, "Enabling self-learning of languages")
RULE_BOOL(Skills, RequireTomeHandin, false, "Disable click-to-learn and force hand in to Guild Master")
RULE_INT(Skills, TradeSkillClamp, 0, "Legacy tradeskills would clamp at 252 regardless of item modifiers and skill combination. DEFAULT: 0 will bypass clamp. Legacy value 252")
RULE_CATEGORY_END()
RULE_CATEGORY(Pets)
@@ -430,6 +434,10 @@ RULE_BOOL(Spells, AlwaysSendTargetsBuffs, false, "Ignore Leadership Alternate Ab
RULE_BOOL(Spells, FlatItemExtraSpellAmt, false, "Allow SpellDmg stat to affect all spells, regardless of cast time/cooldown/etc")
RULE_BOOL(Spells, IgnoreSpellDmgLvlRestriction, false, "Ignore the 5 level spread on applying SpellDmg")
RULE_BOOL(Spells, ItemExtraSpellAmtCalcAsPercent, false, "Apply the Item stats Spell Dmg and Heal Amount as Percentage-based modifiers instead of flat additions")
RULE_REAL(Spells, BreakFeignDeathWhenCastOn, 2.0, "Percentage that Feign Death will break when you resist a spell")
RULE_REAL(Spells, BreakSneakWhenCastOn, 2.0, "Percentage that Sneak will break when you resist a spell")
RULE_BOOL(Spells, EnableResistSoftCap, false, "Enabled resist softcap and can be adjusted by rule, SpellResistSoftCap")
RULE_INT(Spells, SpellResistSoftCap, 255, "Softcap for spell resists.")
RULE_BOOL(Spells, AllowItemTGB, false, "Target group buff (/tgb) doesn't work with items on live, custom servers want it though")
RULE_BOOL(Spells, NPCInnateProcOverride, true, "NPC innate procs override the target type to single target")
RULE_BOOL(Spells, OldRainTargets, false, "Use old incorrectly implemented maximum targets for rains")
@@ -462,6 +470,14 @@ RULE_INT(Spells, DefensiveProcPenaltyLevelGap, 6, "Defensive Proc Penalty Level
RULE_REAL(Spells, DefensiveProcPenaltyLevelGapModifier, 10.0f, "Defensive Proc Penalty Level Gap Modifier where procs start losing their proc rate at defined % after RuleI(Spells, DefensiveProcLevelGap) level difference")
RULE_BOOL(Spells, DOTBonusDamageSplitOverDuration, true, "Disable to have Damage Over Time total bonus damage added to each tick instead of divided across duration")
RULE_BOOL(Spells, HOTBonusHealingSplitOverDuration, true, "Disable to have Heal Over Time total bonus healing added to each tick instead of divided across duration")
RULE_BOOL(Spells, UseLegacyFizzleCode, false, "Enable will turn on the legacy fizzle code which is far stricter and more accurate to 2001/2002 testing.")
RULE_BOOL(Spells, LegacyManaburn, false, "Enable to have the legacy manaburn system from 2003 and earlier.")
RULE_INT(Spells, LegacyManaburnCap, 9492, "Adjusted the hard cap (Normal or Crit) for the Legacy Manaburn system. DEFAULT: 9492")
RULE_BOOL(Spells, EvacClearAggroInSameZone, false, "Enable to clear aggro on clients when evacing in same zone.")
RULE_BOOL(Spells, CharmAggroOverLevel, false, "Enabling this rule will cause Charm casts over level to show resisted and cause aggro. Early EQ style.")
RULE_BOOL(Spells, RequireMnemonicRetention, true, "Enabling will require spell slots 9-12 to have the appropriate Mnemonic Retention AA learned.")
RULE_BOOL(Spells, EvacClearCharmPet, false, "Enable to have evac in zone clear charm from charm pets and detach buffs.")
RULE_BOOL(Spells, ManaTapsRequireNPCMana, false, "Enabling will require target to have mana to tap. Default off as many npc's are caster class with 0 mana and need fixed.")
RULE_CATEGORY_END()
RULE_CATEGORY(Combat)
@@ -499,6 +515,7 @@ RULE_REAL(Combat, AvgDefProcsPerMinute, 2.0, "Average defense procs per minute")
RULE_REAL(Combat, DefProcPerMinAgiContrib, 0.075, "How much agility contributes to defensive proc rate")
RULE_INT(Combat, NPCFlurryChance, 20, "Chance for NPC to flurry")
RULE_BOOL(Combat, TauntOverLevel, 1, "Allows you to taunt NPC's over warriors level")
RULE_INT(Combat, TauntOverAggro, 0, "+ amount over hate_top it will add before any bonus hate.")
RULE_REAL(Combat, TauntSkillFalloff, 0.33, "For every taunt skill point that's not maxed you lose this percentage chance to taunt")
RULE_BOOL(Combat, EXPFromDmgShield, false, "Determine if damage from a damage shield counts for experience gain")
RULE_INT(Combat, QuiverHasteCap, 1000, "Quiver haste cap 1000 on live for a while, currently 700 on live")
@@ -508,6 +525,9 @@ RULE_BOOL(Combat, OneProcPerWeapon, true, "If enabled, One proc per weapon per r
RULE_BOOL(Combat, ProjectileDmgOnImpact, true, "If enabled, projectiles (i.e. arrows) will hit on impact, instead of instantly")
RULE_BOOL(Combat, MeleePush, true, "Enable melee push")
RULE_INT(Combat, MeleePushChance, 50, "NPC chance the target will be pushed. Made up, 100 actually isn't that bad")
RULE_REAL(Combat, MeleePushForceClientPercent, 0.00, "Percent to add or remove from push for players")
RULE_REAL(Combat, MeleePushForcePetPercent, 0.00, "Percent to add or remove from push for pets")
RULE_BOOL(Combat, NPCtoNPCPush, false, "Disabled prevents NPC to NPC pushing per the 2013+ patch.")
RULE_BOOL(Combat, UseLiveCombatRounds, true, "Turn this false if you don't want to worry about fixing up combat rounds for NPCs")
RULE_INT(Combat, NPCAssistCap, 5, "Maximum number of NPC that will assist another NPC at once")
RULE_INT(Combat, NPCAssistCapTimer, 6000, "Time a NPC will take to clear assist aggro cap space (milliseconds)")
@@ -538,6 +558,13 @@ RULE_BOOL(Combat, SummonMeleeRange, true, "Enable or disable summoning of a play
RULE_BOOL(Combat, WaterMatchRequiredForAutoFireLoS, true, "Enable/Disable the requirement of both the attacker/victim being both in or out of water for AutoFire LoS to pass.")
RULE_INT(Combat, ExtraAllowedKickClassesBitmask, 0, "Bitmask for allowing extra classes beyond Warrior, Ranger, Beastlord, and Berserker to kick, No Extra Classes (0) by default")
RULE_INT(Combat, MaxProcs, 4, "Adjustable maximum number of procs per round, the hard cap is MAX_PROCS (11). Requires mob repop or client zone when changed")
RULE_BOOL(Combat, FinishingBlowOnlyWhenFleeing, false, "Enable to only allow Finishing Blow when fleeing (Original Style Finishing Blow)")
RULE_BOOL(Combat, ClassicTripleAttack, false, "enable to use non-skill based classic triple attack. Originally it was Warrior Only but was expanded, can use the TripleAttackChance to tune the classes out.")
RULE_INT(Combat, ClassicTripleAttackChanceWarrior, 100, "Innate Chance for Warrior to Triple Attack after a Double Attack (125 = 12.5%). DEFAULT: 100")
RULE_INT(Combat, ClassicTripleAttackChanceMonk, 100, "Innate Chance for Monk to Triple Attack after a Double Attack (200 = 20%). DEFAULT: 100")
RULE_INT(Combat, ClassicTripleAttackChanceBerserker, 100, "Innate Chance for Berserker to Triple Attack after a Double Attack (200 = 20%). DEFAULT: 100")
RULE_INT(Combat, ClassicTripleAttackChanceRanger, 100, "Innate Chance for Ranger to Triple Attack after a Double Attack (200 = 20%). DEFAULT: 100")
RULE_INT(Combat, StunChance, 12, "Percent chance that client will be stunned when mob is behind player. DEFAULT: 12")
RULE_CATEGORY_END()
RULE_CATEGORY(NPC)
@@ -563,6 +590,7 @@ RULE_INT(NPC, NPCToNPCAggroTimerMin, 500, "Minimum time span after which one NPC
RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000, "Maximum time span after which one NPC aggro another NPC (milliseconds)")
RULE_BOOL(NPC, UseClassAsLastName, true, "Uses class archetype as LastName for NPC with none")
RULE_BOOL(NPC, NewLevelScaling, true, "Better level scaling, use old if new formulas would break your server")
RULE_REAL(NPC,NPCBackstabMod, 1.9, "Multiplier for NPC Backstab, Higher = Lower backstab amount")
RULE_INT(NPC, NPCGatePercent, 20, " Percentage at which the NPC Will attempt to gate at")
RULE_BOOL(NPC, NPCGateNearBind, false, "Will NPC attempt to gate when near bind location?")
RULE_INT(NPC, NPCGateDistanceBind, 75, "Distance from bind before NPC will attempt to gate")
@@ -593,6 +621,7 @@ RULE_INT(Aggro, ClientAggroCheckIdleInterval, 6000, "Interval in which clients a
RULE_REAL(Aggro, PetAttackRange, 40000.0, "Maximum squared range /pet attack works at default is 200")
RULE_BOOL(Aggro, NPCAggroMaxDistanceEnabled, true, "If enabled, NPC's will drop aggro beyond 600 units or what is defined at the zone level")
RULE_BOOL(Aggro, AggroPlayerPets, false, "If enabled, NPCs will aggro player pets")
RULE_BOOL(Aggro, UndeadAlwaysAggro, true, "should undead always aggro?")
RULE_CATEGORY_END()
RULE_CATEGORY(TaskSystem)
+34 -18
View File
@@ -44,6 +44,7 @@
#include "repositories/starting_items_repository.h"
#include "path_manager.h"
#include "repositories/loottable_repository.h"
#include "repositories/character_item_recast_repository.h"
namespace ItemField
{
@@ -882,34 +883,49 @@ bool SharedDatabase::GetInventory(uint32 account_id, char *name, EQ::InventoryPr
std::map<uint32, uint32> SharedDatabase::GetItemRecastTimestamps(uint32 char_id)
{
std::map<uint32, uint32> timers;
const std::string query = StringFormat("SELECT recast_type,timestamp FROM character_item_recast WHERE id=%u", char_id);
auto results = QueryDatabase(query);
if (!results.Success() || results.RowCount() == 0)
return timers;
for (auto& row = results.begin(); row != results.end(); ++row)
timers[Strings::ToUnsignedInt(row[0])] = Strings::ToUnsignedInt(row[1]);
return timers; // RVO or move assigned
const auto& l = CharacterItemRecastRepository::GetWhere(
*this,
fmt::format(
"`id` = {}",
char_id
)
);
if (l.empty()) {
return timers;
}
for (const auto& e : l) {
timers[e.recast_type] = e.timestamp;
}
return timers;
}
uint32 SharedDatabase::GetItemRecastTimestamp(uint32 char_id, uint32 recast_type)
{
const std::string query = StringFormat("SELECT timestamp FROM character_item_recast WHERE id=%u AND recast_type=%u",
char_id, recast_type);
auto results = QueryDatabase(query);
if (!results.Success() || results.RowCount() == 0)
return 0;
const auto& l = CharacterItemRecastRepository::GetWhere(
*this,
fmt::format(
"`id` = {} AND `recast_type` = {}",
char_id,
recast_type
)
);
auto& row = results.begin();
return Strings::ToUnsignedInt(row[0]);
return l.empty() ? 0 : l[0].timestamp;
}
void SharedDatabase::ClearOldRecastTimestamps(uint32 char_id)
{
// This actually isn't strictly live-like. Live your recast timestamps are forever
const std::string query =
StringFormat("DELETE FROM character_item_recast WHERE id = %u and timestamp < UNIX_TIMESTAMP()", char_id);
QueryDatabase(query);
CharacterItemRecastRepository::DeleteWhere(
*this,
fmt::format(
"`id` = {} AND `timestamp` < UNIX_TIMESTAMP()",
char_id
)
);
}
void SharedDatabase::GetItemsCount(int32 &item_count, uint32 &max_id)
+13
View File
@@ -202,6 +202,19 @@
#define SPELL_GUIDE_LEVITATION 39852
#define SPELL_GUIDE_SPELL_HASTE 39853
#define SPELL_GUIDE_HASTE 39854
#define SPELL_VAMPIRIC_EMBRACE 821
#define SPELL_VAMPIRIC_EMBRACE_OF_SHADOW 822
#define SPELL_BATTLE_CRY 5027
#define SPELL_WAR_CRY 5028
#define SPELL_BATTLE_CRY_OF_DRAVEL 5029
#define SPELL_WAR_CRY_OF_DRAVEL 5030
#define SPELL_BATTLE_CRY_OF_THE_MASTRUQ 5031
#define SPELL_ANCIENT_CRY_OF_CHAOS 5032
#define SPELL_BLOODTHIRST 8476
#define SPELL_AMPLIFICATION 2603
// discipline IDs.
#define DISC_UNHOLY_AURA 4520
//spellgroup ids
#define SPELLGROUP_FRENZIED_BURNOUT 2754
+3 -1
View File
@@ -359,9 +359,11 @@ namespace Tasks {
if (activity_states[i].activity_state != ActivityCompleted)
{
completed_ids[i] = false;
current_step = std::min(current_step, el.step);
// step system advances to next step if only optionals active
if (!el.optional)
{
current_step = std::min(current_step, el.step);
result.is_task_complete = false;
}
}
+2 -2
View File
@@ -25,7 +25,7 @@
// Build variables
// these get injected during the build pipeline
#define CURRENT_VERSION "22.38.0-dev" // always append -dev to the current version for custom-builds
#define CURRENT_VERSION "22.41.0-dev" // always append -dev to the current version for custom-builds
#define LOGIN_VERSION "0.8.0"
#define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__
@@ -42,7 +42,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9248
#define CURRENT_BINARY_DATABASE_VERSION 9249
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9041
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "eqemu-server",
"version": "22.38.0",
"version": "22.41.0",
"repository": {
"type": "git",
"url": "https://github.com/EQEmu/Server.git"
+1 -1
View File
@@ -24,7 +24,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errmsg.h>
#include <mysqld_error.h>
#include <limits.h>
#include <ctype.h>
@@ -399,5 +398,6 @@ void QSDatabase::GeneralQueryReceive(ServerPacket *pack)
LogInfo("[{}]", query.c_str());
}
safe_delete(pack);
safe_delete_array(queryBuffer);
}
+12 -8
View File
@@ -463,15 +463,17 @@ void TaskStateTest::TestOptionalSteps()
// activity_id | step | state
// 0 | 0 | complete
// 1 | 1 | hidden | optional (active)
// 2 | 2 | hidden | optional
// 3 | 2 | hidden
// 2 | 2 | hidden | optional (active)
// 3 | 2 | hidden (active)
auto res = Tasks::GetActiveElements(data, state, activity_count);
// since optional is on its own step it's effectively non-optional to open next step
// steps that only contain optionals should not need to be completed to open next step
TEST_ASSERT(res.is_task_complete == false);
TEST_ASSERT(res.active.size() == 1);
TEST_ASSERT(res.active.size() == 3);
TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end());
TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end());
TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 3) != res.active.end());
}
{
@@ -530,9 +532,9 @@ void TaskStateTest::TestOptionalLastSteps()
auto state = GetMockWorldState(activity_count);
data[0].step = 0;
data[1].optional = true;
data[1].step = 2;
data[1].step = 1;
data[2].optional = true;
data[2].step = 3;
data[2].step = 2;
{
// activity_id | step | state
@@ -553,13 +555,15 @@ void TaskStateTest::TestOptionalLastSteps()
// activity_id | step | state
// 0 | 0 | complete
// 1 | 1 | hidden | optional (active)
// 2 | 2 | hidden | optional
// 2 | 2 | hidden | optional (active)
// step with only an optional should not prevent next step being active
auto res = Tasks::GetActiveElements(data, state, activity_count);
TEST_ASSERT(res.is_task_complete == true);
TEST_ASSERT(res.active.size() == 1);
TEST_ASSERT(res.active.size() == 2);
TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 1) != res.active.end());
TEST_ASSERT(std::find(res.active.begin(), res.active.end(), 2) != res.active.end());
}
{
+1 -2
View File
@@ -24,7 +24,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errmsg.h>
#include <mysqld_error.h>
#include <limits.h>
#include <ctype.h>
@@ -160,7 +159,7 @@ bool UCSDatabase::VerifyMailKey(const std::string& characterName, int IPAddress,
LogInfo("No mailkeys found for [{}].", characterName.c_str());
return false;
}
auto row = results.begin();
// The key is the client's IP address (expressed as 8 hex digits) and an 8 hex digit random string generated
+1 -7
View File
@@ -164,13 +164,7 @@ int main() {
database.ExpireMail();
if(Config->ChatPort != Config->MailPort)
{
LogInfo("MailPort and CharPort must be the same in eqemu_config.json for UCS");
exit(1);
}
g_Clientlist = new Clientlist(Config->ChatPort);
g_Clientlist = new Clientlist(Config->GetUCSPort());
ChannelList = new ChatChannelList();
@@ -513,6 +513,13 @@ foreach my $table_to_generate (@tables) {
#############################################
if ($repository_generation_option eq "all" || $repository_generation_option eq "extended") {
my $generated_repository = './common/repositories/' . $table_to_generate . '_repository.h';
# check if file exists firsts
if (-e $generated_repository) {
print "File [$generated_repository] already exists! Can't overwrite extended once created!\n";
next;
}
my $cmake_generated_reference = $generated_repository;
$cmake_generated_reference =~ s/.\/common\///g;
$generated_repository_files .= $cmake_generated_reference . "\n";
+3 -1
View File
@@ -9,5 +9,7 @@ void WorldserverCLI::DatabaseUpdates(int argc, char **argv, argh::parser &cmd, s
}
DatabaseUpdate update;
update.SetDatabase(&database)->CheckDbUpdates();
update.SetDatabase(&database)
->SetContentDatabase(&content_db)
->CheckDbUpdates();
}
+85 -95
View File
@@ -28,7 +28,6 @@
#include "../common/inventory_profile.h"
#include "../common/races.h"
#include "../common/classes.h"
#include "../common/languages.h"
#include "../common/skills.h"
#include "../common/extprofile.h"
#include "../common/strings.h"
@@ -945,8 +944,8 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
}
buffer = fmt::format("{},{},{}.{},{}{:08X}",
config->ChatHost,
config->ChatPort,
config->GetUCSHost(),
config->GetUCSPort(),
config->ShortName,
GetCharName(),
static_cast<char>(connection_type),
@@ -971,8 +970,8 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
}
buffer = fmt::format("{},{},{}.{},{}{:08X}",
config->MailHost,
config->MailPort,
config->GetUCSHost(),
config->GetUCSPort(),
config->ShortName,
GetCharName(),
static_cast<char>(connection_type),
@@ -1692,15 +1691,16 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
pp.hunger_level = 6000;
pp.thirst_level = 6000;
/* Set default skills for everybody */
pp.skills[EQ::skills::SkillSwimming] = RuleI(Skills, SwimmingStartValue);
pp.skills[EQ::skills::SkillSenseHeading] = RuleI(Skills, SenseHeadingStartValue);
/* Set Racial and Class specific language and skills */
SetRacialLanguages(&pp);
SetRaceStartingSkills(&pp);
SetClassStartingSkills(&pp);
SetClassLanguages(&pp);
pp.skills[EQ::skills::SkillSwimming] = RuleI(Skills, SwimmingStartValue);
pp.skills[EQ::skills::SkillSenseHeading] = RuleI(Skills, SenseHeadingStartValue);
// strcpy(pp.servername, WorldConfig::get()->ShortName.c_str());
memset(pp.spell_book, 0xFF, (sizeof(uint32) * EQ::spells::SPELLBOOK_SIZE));
@@ -2144,7 +2144,9 @@ void Client::SetRaceStartingSkills( PlayerProfile_Struct *pp )
}
case FROGLOK:
{
pp->skills[EQ::skills::SkillSwimming] = 125;
if (RuleI(Skills, SwimmingStartValue) < 125) {
pp->skills[EQ::skills::SkillSwimming] = 125;
}
break;
}
case GNOME:
@@ -2161,7 +2163,9 @@ void Client::SetRaceStartingSkills( PlayerProfile_Struct *pp )
case IKSAR:
{
pp->skills[EQ::skills::SkillForage] = 50;
pp->skills[EQ::skills::SkillSwimming] = 100;
if (RuleI(Skills, SwimmingStartValue) < 100) {
pp->skills[EQ::skills::SkillSwimming] = 100;
}
break;
}
case WOOD_ELF:
@@ -2181,115 +2185,101 @@ void Client::SetRaceStartingSkills( PlayerProfile_Struct *pp )
void Client::SetRacialLanguages( PlayerProfile_Struct *pp )
{
switch( pp->race )
{
case BARBARIAN:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
pp->languages[LANG_BARBARIAN] = 100;
switch (pp->race) {
case Race::Human: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
break;
}
case DARK_ELF:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
pp->languages[LANG_DARK_ELVISH] = 100;
pp->languages[LANG_DARK_SPEECH] = 100;
pp->languages[LANG_ELDER_ELVISH] = 100;
pp->languages[LANG_ELVISH] = 25;
case Race::Barbarian: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
pp->languages[Language::Barbarian] = Language::MaxValue;
break;
}
case DWARF:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
pp->languages[LANG_DWARVISH] = 100;
pp->languages[LANG_GNOMISH] = 25;
case Race::Erudite: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
pp->languages[Language::Erudian] = Language::MaxValue;
break;
}
case ERUDITE:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
pp->languages[LANG_ERUDIAN] = 100;
case Race::WoodElf: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
pp->languages[Language::Elvish] = Language::MaxValue;
break;
}
case FROGLOK:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
pp->languages[LANG_FROGLOK] = 100;
pp->languages[LANG_TROLL] = 25;
case Race::HighElf: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
pp->languages[Language::DarkElvish] = 25;
pp->languages[Language::ElderElvish] = 25;
pp->languages[Language::Elvish] = Language::MaxValue;
break;
}
case GNOME:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
pp->languages[LANG_DWARVISH] = 25;
pp->languages[LANG_GNOMISH] = 100;
case Race::DarkElf: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
pp->languages[Language::DarkElvish] = Language::MaxValue;
pp->languages[Language::DarkSpeech] = Language::MaxValue;
pp->languages[Language::ElderElvish] = Language::MaxValue;
pp->languages[Language::Elvish] = 25;
break;
}
case HALF_ELF:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
pp->languages[LANG_ELVISH] = 100;
case Race::HalfElf: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
pp->languages[Language::Elvish] = Language::MaxValue;
break;
}
case HALFLING:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
pp->languages[LANG_HALFLING] = 100;
case Race::Dwarf: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
pp->languages[Language::Dwarvish] = Language::MaxValue;
pp->languages[Language::Gnomish] = 25;
break;
}
case HIGH_ELF:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
pp->languages[LANG_DARK_ELVISH] = 25;
pp->languages[LANG_ELDER_ELVISH] = 25;
pp->languages[LANG_ELVISH] = 100;
case Race::Troll: {
pp->languages[Language::CommonTongue] = RuleI(Character, TrollCommonTongue);
pp->languages[Language::DarkSpeech] = Language::MaxValue;
pp->languages[Language::Troll] = Language::MaxValue;
break;
}
case HUMAN:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
case Race::Ogre: {
pp->languages[Language::CommonTongue] = RuleI(Character, OgreCommonTongue);
pp->languages[Language::DarkSpeech] = Language::MaxValue;
pp->languages[Language::Ogre] = Language::MaxValue;
break;
}
case IKSAR:
{
pp->languages[LANG_COMMON_TONGUE] = RuleI(Character, IksarCommonTongue);
pp->languages[LANG_DARK_SPEECH] = 100;
pp->languages[LANG_LIZARDMAN] = 100;
case Race::Halfling: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
pp->languages[Language::Halfling] = Language::MaxValue;
break;
}
case OGRE:
{
pp->languages[LANG_COMMON_TONGUE] = RuleI(Character, OgreCommonTongue);
pp->languages[LANG_DARK_SPEECH] = 100;
pp->languages[LANG_OGRE] = 100;
case Race::Gnome: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
pp->languages[Language::Dwarvish] = 25;
pp->languages[Language::Gnomish] = Language::MaxValue;
break;
}
case TROLL:
{
pp->languages[LANG_COMMON_TONGUE] = RuleI(Character, TrollCommonTongue);
pp->languages[LANG_DARK_SPEECH] = 100;
pp->languages[LANG_TROLL] = 100;
case Race::Iksar: {
pp->languages[Language::CommonTongue] = RuleI(Character, IksarCommonTongue);
pp->languages[Language::DarkSpeech] = Language::MaxValue;
pp->languages[Language::Lizardman] = Language::MaxValue;
break;
}
case WOOD_ELF:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
pp->languages[LANG_ELVISH] = 100;
case Race::VahShir: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
pp->languages[Language::CombineTongue] = Language::MaxValue;
pp->languages[Language::Erudian] = 25;
pp->languages[Language::VahShir] = Language::MaxValue;
break;
}
case VAHSHIR:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
pp->languages[LANG_COMBINE_TONGUE] = 100;
pp->languages[LANG_ERUDIAN] = 25;
pp->languages[LANG_VAH_SHIR] = 100;
case Race::Froglok: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
pp->languages[Language::Froglok] = Language::MaxValue;
pp->languages[Language::Troll] = 25;
break;
}
case DRAKKIN:
{
pp->languages[LANG_COMMON_TONGUE] = 100;
pp->languages[LANG_ELDER_DRAGON] = 100;
pp->languages[LANG_DRAGON] = 100;
case Race::Drakkin: {
pp->languages[Language::CommonTongue] = Language::MaxValue;
pp->languages[Language::ElderDragon] = Language::MaxValue;
pp->languages[Language::Dragon] = Language::MaxValue;
break;
}
default: {
break;
}
}
@@ -2298,12 +2288,12 @@ void Client::SetRacialLanguages( PlayerProfile_Struct *pp )
void Client::SetClassLanguages(PlayerProfile_Struct *pp)
{
// we only need to handle one class, but custom server might want to do more
switch(pp->class_) {
case Class::Rogue:
pp->languages[LANG_THIEVES_CANT] = 100;
break;
default:
break;
switch (pp->class_) {
case Class::Rogue:
pp->languages[Language::ThievesCant] = Language::MaxValue;
break;
default:
break;
}
}
+1 -1
View File
@@ -460,7 +460,7 @@ void SharedTaskManager::LoadSharedTaskState()
SharedTaskManager *SharedTaskManager::LoadTaskData()
{
m_task_data = TasksRepository::All(*m_content_database);
m_task_data = TasksRepository::GetWhere(*m_content_database, "enabled = 1");
m_task_activity_data = TaskActivitiesRepository::All(*m_content_database);
LogTasks("Loaded tasks [{}] activities [{}]", m_task_data.size(), m_task_activity_data.size());
+16 -10
View File
@@ -26,6 +26,7 @@
#include "../common/ip_util.h"
#include "../common/zone_store.h"
#include "../common/path_manager.h"
#include "../common/database/database_update.h"
extern ZSList zoneserver_list;
extern WorldConfig Config;
@@ -293,7 +294,18 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
const auto c = EQEmuConfig::get();
if (c->auto_database_updates) {
LogInfo("Checking Database Conversions");
database.CheckDatabaseConversions();
auto *r = RuleManager::Instance();
r->LoadRules(&database, "default", false);
if (!RuleB(Bots, Enabled) && database.DoesTableExist("bot_data")) {
LogInfo("Bot tables found but rule not enabled, enabling");
r->SetRule("Bots:Enabled", "true", &database, true, true);
}
DatabaseUpdate update{};
update.SetDatabase(&database)
->SetContentDatabase(&content_db)
->CheckDbUpdates();
}
if (RuleB(Logging, WorldGMSayLogging)) {
@@ -622,8 +634,7 @@ void WorldBoot::CheckForPossibleConfigurationIssues()
// ucs (public)
if (
(!config_address.empty() && c->MailHost != config_address) ||
(!config_address.empty() && c->ChatHost != config_address)
(!config_address.empty() && c->GetUCSHost() != config_address)
) {
LogWarning("# UCS Address Mailhost (Configuration)");
LogWarning("");
@@ -635,14 +646,9 @@ void WorldBoot::CheckForPossibleConfigurationIssues()
LogWarning("Docs [https://docs.eqemu.io/server/installation/configure-your-eqemu_config/#mailserver]");
LogWarning("");
LogWarning(
"[server.world.address] value [{}] [server.chatserver.host] [{}]",
"[server.world.address] value [{}] [server.ucs.host] [{}]",
config_address,
c->ChatHost
);
LogWarning(
"[server.world.address] value [{}] [server.mailserver.host] [{}]",
config_address,
c->MailHost
c->GetUCSHost()
);
std::cout << std::endl;
}
+2 -2
View File
@@ -111,7 +111,7 @@ void ZSList::Process() {
0,
0,
AccountStatus::Player,
Chat::Yellow,
Chat::System,
fmt::format(
"[SYSTEM] World will be shutting down in {} minutes.",
((shutdowntimer->GetRemainingTime() / 1000) / 60)
@@ -759,7 +759,7 @@ void ZSList::WorldShutDown(uint32 time, uint32 interval)
0,
0,
AccountStatus::Player,
Chat::Yellow,
Chat::System,
fmt::format(
"[SYSTEM] World will be shutting down in {} minutes.",
(time / 60)
-1
View File
@@ -191,7 +191,6 @@ SET(zone_headers
embperl.h
encounter.h
entity.h
errmsg.h
event_codes.h
expedition.h
expedition_database.h
+111 -99
View File
@@ -39,6 +39,8 @@ Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
#include "bot.h"
#include "../common/repositories/character_alternate_abilities_repository.h"
extern WorldServer worldserver;
extern QueryServ* QServ;
@@ -510,38 +512,41 @@ void Mob::WakeTheDead(uint16 spell_id, Corpse *corpse_to_use, Mob *tar, uint32 d
delete made_npc;
}
void Client::ResetAA() {
void Client::ResetAA()
{
SendClearAA();
RefundAA();
memset(&m_pp.aa_array[0], 0, sizeof(AA_Array) * MAX_PP_AA_ARRAY);
int i = 0;
for(auto &rank_value : aa_ranks) {
auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(rank_value.first, rank_value.second.first);
auto ability = ability_rank.first;
auto rank = ability_rank.second;
int slot_id = 0;
if(!rank) {
for (auto& rank_value: aa_ranks) {
auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(rank_value.first, rank_value.second.first);
auto ability = ability_rank.first;
auto rank = ability_rank.second;
if (!rank) {
continue;
}
m_pp.aa_array[i].AA = rank_value.first;
m_pp.aa_array[i].value = rank_value.second.first;
m_pp.aa_array[i].charges = rank_value.second.second;
++i;
m_pp.aa_array[slot_id].AA = rank_value.first;
m_pp.aa_array[slot_id].value = rank_value.second.first;
m_pp.aa_array[slot_id].charges = rank_value.second.second;
++slot_id;
}
for(int i = 0; i < _maxLeaderAA; ++i)
m_pp.leader_abilities.ranks[i] = 0;
for (int slot_id = 0; slot_id < _maxLeaderAA; ++slot_id) {
m_pp.leader_abilities.ranks[slot_id] = 0;
}
m_pp.group_leadership_points = 0;
m_pp.raid_leadership_points = 0;
m_pp.group_leadership_exp = 0;
m_pp.raid_leadership_exp = 0;
m_pp.raid_leadership_points = 0;
m_pp.group_leadership_exp = 0;
m_pp.raid_leadership_exp = 0;
database.DeleteCharacterAAs(CharacterID());
database.DeleteCharacterLeadershipAAs(CharacterID());
database.DeleteCharacterLeadershipAbilities(CharacterID());
}
void Client::SendClearAA()
@@ -833,31 +838,31 @@ void Client::RefundAA() {
int refunded = 0;
auto rank_value = aa_ranks.begin();
while(rank_value != aa_ranks.end()) {
while (rank_value != aa_ranks.end()) {
auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(rank_value->first, rank_value->second.first);
auto ability = ability_rank.first;
auto rank = ability_rank.second;
auto ability = ability_rank.first;
auto rank = ability_rank.second;
if(!ability) {
if (!ability) {
++rank_value;
continue;
}
if(ability->charges > 0 && rank_value->second.second < 1) {
if (ability->charges > 0 && rank_value->second.second < 1) {
++rank_value;
continue;
}
if(ability->grant_only) {
if (ability->grant_only) {
++rank_value;
continue;
}
refunded += rank->total_cost;
rank_value = aa_ranks.erase(rank_value);
rank_value = aa_ranks.erase(rank_value);
}
if(refunded > 0) {
if (refunded > 0) {
m_pp.aapoints += refunded;
SaveAA();
Save();
@@ -1438,40 +1443,42 @@ void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) {
bool ZoneDatabase::LoadAlternateAdvancement(Client *c) {
c->ClearAAs();
std::string query = StringFormat(
"SELECT "
"aa_id, "
"aa_value, "
"charges "
"FROM "
"`character_alternate_abilities` "
"WHERE `id` = %u", c->CharacterID());
MySQLRequestResult results = database.QueryDatabase(query);
int i = 0;
for(auto row = results.begin(); row != results.end(); ++row) {
uint32 aa = Strings::ToUnsignedInt(row[0]);
uint32 value = Strings::ToUnsignedInt(row[1]);
uint32 charges = Strings::ToUnsignedInt(row[2]);
const auto& l = CharacterAlternateAbilitiesRepository::GetWhere(
database,
fmt::format(
"`id` = {}",
c->CharacterID()
)
);
auto rank = zone->GetAlternateAdvancementRank(aa);
if(!rank) {
uint32 slot_id = 0;
for (const auto& e : l) {
const uint16 aa_id = e.aa_id;
const uint16 aa_value = e.aa_value;
const uint16 charges = e.charges;
auto rank = zone->GetAlternateAdvancementRank(aa_id);
if (!rank) {
continue;
}
auto ability = rank->base_ability;
if(!ability) {
if (!ability) {
continue;
}
rank = ability->GetRankByPointsSpent(value);
rank = ability->GetRankByPointsSpent(aa_value);
if(c->CanUseAlternateAdvancementRank(rank)) {
c->GetPP().aa_array[i].AA = aa;
c->GetPP().aa_array[i].value = value;
c->GetPP().aa_array[i].charges = charges;
c->SetAA(aa, value, charges);
i++;
if (c->CanUseAlternateAdvancementRank(rank)) {
c->GetPP().aa_array[slot_id].AA = aa_id;
c->GetPP().aa_array[slot_id].value = aa_value;
c->GetPP().aa_array[slot_id].charges = charges;
c->SetAA(aa_id, aa_value, charges);
slot_id++;
}
}
@@ -1556,56 +1563,61 @@ uint32 Mob::GetAAByAAID(uint32 aa_id, uint32 *charges) const {
return 0;
}
bool Mob::SetAA(uint32 rank_id, uint32 new_value, uint32 charges) {
if(zone) {
AA::Ability *ability = zone->GetAlternateAdvancementAbilityByRank(rank_id);
bool Mob::SetAA(uint32 rank_id, uint32 new_value, uint32 charges)
{
if (zone) {
auto a = zone->GetAlternateAdvancementAbilityByRank(rank_id);
if(!ability) {
if (!a) {
return false;
}
if(new_value > ability->GetMaxLevel(this)) {
if(new_value > a->GetMaxLevel(this)) {
return false;
}
aa_ranks[ability->id] = std::make_pair(new_value, charges);
aa_ranks[a->id] = std::make_pair(new_value, charges);
}
return true;
}
bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) {
bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank)
{
if (!rank) {
return false;
}
AA::Ability *ability = rank->base_ability;
const auto a = rank->base_ability;
if(!ability)
return false;
if(!(ability->classes & (1 << GetClass()))) {
if (!a) {
return false;
}
// Passive and Active Shroud AAs
// For now we skip them
if(ability->category == 3 || ability->category == 4) {
if (!(a->classes & (1 << GetClass()))) {
return false;
}
// Passive and Active Shroud AAs, skip for now
if (
a->category == AACategory::ShroudPassive ||
a->category == AACategory::ShroudActive
) {
return false;
}
//the one titanium hack i will allow
//just to make sure we dont crash the client with newer aas
//we'll exclude any expendable ones
if(IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskTitaniumAndEarlier) {
if(ability->charges > 0) {
if (IsClient() && CastToClient()->ClientVersionBit() & EQ::versions::maskTitaniumAndEarlier) {
if (a->charges > 0) {
return false;
}
}
int expansion = RuleI(Expansion, CurrentExpansion);
bool use_expansion_aa = RuleB(Expansion, UseCurrentExpansionAAOnly);
const int expansion = RuleI(Expansion, CurrentExpansion);
const bool use_expansion_aa = RuleB(Expansion, UseCurrentExpansionAAOnly);
if (use_expansion_aa && expansion >= 0) {
if (rank->expansion > expansion) {
return false;
@@ -1617,36 +1629,35 @@ bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) {
if (rank->expansion && !(CastToClient()->GetPP().expansions & (1 << (rank->expansion - 1)))) {
return false;
}
}
else if (IsBot()) {
} else if (IsBot()) {
if (rank->expansion && !(CastToBot()->GetExpansionBitmask() & (1 << (rank->expansion - 1)))) {
return false;
}
}
else {
} else {
if (rank->expansion && !(RuleI(World, ExpansionSettings) & (1 << (rank->expansion - 1)))) {
return false;
}
}
auto race = GetPlayerRaceValue(GetBaseRace());
race = race > 16 ? 1 : race;
if(!(ability->races & (1 << (race - 1)))) {
race = race > PLAYER_RACE_COUNT ? Race::Human : race;
if (!(a->races & (1 << (race - 1)))) {
return false;
}
auto deity = GetDeityBit();
if(!(ability->deities & deity)) {
const auto deity = GetDeityBit();
if (!(a->deities & deity)) {
return false;
}
if(IsClient() && CastToClient()->Admin() < ability->status) {
if (IsClient() && CastToClient()->Admin() < a->status) {
return false;
}
if(GetBaseRace() == 522) {
//drakkin_heritage
if(!(ability->drakkin_heritage & (1 << GetDrakkinHeritage()))) {
if (GetBaseRace() == Race::Drakkin) {
if (!(a->drakkin_heritage & (1 << GetDrakkinHeritage()))) {
return false;
}
}
@@ -1654,13 +1665,15 @@ bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) {
return true;
}
bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price, bool check_grant) {
AA::Ability *ability = rank->base_ability;
bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price, bool check_grant)
{
auto a = rank->base_ability;
if(!ability)
if (!a) {
return false;
}
if(!CanUseAlternateAdvancementRank(rank)) {
if (!CanUseAlternateAdvancementRank(rank)) {
return false;
}
@@ -1668,54 +1681,53 @@ bool Mob::CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price,
return false;
}
//You can't purchase grant only AAs they can only be assigned
if(check_grant && ability->grant_only) {
// You cannot purchase grant only AAs they can only be assigned
if (check_grant && a->grant_only) {
return false;
}
//check level req
if(rank->level_req > GetLevel()) {
if (rank->level_req > GetLevel()) {
return false;
}
uint32 current_charges = 0;
auto points = GetAA(rank->id, &current_charges);
uint32 current_charges = 0;
const uint32 points = GetAA(rank->id, &current_charges);
//check that we are on previous rank already (if exists)
//grant ignores the req to own the previous rank.
if(check_grant && rank->prev) {
if(points != rank->prev->current_value) {
if (check_grant && rank->prev) {
if (points != rank->prev->current_value) {
return false;
}
}
//check that we aren't already on this rank or one ahead of us
if(points >= rank->current_value) {
if (points >= rank->current_value) {
return false;
}
//if expendable only let us purchase if we have no charges already
//not quite sure on how this functions client side atm
//I intend to look into it later to make sure the behavior is right
if(ability->charges > 0 && current_charges > 0) {
if (a->charges > 0 && current_charges > 0) {
return false;
}
//check prereqs
for(auto &prereq : rank->prereqs) {
for (auto &prereq: rank->prereqs) {
AA::Ability *prereq_ability = zone->GetAlternateAdvancementAbility(prereq.first);
if(prereq_ability) {
if (prereq_ability) {
auto ranks = GetAA(prereq_ability->first_rank_id);
if(ranks < prereq.second) {
if (ranks < prereq.second) {
return false;
}
}
}
//check price, if client
if(check_price && IsClient()) {
if(rank->cost > CastToClient()->GetAAPoints()) {
if (check_price && IsClient()) {
if (rank->cost > CastToClient()->GetAAPoints()) {
return false;
}
}
+13
View File
@@ -1525,6 +1525,19 @@ enum { //values of AA_Action.action
aaActionBuy = 3
};
namespace AACategory {
constexpr int None = -1;
constexpr int Passive = 1;
constexpr int Progression = 2;
constexpr int ShroudPassive = 3;
constexpr int ShroudActive = 4;
constexpr int VeteranReward = 5;
constexpr int Tradeskill = 6;
constexpr int Expendable = 7;
constexpr int RacialInnate = 8;
constexpr int EverQuest = 9;
}
class Timer;
class Mob;
class SwarmPet {
+2 -1
View File
@@ -524,7 +524,8 @@ bool Mob::CheckWillAggro(Mob *mob) {
} else {
if (
(
GetINT() <= RuleI(Aggro, IntAggroThreshold) ||
(RuleB(Aggro, UndeadAlwaysAggro) && GetBodyType() == BT_Undead) ||
(GetINT() <= RuleI(Aggro, IntAggroThreshold)) ||
AlwaysAggro() ||
(
mob->IsClient() &&
+194 -119
View File
@@ -1419,7 +1419,7 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
if (other->CheckHitChance(this, hit)) {
if (IsNPC() && other->IsClient() && other->animation > 0 && GetLevel() >= 5 && BehindMob(other, GetX(), GetY())) {
// ~ 12% chance
if (zone->random.Roll(12)) {
if (zone->random.Roll(RuleI(Combat, StunChance))) {
int stun_resist2 = other->spellbonuses.FrontalStunResist + other->itembonuses.FrontalStunResist + other->aabonuses.FrontalStunResist;
int stun_resist = other->spellbonuses.StunResist + other->itembonuses.StunResist + other->aabonuses.StunResist;
if (zone->random.Roll(stun_resist2)) {
@@ -2119,6 +2119,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
}
RemoveFromHateList(other);
RemoveFromRampageList(other);
LogCombat("I am not allowed to attack [{}]", other->GetName());
return false;
}
@@ -2280,49 +2281,51 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
if (other->IsClient() && IsPet() && GetOwner()->IsClient()) {
//pets do half damage to clients in pvp
my_hit.damage_done /= 2;
if (my_hit.damage_done < 1)
if (my_hit.damage_done < 1) {
my_hit.damage_done = 1;
}
}
}
else {
} else {
my_hit.damage_done = DMG_INVULNERABLE;
}
if (GetHP() > 0 && !other->HasDied()) {
other->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, my_hit.skill, true, -1, false, m_specialattacks); // Not avoidable client already had thier chance to Avoid
} else {
return false;
}
else
return false;
if (HasDied()) //killed by damage shield ect
if (HasDied()) { //killed by damage shield ect
return false;
}
MeleeLifeTap(my_hit.damage_done);
CommonBreakInvisibleFromCombat();
//I doubt this works...
if (!GetTarget())
if (!GetTarget()) {
return true; //We killed them
if (!bRiposte && !other->HasDied()) {
TryWeaponProc(nullptr, weapon, other, Hand); //no weapon
if (!other->HasDied())
TrySpellProc(nullptr, weapon, other, Hand);
if (my_hit.damage_done > 0 && HasSkillProcSuccess() && !other->HasDied())
TrySkillProc(other, my_hit.skill, 0, true, Hand);
}
if (GetHP() > 0 && !other->HasDied())
bool has_hit = my_hit.damage_done > 0;
if (has_hit && !bRiposte && !other->HasDied()) {
TryWeaponProc(nullptr, weapon, other, Hand);
if (!other->HasDied()) {
TrySpellProc(nullptr, weapon, other, Hand);
}
if (HasSkillProcSuccess() && !other->HasDied()) {
TrySkillProc(other, my_hit.skill, 0, true, Hand);
}
}
if (GetHP() > 0 && !other->HasDied()) {
TriggerDefensiveProcs(other, Hand, true, my_hit.damage_done);
}
if (my_hit.damage_done > 0)
return true;
else
return false;
return has_hit;
}
void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic, eSpecialAttacks special) {
@@ -2705,68 +2708,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
Corpse* corpse = nullptr;
// Parse quests even if we're killed by an NPC
if (oos) {
if (IsNPC()) {
auto emote_id = GetEmoteID();
if (emote_id) {
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid, killer_mob);
}
}
if (oos->IsNPC()) {
if (parse->HasQuestSub(oos->GetNPCTypeID(), EVENT_NPC_SLAY)) {
parse->EventNPC(EVENT_NPC_SLAY, oos->CastToNPC(), this, "", 0);
}
auto emote_id = oos->GetEmoteID();
if (emote_id) {
oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id, this);
}
if (killer_mob) {
killer_mob->TrySpellOnKill(killed_level, spell);
}
}
}
if (killer_mob && killer_mob->IsBot()) {
if (parse->BotHasQuestSub(EVENT_NPC_SLAY)) {
parse->EventBot(EVENT_NPC_SLAY, killer_mob->CastToBot(), this, "", 0);
}
killer_mob->TrySpellOnKill(killed_level, spell);
}
m_combat_record.Stop();
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH_COMPLETE)) {
const auto& export_string = fmt::format(
"{} {} {} {}",
killer_mob ? killer_mob->GetID() : 0,
damage,
spell,
static_cast<int>(attack_skill)
);
std::vector<std::any> args = { corpse };
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, export_string, 0, &args);
}
/* Zone controller process EVENT_DEATH_ZONE (Death events) */
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DEATH_ZONE)) {
const auto& export_string = fmt::format(
"{} {} {} {}",
killer_mob ? killer_mob->GetID() : 0,
damage,
spell,
static_cast<int>(attack_skill)
);
std::vector<std::any> args = { corpse, this };
DispatchZoneControllerEvent(EVENT_DEATH_ZONE, oos, export_string, 0, &args);
}
const uint16 entity_id = GetID();
if (!HasOwner() && !IsMerc() && !GetSwarmInfo() && (!is_merchant || allow_merchant_corpse) &&
((killer && (killer->IsClient() || (killer->HasOwner() && killer->GetUltimateOwner()->IsClient()) ||
@@ -2877,6 +2819,39 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
entity_list.RemoveFromXTargets(this);
}
// Parse quests even if we're killed by an NPC
if (oos) {
if (IsNPC()) {
auto emote_id = GetEmoteID();
if (emote_id) {
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid, killer_mob);
}
}
if (oos->IsNPC()) {
if (parse->HasQuestSub(oos->GetNPCTypeID(), EVENT_NPC_SLAY)) {
parse->EventNPC(EVENT_NPC_SLAY, oos->CastToNPC(), this, "", 0);
}
auto emote_id = oos->GetEmoteID();
if (emote_id) {
oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id, this);
}
if (killer_mob) {
killer_mob->TrySpellOnKill(killed_level, spell);
}
}
}
if (killer_mob && killer_mob->IsBot()) {
if (parse->BotHasQuestSub(EVENT_NPC_SLAY)) {
parse->EventBot(EVENT_NPC_SLAY, killer_mob->CastToBot(), this, "", 0);
}
killer_mob->TrySpellOnKill(killed_level, spell);
}
WipeHateList();
p_depop = true;
@@ -2885,6 +2860,39 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
entity_list.UpdateFindableNPCState(this, true);
m_combat_record.Stop();
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH_COMPLETE)) {
const auto& export_string = fmt::format(
"{} {} {} {} {}",
killer_mob ? killer_mob->GetID() : 0,
damage,
spell,
static_cast<int>(attack_skill),
entity_id
);
std::vector<std::any> args = { corpse };
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, export_string, 0, &args);
}
// Zone controller process EVENT_DEATH_ZONE (Death events)
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DEATH_ZONE)) {
const auto& export_string = fmt::format(
"{} {} {} {} {}",
killer_mob ? killer_mob->GetID() : 0,
damage,
spell,
static_cast<int>(attack_skill),
entity_id
);
std::vector<std::any> args = { corpse, this };
DispatchZoneControllerEvent(EVENT_DEATH_ZONE, oos, export_string, 0, &args);
}
return true;
}
@@ -3718,9 +3726,43 @@ bool Client::CheckDoubleAttack()
// with varying triple attack skill (1-3% error at least)
bool Client::CheckTripleAttack()
{
int chance = GetSkill(EQ::skills::SkillTripleAttack);
if (chance < 1)
int chance;
if (RuleB(Combat, ClassicTripleAttack)) {
if (
IsClient() &&
GetLevel() >= 60 &&
(
GetClass() == Class::Warrior ||
GetClass() == Class::Ranger ||
GetClass() == Class::Monk ||
GetClass() == Class::Berserker
)
) {
switch (GetClass()) {
case Class::Warrior:
chance = RuleI(Combat, ClassicTripleAttackChanceWarrior);
break;
case Class::Ranger:
chance = RuleI(Combat, ClassicTripleAttackChanceRanger);
break;
case Class::Monk:
chance = RuleI(Combat, ClassicTripleAttackChanceMonk);
break;
case Class::Berserker:
chance = RuleI(Combat, ClassicTripleAttackChanceBerserker);
break;
default:
break;
}
}
} else {
chance = GetSkill(EQ::skills::SkillTripleAttack);
}
if (chance < 1) {
return false;
}
int inc = aabonuses.TripleAttackChance + spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance;
chance = static_cast<int>(chance * (1 + inc / 100.0f));
@@ -3805,6 +3847,8 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
if (damage > 0) {
//if there is some damage being done and theres an attacker involved
int previous_hp_ratio = GetHPRatio();
if (attacker) {
// if spell is lifetap add hp to the caster
if (IsValidSpell(spell_id) && IsLifetapSpell(spell_id)) {
@@ -3909,13 +3953,13 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
TryTriggerThreshHold(damage, SE_TriggerSpellThreshold, attacker);
}
if (IsClient() && CastToClient()->sneaking) {
CastToClient()->sneaking = false;
SendAppearancePacket(AT_Sneak, 0);
if (IsClient()) {
CommonBreakInvisible();
}
if (attacker && attacker->IsClient() && attacker->CastToClient()->sneaking) {
attacker->CastToClient()->sneaking = false;
attacker->SendAppearancePacket(AT_Sneak, 0);
attacker->SendAppearancePacket(AppearanceType::Sneak, 0);
}
//final damage has been determined.
@@ -4030,8 +4074,9 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
}
}
else {
if (GetHPRatio() < 16)
if (GetHPRatio() < 16 && previous_hp_ratio >= 16) {
TryDeathSave();
}
}
TryTriggerOnCastRequirement();
@@ -4179,8 +4224,9 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
}
//send an HP update if we are hurt
if (GetHP() < GetMaxHP())
if (GetHP() < GetMaxHP()) {
SendHPUpdate(); // the OP_Damage actually updates the client in these cases, so we skip the HP update for them
}
} //end `if damage was done`
//send damage packet...
@@ -4188,32 +4234,48 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
auto outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct));
CombatDamage_Struct* a = (CombatDamage_Struct*)outapp->pBuffer;
a->target = GetID();
if (attacker == nullptr)
if (!attacker) {
a->source = 0;
else if (attacker->IsClient() && attacker->CastToClient()->GMHideMe())
} else if (attacker->IsClient() && attacker->CastToClient()->GMHideMe()) {
a->source = 0;
else
} else {
a->source = attacker->GetID();
}
a->type = (EQ::ValueWithin(skill_used, EQ::skills::Skill1HBlunt, EQ::skills::Skill2HPiercing)) ?
SkillDamageTypes[skill_used] : SkillDamageTypes[EQ::skills::SkillHandtoHand]; // was 0x1c
a->damage = damage;
a->spellid = spell_id;
if (special == eSpecialAttacks::AERampage)
if (special == eSpecialAttacks::AERampage) {
a->special = 1;
else if (special == eSpecialAttacks::Rampage)
} else if (special == eSpecialAttacks::Rampage) {
a->special = 2;
else
} else {
a->special = 0;
}
a->hit_heading = attacker ? attacker->GetHeading() : 0.0f;
if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() &&
(IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) {
a->force = EQ::skills::GetSkillMeleePushForce(skill_used);
if (RuleR(Combat, MeleePushForceClientPercent) && IsClient()) {
a->force += a->force * RuleR(Combat, MeleePushForceClientPercent);
}
if (RuleR(Combat, MeleePushForcePetPercent) && IsPet()) {
a->force += a->force * RuleR(Combat, MeleePushForcePetPercent);
}
if (IsNPC()) {
if (attacker && attacker->IsNPC()) {
if (!RuleB(Combat, NPCtoNPCPush) && attacker && attacker->IsNPC()) {
a->force = 0.0f; // 2013 change that disabled NPC vs NPC push
} else {
a->force *= 0.10f; // force against NPCs is divided by 10 I guess? ex bash is 0.3, parsed 0.03 against an NPC
}
if (ForcedMovement == 0 && a->force != 0.0f && position_update_melee_push_timer.Check()) {
m_Delta.x += a->force * g_Math.FastSin(a->hit_heading);
m_Delta.y += a->force * g_Math.FastCos(a->hit_heading);
@@ -5015,7 +5077,7 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
hit.damage_done = (hit.damage_done * SlayDmgBonus) / 100;
/* Female */
if (GetGender() == 1) {
if (GetGender() == Gender::Female) {
entity_list.FilteredMessageCloseString(
this, /* Sender */
false, /* Skip Sender */
@@ -5183,7 +5245,6 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
bool Mob::TryFinishingBlow(Mob *defender, int64 &damage)
{
float hp_limit = 10.0f;
auto fb_hp_limit = std::max(
{
aabonuses.FinishingBlowLvl[SBIndex::FINISHING_BLOW_LEVEL_HP_RATIO],
@@ -5195,28 +5256,36 @@ bool Mob::TryFinishingBlow(Mob *defender, int64 &damage)
if (fb_hp_limit) {
hp_limit = fb_hp_limit/10.0f;
}
if (defender && !defender->IsClient() && defender->GetHPRatio() < hp_limit) {
uint32 FB_Dmg =
if (defender && !defender->IsClient() && defender->GetHPRatio() < hp_limit) {
uint32 finishing_blow_damage =
aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] + spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG] + itembonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_DMG];
uint32 FB_Level = 0;
FB_Level = aabonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX];
if (FB_Level < spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX])
FB_Level = spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX];
else if (FB_Level < itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX])
FB_Level = itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX];
uint32 finishing_blow_level = 0;
finishing_blow_level = aabonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX];
if (finishing_blow_level < spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX]) {
finishing_blow_level = spellbonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX];
} else if (finishing_blow_level < itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX]) {
finishing_blow_level = itembonuses.FinishingBlowLvl[SBIndex::FINISHING_EFFECT_LEVEL_MAX];
}
// modern AA description says rank 1 (500) is 50% chance
int ProcChance = (
int proc_chance = (
aabonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] +
itembonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE] +
spellbonuses.FinishingBlow[SBIndex::FINISHING_EFFECT_PROC_CHANCE]
);
if (FB_Level && FB_Dmg && (defender->GetLevel() <= FB_Level) &&
(ProcChance >= zone->random.Int(1, 1000))) {
if (
(
(RuleB(Combat, FinishingBlowOnlyWhenFleeing) && !defender->currently_fleeing) ||
!RuleB(Combat, FinishingBlowOnlyWhenFleeing)
) &&
finishing_blow_level &&
finishing_blow_damage &&
defender->GetLevel() <= finishing_blow_level &&
proc_chance >= zone->random.Int(1, 1000)
) {
/* Finishing Blow Critical Message */
entity_list.FilteredMessageCloseString(
this, /* Sender */
@@ -5229,7 +5298,7 @@ bool Mob::TryFinishingBlow(Mob *defender, int64 &damage)
GetCleanName() /* Message1 */
);
damage = FB_Dmg;
damage = finishing_blow_damage;
return true;
}
}
@@ -6287,15 +6356,21 @@ void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell)
// you can only triple from the main hand
if (hand == EQ::invslot::slotPrimary && CanThisClassTripleAttack()) {
CheckIncreaseSkill(EQ::skills::SkillTripleAttack, target, -10);
if (!RuleB(Combat, ClassicTripleAttack)) {
CheckIncreaseSkill(EQ::skills::SkillTripleAttack, target, -10);
}
if (CheckTripleAttack()) {
Attack(target, hand, false, false, IsFromSpell);
auto flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance +
int flurry_chance = aabonuses.FlurryChance + spellbonuses.FlurryChance +
itembonuses.FlurryChance;
if (flurrychance && zone->random.Roll(flurrychance)) {
if (flurry_chance && zone->random.Roll(flurry_chance)) {
Attack(target, hand, false, false, IsFromSpell);
if (zone->random.Roll(flurrychance))
if (zone->random.Roll(flurry_chance)) {
Attack(target, hand, false, false, IsFromSpell);
}
MessageString(Chat::NPCFlurry, YOU_FLURRY);
}
}
+3 -3
View File
@@ -49,9 +49,9 @@ Beacon::Beacon(const glm::vec4 &in_pos, int lifetime) : Mob(
nullptr, // in_lastname
0, // in_cur_hp
0, // in_max_hp
MALE, // in_gender
INVISIBLE_MAN, // in_race
0, // in_class
Gender::Male, // in_gender
Race::InvisibleMan, // in_race
Class::None, // in_class
BT_NoTarget, // in_bodytype
0, // in_deity
0, // in_level
+20 -40
View File
@@ -281,10 +281,10 @@ Bot::Bot(
case SE_IllusionCopy:
case SE_Illusion: {
if (spell.base_value[x1] == -1) {
if (gender == FEMALE) {
gender = MALE;
} else if (gender == MALE) {
gender = FEMALE;
if (gender == Gender::Female) {
gender = Gender::Male;
} else if (gender == Gender::Male) {
gender = Gender::Female;
}
SendIllusionPacket(
@@ -325,30 +325,30 @@ Bot::Bot(
switch (spell.base_value[x1]) {
case OGRE:
SendAppearancePacket(AT_Size, 9);
SendAppearancePacket(AppearanceType::Size, 9);
break;
case TROLL:
SendAppearancePacket(AT_Size, 8);
SendAppearancePacket(AppearanceType::Size, 8);
break;
case VAHSHIR:
case BARBARIAN:
SendAppearancePacket(AT_Size, 7);
SendAppearancePacket(AppearanceType::Size, 7);
break;
case HALF_ELF:
case WOOD_ELF:
case DARK_ELF:
case FROGLOK:
SendAppearancePacket(AT_Size, 5);
SendAppearancePacket(AppearanceType::Size, 5);
break;
case DWARF:
SendAppearancePacket(AT_Size, 4);
SendAppearancePacket(AppearanceType::Size, 4);
break;
case HALFLING:
case GNOME:
SendAppearancePacket(AT_Size, 3);
SendAppearancePacket(AppearanceType::Size, 3);
break;
default:
SendAppearancePacket(AT_Size, 6);
SendAppearancePacket(AppearanceType::Size, 6);
break;
}
break;
@@ -372,18 +372,18 @@ Bot::Bot(
case SE_Invisibility:
{
invisible = true;
SendAppearancePacket(AT_Invis, 1);
SendAppearancePacket(AppearanceType::Invisibility, 1);
break;
}
case SE_Levitate:
{
if (!zone->CanLevitate())
{
SendAppearancePacket(AT_Levitate, 0);
SendAppearancePacket(AppearanceType::FlyMode, 0);
BuffFadeByEffect(SE_Levitate);
}
else {
SendAppearancePacket(AT_Levitate, 2);
SendAppearancePacket(AppearanceType::FlyMode, 2);
}
break;
}
@@ -2783,10 +2783,12 @@ bool Bot::IsValidTarget(Client* bot_owner, Client* leash_owner, float lo_distanc
// Normally, we wouldn't want to do this without class checks..but, too many issues can arise if we let enchanter animation pets run rampant
if (HasPet()) {
GetPet()->RemoveFromHateList(tar);
GetPet()->RemoveFromRampageList(tar);
GetPet()->SetTarget(nullptr);
}
RemoveFromHateList(tar);
RemoveFromRampageList(tar);
SetTarget(nullptr);
SetAttackFlag(false);
@@ -3674,7 +3676,7 @@ void Bot::LevelBotWithClient(Client* c, uint8 new_level, bool send_appearance) {
}
e->SendHPUpdate();
e->SendAppearancePacket(AT_WhoLevel, new_level, true, true); // who level change
e->SendAppearancePacket(AppearanceType::WhoLevel, new_level, true, true); // who level change
e->AI_AddBotSpells(e->GetBotSpellID());
}
}
@@ -3706,7 +3708,7 @@ void Bot::BotAddEquipItem(uint16 slot_id, uint32 item_id) {
UpdateEquipmentLight();
if (UpdateActiveLight() && GetID()) { // temp hack fix
SendAppearancePacket(AT_Light, GetActiveLightType());
SendAppearancePacket(AppearanceType::Light, GetActiveLightType());
}
}
}
@@ -3726,7 +3728,7 @@ void Bot::BotRemoveEquipItem(uint16 slot_id)
UpdateEquipmentLight();
if (UpdateActiveLight()) {
SendAppearancePacket(AT_Light, GetActiveLightType());
SendAppearancePacket(AppearanceType::Light, GetActiveLightType());
}
}
@@ -7894,7 +7896,7 @@ void Bot::BotGroupSay(Mob *speaker, const char *msg, ...) {
if (speaker->HasGroup()) {
Group *g = speaker->GetGroup();
if (g)
g->GroupMessage(speaker->CastToMob(), 0, 100, buf);
g->GroupMessage(speaker->CastToMob(), Language::CommonTongue, Language::MaxValue, buf);
} else
speaker->Say("%s", buf);
}
@@ -8138,28 +8140,6 @@ bool Bot::DyeArmor(int16 slot_id, uint32 rgb, bool all_flag, bool save_flag)
return true;
}
std::string Bot::CreateSayLink(Client* c, const char* message, const char* name)
{
// TODO: review
int saylink_size = strlen(message);
char* escaped_string = new char[saylink_size * 2];
database.DoEscapeString(escaped_string, message, saylink_size);
uint32 saylink_id = database.LoadSaylinkID(escaped_string);
safe_delete_array(escaped_string);
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemData);
linker.SetProxyItemID(SAYLINK_ITEM_ID);
linker.SetProxyAugment1ID(saylink_id);
linker.SetProxyText(name);
auto saylink = linker.GenerateLink();
return saylink;
}
void Bot::Signal(int signal_id)
{
if (parse->BotHasQuestSub(EVENT_SIGNAL)) {
-2
View File
@@ -659,8 +659,6 @@ public:
void SetBotEnforceSpellSetting(bool enforcespellsettings, bool save = false);
bool GetBotEnforceSpellSetting() const { return m_enforce_spell_settings; }
std::string CreateSayLink(Client* botOwner, const char* message, const char* name);
// Class Destructors
~Bot() override;
+58 -71
View File
@@ -404,38 +404,38 @@ public:
for (int i = EffectIDFirst; i <= EffectIDLast; ++i) {
int effect_index = EFFECTIDTOINDEX(i);
if (spells[spell_id].base_value[effect_index] <= 0)
if (spells[spell_id].max_value[effect_index] <= 0)
continue;
switch (spells[spell_id].effect_id[effect_index]) {
case SE_ResistFire:
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(
BCEnum::RT_Fire)] += spells[spell_id].base_value[effect_index];
BCEnum::RT_Fire)] += spells[spell_id].max_value[effect_index];
break;
case SE_ResistCold:
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(
BCEnum::RT_Cold)] += spells[spell_id].base_value[effect_index];
BCEnum::RT_Cold)] += spells[spell_id].max_value[effect_index];
break;
case SE_ResistPoison:
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(
BCEnum::RT_Poison)] += spells[spell_id].base_value[effect_index];
BCEnum::RT_Poison)] += spells[spell_id].max_value[effect_index];
break;
case SE_ResistDisease:
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(
BCEnum::RT_Disease)] += spells[spell_id].base_value[effect_index];
BCEnum::RT_Disease)] += spells[spell_id].max_value[effect_index];
break;
case SE_ResistMagic:
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(
BCEnum::RT_Magic)] += spells[spell_id].base_value[effect_index];
BCEnum::RT_Magic)] += spells[spell_id].max_value[effect_index];
break;
case SE_ResistCorruption:
entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX(
BCEnum::RT_Corruption)] += spells[spell_id].base_value[effect_index];
BCEnum::RT_Corruption)] += spells[spell_id].max_value[effect_index];
break;
default:
continue;
}
entry_prototype->SafeCastToResistance()->resist_total += spells[spell_id].base_value[effect_index];
entry_prototype->SafeCastToResistance()->resist_total += spells[spell_id].max_value[effect_index];
valid_spell = true;
}
if (!valid_spell) {
@@ -2870,7 +2870,7 @@ void bot_command_apply_poison(Client *c, const Seperator *sep)
}
if (my_rogue_bot->GetLevel() < 18) {
c->Message(Chat::White, "Your rogue bot must be level 18 before %s can apply poison!", (my_rogue_bot->GetGender() == 1 ? "she" : "he"));
c->Message(Chat::White, "Your rogue bot must be level 18 before %s can apply poison!", (my_rogue_bot->GetGender() == Gender::Female ? "she" : "he"));
return;
}
@@ -4003,8 +4003,6 @@ void bot_command_item_use(Client* c, const Seperator* sep)
}
}
std::string text_link;
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
@@ -4050,15 +4048,6 @@ void bot_command_item_use(Client* c, const Seperator* sep)
continue;
}
text_link = bot_iter->CreateSayLink(
c,
fmt::format(
"^inventorygive byname {}",
bot_iter->GetCleanName()
).c_str(),
bot_iter->GetCleanName()
);
for (const auto& slot_iter : equipable_slot_list) {
// needs more failure criteria - this should cover the bulk for now
if (slot_iter == EQ::invslot::slotSecondary && item_data->Damage && !bot_iter->CanThisClassDualWield()) {
@@ -4074,7 +4063,13 @@ void bot_command_item_use(Client* c, const Seperator* sep)
Chat::Say,
fmt::format(
"{} says, 'I can use that for my {} instead of my {}! Would you like to {} my {}?'",
text_link,
Saylink::Silent(
fmt::format(
"^inventorygive byname {}",
bot_iter->GetCleanName()
),
bot_iter->GetCleanName()
),
EQ::invslot::GetInvPossessionsSlotName(slot_iter),
linker.GenerateLink(),
Saylink::Silent(
@@ -4096,7 +4091,13 @@ void bot_command_item_use(Client* c, const Seperator* sep)
Chat::Say,
fmt::format(
"{} says, 'I can use that for my {}! Would you like to {} it to me?'",
text_link,
Saylink::Silent(
fmt::format(
"^inventorygive byname {}",
bot_iter->GetCleanName()
),
bot_iter->GetCleanName()
),
EQ::invslot::GetInvPossessionsSlotName(slot_iter),
Saylink::Silent(
fmt::format(
@@ -5633,7 +5634,7 @@ void bot_subcommand_bot_beard_color(Client *c, const Seperator *sep)
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
if (my_bot->GetGender() != MALE && my_bot->GetRace() != DWARF)
if (my_bot->GetGender() != Gender::Male && my_bot->GetRace() != DWARF)
fail_type = BCEnum::AFT_GenderRace;
else if (!PlayerAppearance::IsValidBeardColor(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
@@ -5670,7 +5671,7 @@ void bot_subcommand_bot_beard_style(Client *c, const Seperator *sep)
uint8 uvalue = Strings::ToInt(sep->arg[1]);
auto fail_type = BCEnum::AFT_None;
if (my_bot->GetGender() != MALE && my_bot->GetRace() != DWARF)
if (my_bot->GetGender() != Gender::Male && my_bot->GetRace() != DWARF)
fail_type = BCEnum::AFT_GenderRace;
else if (!PlayerAppearance::IsValidBeard(my_bot->GetRace(), my_bot->GetGender(), uvalue))
fail_type = BCEnum::AFT_Value;
@@ -6171,18 +6172,18 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
return;
}
auto bot_gender = MALE;
auto bot_gender = Gender::Male;
if (sep->IsNumber(4)) {
bot_gender = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[4]));
if (bot_gender == NEUTER) {
bot_gender = MALE;
if (bot_gender == Gender::Neuter) {
bot_gender = Gender::Male;
}
} else {
if (!strcasecmp(sep->arg[4], "m") || !strcasecmp(sep->arg[4], "male")) {
bot_gender = MALE;
bot_gender = Gender::Male;
} else if (!strcasecmp(sep->arg[4], "f") || !strcasecmp(sep->arg[4], "female")) {
bot_gender = FEMALE;
bot_gender = Gender::Female;
}
}
@@ -7594,7 +7595,7 @@ void bot_subcommand_bot_toggle_helm(Client *c, const Seperator *sep)
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
SpawnAppearance_Struct* saptr = (SpawnAppearance_Struct*)outapp->pBuffer;
saptr->spawn_id = bot_iter->GetID();
saptr->type = AT_ShowHelm;
saptr->type = AppearanceType::ShowHelm;
saptr->parameter = bot_iter->GetShowHelm();
entity_list.QueueClients(bot_iter, outapp);
@@ -7667,7 +7668,7 @@ void bot_subcommand_bot_toggle_helm(Client *c, const Seperator *sep)
[10-16-2015 :: 22:15:40] [Packet :: Server -> Client (Dump)] [OP_SpawnAppearance - 0x01d1] [Size: 10]
0: A2 02 2B 00 00 00 00 00 - showhelm = false
*** Bot did not update using the OP_SpawnAppearance packet with AT_ShowHelm appearance type ***
*** Bot did not update using the OP_SpawnAppearance packet with AppearanceType::ShowHelm appearance type ***
*/
}
@@ -7694,7 +7695,7 @@ void bot_subcommand_bot_update(Client *c, const Seperator *sep)
bot_iter->SetPetChooser(false);
bot_iter->CalcBotStats(c->GetBotOption(Client::booStatsUpdate));
bot_iter->SendAppearancePacket(AT_WhoLevel, bot_iter->GetLevel(), true, true);
bot_iter->SendAppearancePacket(AppearanceType::WhoLevel, bot_iter->GetLevel(), true, true);
++bot_count;
}
@@ -9542,10 +9543,6 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
if (!Bot::IsValidRaceClassCombo(bot_race, bot_class)) {
const std::string bot_race_name = GetRaceIDName(bot_race);
const std::string bot_class_name = GetClassIDName(bot_class);
const auto view_saylink = Saylink::Silent(
fmt::format("^viewcombos {}", bot_race),
"view"
);
bot_owner->Message(
Chat::White,
@@ -9553,7 +9550,10 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
"{} {} is an invalid race-class combination, would you like to {} proper combinations for {}?",
bot_race_name,
bot_class_name,
view_saylink,
Saylink::Silent(
fmt::format("^viewcombos {}", bot_race),
"view"
),
bot_race_name
).c_str()
);
@@ -9561,15 +9561,15 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
return bot_id;
}
if (!EQ::ValueWithin(bot_gender, MALE, FEMALE)) {
if (!EQ::ValueWithin(bot_gender, Gender::Male, Gender::Female)) {
bot_owner->Message(
Chat::White,
fmt::format(
"Gender: {} ({}) or {} ({})",
GetGenderName(MALE),
MALE,
GetGenderName(FEMALE),
FEMALE
GetGenderName(Gender::Male),
Gender::Male,
GetGenderName(Gender::Female),
Gender::Female
).c_str()
);
return bot_id;
@@ -9871,9 +9871,6 @@ void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_b
return;
}
std::string msg;
std::string text_link;
auto destination_count = 0;
auto destination_number = 1;
for (auto list_iter : *local_list) {
@@ -9891,24 +9888,19 @@ void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_b
continue;
}
msg = fmt::format(
"^circle {}{}",
spells[local_entry->spell_id].teleport_zone,
single_flag ? " single" : ""
);
text_link = druid_bot->CreateSayLink(
bot_owner,
msg.c_str(),
"Goto"
);
druid_bot->OwnerMessage(
fmt::format(
"Destination {} | {} | {}",
destination_number,
local_entry->long_name,
text_link
Saylink::Silent(
fmt::format(
"^circle {}{}",
spells[local_entry->spell_id].teleport_zone,
single_flag ? " single" : ""
),
"Goto"
)
)
);
@@ -9926,24 +9918,19 @@ void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_b
continue;
}
msg = fmt::format(
"^portal {}{}",
spells[local_entry->spell_id].teleport_zone,
single_flag ? " single" : ""
);
text_link = wizard_bot->CreateSayLink(
bot_owner,
msg.c_str(),
"Goto"
);
wizard_bot->OwnerMessage(
fmt::format(
"Destination {} | {} | {}",
destination_number,
local_entry->long_name,
text_link
Saylink::Silent(
fmt::format(
"^portal {}{}",
spells[local_entry->spell_id].teleport_zone,
single_flag ? " single" : ""
),
"Goto"
)
)
);
+2 -2
View File
@@ -367,7 +367,7 @@ bool Bot::BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
if (casted_spell && GetClass() != Class::Bard) {
if (raid) {
const auto msg = fmt::format("Attempting to slow {}.", tar->GetCleanName());
raid->RaidSay(msg.c_str(), GetCleanName(), 0, 100);
raid->RaidSay(msg.c_str(), GetCleanName(), Language::CommonTongue, Language::MaxValue);
} else {
BotGroupSay(
this,
@@ -1159,7 +1159,7 @@ bool Bot::BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
} else if (IsRaidGrouped()) {
uint32 r_group = raid->GetGroup(GetName());
const auto msg = fmt::format("Casting {}.", spells[botSpell.SpellId].name);
raid->RaidGroupSay(msg.c_str(), GetCleanName(), 0, 100);
raid->RaidGroupSay(msg.c_str(), GetCleanName(), Language::CommonTongue, Language::MaxValue);
std::vector<RaidMember> raid_group_members = raid->GetRaidGroupMembers(r_group);
for (const auto& rgm : raid_group_members) {
if (rgm.member && !rgm.member->qglobal) {
+2 -2
View File
@@ -349,10 +349,10 @@ void CheatManager::ProcessMovementHistory(const EQApplicationPacket *app)
void CheatManager::ProcessSpawnApperance(uint16 spawn_id, uint16 type, uint32 parameter)
{
if (type == AT_Anim && parameter == ANIM_SIT) {
if (type == AppearanceType::Animation && parameter == Animation::Sitting) {
m_time_since_last_memorization = Timer::GetCurrentTime();
}
else if (spawn_id == 0 && type == AT_AntiCheat) {
else if (spawn_id == 0 && type == AppearanceType::AntiCheat) {
m_time_since_last_action = parameter;
}
}
+245 -174
View File
@@ -58,6 +58,7 @@ extern volatile bool RunLoops;
#include "mob_movement_manager.h"
#include "cheat_manager.h"
#include "../common/repositories/character_alternate_abilities_repository.h"
#include "../common/repositories/account_flags_repository.h"
#include "../common/repositories/bug_reports_repository.h"
#include "../common/repositories/char_recipe_list_repository.h"
@@ -86,9 +87,9 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
"", // in_lastname
0, // in_cur_hp
0, // in_max_hp
0, // in_gender
0, // in_race
0, // in_class
Gender::Male, // in_gender
Race::Doug, // in_race
Class::None, // in_class
BT_Humanoid, // in_bodytype
0, // in_deity
0, // in_level
@@ -483,7 +484,7 @@ void Client::SendZoneInPackets()
// Spawn Appearance Packet
auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer;
sa->type = AT_SpawnID; // Is 0x10 used to set the player id?
sa->type = AppearanceType::SpawnID; // Is 0x10 used to set the player id?
sa->parameter = GetID(); // Four bytes for this parameter...
outapp->priority = 6;
QueuePacket(outapp);
@@ -498,7 +499,7 @@ void Client::SendZoneInPackets()
safe_delete(outapp);
SetSpawned();
if (GetPVP(false)) //force a PVP update until we fix the spawn struct
SendAppearancePacket(AT_PVP, GetPVP(false), true, false);
SendAppearancePacket(AppearanceType::PVP, GetPVP(false), true, false);
//Send AA Exp packet:
if (GetLevel() >= 51)
@@ -586,45 +587,52 @@ void Client::ReportConnectingState() {
};
}
bool Client::SaveAA() {
std::string iquery;
int spentpoints = 0;
int i = 0;
for(auto &rank : aa_ranks) {
AA::Ability *ability = zone->GetAlternateAdvancementAbility(rank.first);
if(!ability)
bool Client::SaveAA()
{
std::vector<CharacterAlternateAbilitiesRepository::CharacterAlternateAbilities> v;
uint32 aa_points_spent = 0;
auto e = CharacterAlternateAbilitiesRepository::NewEntity();
for (auto &rank : aa_ranks) {
auto a = zone->GetAlternateAdvancementAbility(rank.first);
if (!a) {
continue;
}
if(rank.second.first > 0) {
AA::Rank *r = ability->GetRankByPointsSpent(rank.second.first);
if(!r)
if (rank.second.first > 0) {
auto r = a->GetRankByPointsSpent(rank.second.first);
if (!r) {
continue;
spentpoints += r->total_cost;
if(i == 0) {
iquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, aa_id, aa_value, charges)"
" VALUES (%u, %u, %u, %u)", character_id, ability->first_rank_id, rank.second.first, rank.second.second);
} else {
iquery += StringFormat(", (%u, %u, %u, %u)", character_id, ability->first_rank_id, rank.second.first, rank.second.second);
}
i++;
aa_points_spent += r->total_cost;
e.id = character_id;
e.aa_id = a->first_rank_id;
e.aa_value = rank.second.first;
e.charges = rank.second.second;
v.emplace_back(e);
}
}
m_pp.aapoints_spent = spentpoints + m_epp.expended_aa;
m_pp.aapoints_spent = aa_points_spent + m_epp.expended_aa;
if(iquery.length() > 0) {
database.QueryDatabase(iquery);
}
return true;
return CharacterAlternateAbilitiesRepository::ReplaceMany(database, v);
}
void Client::RemoveExpendedAA(int aa_id)
{
database.QueryDatabase(StringFormat("DELETE from `character_alternate_abilities` WHERE `id` = %d and `aa_id` = %d", character_id, aa_id));
CharacterAlternateAbilitiesRepository::DeleteWhere(
database,
fmt::format(
"`id` = {} AND `aa_id` = {}",
CharacterID(),
aa_id
)
);
}
bool Client::Save(uint8 iCommitNow) {
@@ -915,15 +923,19 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
// Garble the message based on drunkness
if (GetIntoxication() > 0 && !(RuleB(Chat, ServerWideOOC) && chan_num == ChatChannel_OOC) && !GetGM()) {
GarbleMessage(message, (int)(GetIntoxication() / 3));
language = 0; // No need for language when drunk
lang_skill = 100;
language = Language::CommonTongue; // No need for language when drunk
lang_skill = Language::MaxValue;
}
// some channels don't use languages
if (chan_num == ChatChannel_OOC || chan_num == ChatChannel_GMSAY || chan_num == ChatChannel_Broadcast || chan_num == ChatChannel_Petition)
{
language = 0;
lang_skill = 100;
if (
chan_num == ChatChannel_OOC ||
chan_num == ChatChannel_GMSAY ||
chan_num == ChatChannel_Broadcast ||
chan_num == ChatChannel_Petition
) {
language = Language::CommonTongue;
lang_skill = Language::MaxValue;
}
// Censor the message
@@ -1274,69 +1286,88 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
}
}
void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...) {
if ((chan_num==11 && !(GetGM())) || (chan_num==10 && Admin() < AccountStatus::QuestTroupe)) // dont need to send /pr & /petition to everybody
void Client::ChannelMessageSend(
const char *from,
const char *to,
uint8 channel_id,
uint8 language_id,
uint8 language_skill,
const char *message,
...
)
{
if (
(channel_id == ChatChannel_Petition && Admin() < AccountStatus::QuestTroupe) ||
(channel_id == ChatChannel_GMSAY && !GetGM())
) {
return;
}
va_list argptr;
char buffer[4096];
char message_sender[64];
char buffer[4096];
char message_sender[64];
va_start(argptr, message);
vsnprintf(buffer, 4096, message, argptr);
va_end(argptr);
EQApplicationPacket app(OP_ChannelMessage, sizeof(ChannelMessage_Struct)+strlen(buffer)+1);
ChannelMessage_Struct* cm = (ChannelMessage_Struct*)app.pBuffer;
EQApplicationPacket app(OP_ChannelMessage, sizeof(ChannelMessage_Struct) + strlen(buffer) + 1);
if (from == 0)
auto* cm = (ChannelMessage_Struct *) app.pBuffer;
if (from == 0) {
strcpy(cm->sender, "ZServer");
else if (from[0] == 0)
} else if (from[0] == 0) {
strcpy(cm->sender, "ZServer");
else {
} else {
CleanMobName(from, message_sender);
strcpy(cm->sender, message_sender);
}
if (to != 0)
if (to != 0) {
strcpy((char *) cm->targetname, to);
else if (chan_num == ChatChannel_Tell)
} else if (channel_id == ChatChannel_Tell) {
strcpy(cm->targetname, m_pp.name);
else
} else {
cm->targetname[0] = 0;
uint8 ListenerSkill;
if (language < MAX_PP_LANGUAGE) {
ListenerSkill = m_pp.languages[language];
if (ListenerSkill < 24) {
cm->language = (MAX_PP_LANGUAGE - 1); // in an unknown tongue
}
else {
cm->language = language;
}
}
else {
ListenerSkill = m_pp.languages[0];
cm->language = 0;
uint8 listener_skill;
const bool is_valid_language = EQ::ValueWithin(language_id, Language::CommonTongue, Language::Unknown27);
if (is_valid_language) {
listener_skill = m_pp.languages[language_id];
cm->language = listener_skill < 24 ? Language::Unknown27 : language_id;
} else {
listener_skill = m_pp.languages[Language::CommonTongue];
cm->language = Language::CommonTongue;
}
// set effective language skill = lower of sender and receiver skills
int32 EffSkill = (lang_skill < ListenerSkill ? lang_skill : ListenerSkill);
if (EffSkill > 100) // maximum language skill is 100
EffSkill = 100;
cm->skill_in_language = EffSkill;
uint8 effective_skill = (language_skill < listener_skill ? language_skill : listener_skill);
if (effective_skill > Language::MaxValue) {
effective_skill = Language::MaxValue;
}
cm->chan_num = chan_num;
cm->skill_in_language = effective_skill;
cm->chan_num = channel_id;
strcpy(&cm->message[0], buffer);
QueuePacket(&app);
bool senderCanTrainSelf = RuleB(Client, SelfLanguageLearning);
bool weAreNotSender = strcmp(GetCleanName(), cm->sender);
const bool can_train_self = RuleB(Client, SelfLanguageLearning);
const bool is_not_sender = strcmp(GetCleanName(), cm->sender);
if (senderCanTrainSelf || weAreNotSender) {
if ((chan_num == ChatChannel_Group) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up
if (language < MAX_PP_LANGUAGE && m_pp.languages[language] <= lang_skill)
CheckLanguageSkillIncrease(language, lang_skill);
if (can_train_self || is_not_sender) {
if (
channel_id == ChatChannel_Group &&
listener_skill < Language::MaxValue
) { // group message in non-mastered language, check for skill up
if (is_valid_language && m_pp.languages[language_id] <= language_skill) {
CheckLanguageSkillIncrease(language_id, language_skill);
}
}
}
}
@@ -1637,26 +1668,30 @@ void Client::SetSkill(EQ::skills::SkillType skillid, uint16 value) {
safe_delete(outapp);
}
void Client::IncreaseLanguageSkill(int skill_id, int value) {
void Client::IncreaseLanguageSkill(uint8 language_id, uint8 increase)
{
if (!EQ::ValueWithin(language_id, Language::CommonTongue, Language::Unknown27)) {
return;
}
if (skill_id >= MAX_PP_LANGUAGE)
return; //Invalid lang id
m_pp.languages[language_id] += increase;
m_pp.languages[skill_id] += value;
if (m_pp.languages[language_id] > Language::MaxValue) {
m_pp.languages[language_id] = Language::MaxValue;
}
if (m_pp.languages[skill_id] > 100) //Lang skill above max
m_pp.languages[skill_id] = 100;
database.SaveCharacterLanguage(CharacterID(), skill_id, m_pp.languages[skill_id]);
database.SaveCharacterLanguage(CharacterID(), language_id, m_pp.languages[language_id]);
auto outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct));
SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer;
skill->skillId = 100 + skill_id;
skill->value = m_pp.languages[skill_id];
auto* s = (SkillUpdate_Struct*) outapp->pBuffer;
s->skillId = 100 + language_id;
s->value = m_pp.languages[language_id];
QueuePacket(outapp);
safe_delete(outapp);
MessageString( Chat::Skills, LANG_SKILL_IMPROVED ); //Notify client
MessageString(Chat::Skills, LANG_SKILL_IMPROVED);
}
void Client::AddSkill(EQ::skills::SkillType skillid, uint16 value) {
@@ -2221,7 +2256,7 @@ void Client::SetGM(bool toggle) {
m_pp.gm ? "now" : "no longer"
).c_str()
);
SendAppearancePacket(AT_GM, m_pp.gm);
SendAppearancePacket(AppearanceType::GM, m_pp.gm);
Save();
UpdateWho();
}
@@ -2272,9 +2307,9 @@ void Client::ReadBook(BookRequest_Struct *book) {
memcpy(out->booktext, booktxt2.c_str(), length);
if (book_language > 0 && book_language < MAX_PP_LANGUAGE) {
if (m_pp.languages[book_language] < 100) {
GarbleMessage(out->booktext, (100 - m_pp.languages[book_language]));
if (EQ::ValueWithin(book_language, Language::CommonTongue, Language::Unknown27)) {
if (m_pp.languages[book_language] < Language::MaxValue) {
GarbleMessage(out->booktext, (Language::MaxValue - m_pp.languages[book_language]));
}
}
@@ -2673,38 +2708,43 @@ bool Client::CheckIncreaseSkill(EQ::skills::SkillType skillid, Mob *against_who,
return false;
}
void Client::CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill) {
if (IsDead() || IsUnconscious())
void Client::CheckLanguageSkillIncrease(uint8 language_id, uint8 teacher_skill) {
if (IsDead() || IsUnconscious()) {
return;
if (IsAIControlled())
}
if (IsAIControlled()) {
return;
if (langid >= MAX_PP_LANGUAGE)
return; // do nothing if langid is an invalid language
}
int LangSkill = m_pp.languages[langid]; // get current language skill
if (!EQ::ValueWithin(language_id, Language::CommonTongue, Language::Unknown27)) {
return;
}
if (LangSkill < 100) { // if the language isn't already maxed
int32 Chance = 5 + ((TeacherSkill - LangSkill)/10); // greater chance to learn if teacher's skill is much higher than yours
Chance = (Chance * RuleI(Character, SkillUpModifier)/100);
const uint8 language_skill = m_pp.languages[language_id]; // get current language skill
if(zone->random.Real(0,100) < Chance) { // if they make the roll
IncreaseLanguageSkill(langid); // increase the language skill by 1
if (language_skill < Language::MaxValue) { // if the language isn't already maxed
int chance = 5 + ((teacher_skill - language_skill) / 10); // greater chance to learn if teacher's skill is much higher than yours
chance = (chance * RuleI(Character, SkillUpModifier) / 100);
if (zone->random.Real(0, 100) < chance) { // if they make the roll
IncreaseLanguageSkill(language_id);
if (parse->PlayerHasQuestSub(EVENT_LANGUAGE_SKILL_UP)) {
const auto& export_string = fmt::format(
const auto &export_string = fmt::format(
"{} {} {}",
langid,
LangSkill + 1,
100
language_id,
language_skill + 1,
Language::MaxValue
);
parse->EventPlayer(EVENT_LANGUAGE_SKILL_UP, this, export_string, 0);
}
LogSkills("Language [{}] at value [{}] successfully gain with [{}] % chance", langid, LangSkill, Chance);
LogSkills("Language [{}] at value [{}] successfully gain with [{}] % chance", language_id, language_skill, chance);
} else {
LogSkills("Language [{}] at value [{}] failed to gain with [{}] % chance", language_id, language_skill, chance);
}
else
LogSkills("Language [{}] at value [{}] failed to gain with [{}] % chance", langid, LangSkill, Chance);
}
}
@@ -2839,7 +2879,7 @@ void Client::SetPVP(bool toggle, bool message) {
}
}
SendAppearancePacket(AT_PVP, GetPVP());
SendAppearancePacket(AppearanceType::PVP, GetPVP());
Save();
}
@@ -3566,25 +3606,30 @@ void Client::SetHideMe(bool flag)
UpdateWho();
}
void Client::SetLanguageSkill(int langid, int value)
void Client::SetLanguageSkill(uint8 language_id, uint8 language_skill)
{
if (langid >= MAX_PP_LANGUAGE)
return; //Invalid Language
if (!EQ::ValueWithin(language_id, Language::CommonTongue, Language::Unknown27)) {
return;
}
if (value > 100)
value = 100; //Max lang value
if (language_skill > Language::MaxValue) {
language_skill = Language::MaxValue;
}
m_pp.languages[langid] = value;
database.SaveCharacterLanguage(CharacterID(), langid, value);
m_pp.languages[language_id] = language_skill;
database.SaveCharacterLanguage(CharacterID(), language_id, language_skill);
auto outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct));
SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer;
skill->skillId = 100 + langid;
skill->value = m_pp.languages[langid];
auto* s = (SkillUpdate_Struct*) outapp->pBuffer;
s->skillId = 100 + language_id;
s->value = m_pp.languages[language_id];
QueuePacket(outapp);
safe_delete(outapp);
MessageString( Chat::Skills, LANG_SKILL_IMPROVED ); //Notify the client
MessageString(Chat::Skills, LANG_SKILL_IMPROVED);
}
void Client::LinkDead()
@@ -3607,7 +3652,7 @@ void Client::LinkDead()
// save_timer.Start(2500);
linkdead_timer.Start(RuleI(Zone,ClientLinkdeadMS));
SendAppearancePacket(AT_Linkdead, 1);
SendAppearancePacket(AppearanceType::Linkdead, 1);
client_state = CLIENT_LINKDEAD;
AI_Start(CLIENT_LD_TIMEOUT);
}
@@ -3750,7 +3795,7 @@ void Client::EnteringMessages(Client* client)
).c_str()
);
client->SendAppearancePacket(AT_Anim, ANIM_FREEZE);
client->SendAppearancePacket(AppearanceType::Animation, Animation::Freeze);
}
}
}
@@ -4599,7 +4644,7 @@ void Client::ClearGroupAAs() {
m_pp.raid_leadership_exp = 0;
Save();
database.SaveCharacterLeadershipAA(CharacterID(), &m_pp);
database.SaveCharacterLeadershipAbilities(CharacterID(), &m_pp);
}
void Client::UpdateGroupAAs(int32 points, uint32 type) {
@@ -6519,27 +6564,33 @@ void Client::RemoveFromInstance(uint16 instance_id)
void Client::SendAltCurrencies() {
if (ClientVersion() >= EQ::versions::ClientVersion::SoF) {
uint32 count = zone->AlternateCurrencies.size();
if(count == 0) {
const uint32 currency_count = zone->AlternateCurrencies.size();
if (!currency_count) {
return;
}
auto outapp = new EQApplicationPacket(
OP_AltCurrency,
sizeof(AltCurrencyPopulate_Struct) +
sizeof(AltCurrencyPopulateEntry_Struct) * currency_count
);
auto outapp =
new EQApplicationPacket(OP_AltCurrency, sizeof(AltCurrencyPopulate_Struct) +
sizeof(AltCurrencyPopulateEntry_Struct) * count);
AltCurrencyPopulate_Struct *altc = (AltCurrencyPopulate_Struct*)outapp->pBuffer;
altc->opcode = ALT_CURRENCY_OP_POPULATE;
altc->count = count;
auto a = (AltCurrencyPopulate_Struct*) outapp->pBuffer;
a->opcode = AlternateCurrencyMode::Populate;
a->count = currency_count;
uint32 currency_id = 0;
for (const auto& alternate_currency : zone->AlternateCurrencies) {
const EQ::ItemData* item = database.GetItem(alternate_currency.item_id);
altc->entries[currency_id].currency_number = alternate_currency.id;
altc->entries[currency_id].unknown00 = 1;
altc->entries[currency_id].currency_number2 = alternate_currency.id;
altc->entries[currency_id].item_id = alternate_currency.item_id;
altc->entries[currency_id].item_icon = item ? item->Icon : 1000;
altc->entries[currency_id].stack_size = item ? item->StackSize : 1000;
for (const auto& c : zone->AlternateCurrencies) {
const auto* item = database.GetItem(c.item_id);
a->entries[currency_id].currency_number = c.id;
a->entries[currency_id].unknown00 = 1;
a->entries[currency_id].currency_number2 = c.id;
a->entries[currency_id].item_id = c.item_id;
a->entries[currency_id].item_icon = item ? item->Icon : 1000;
a->entries[currency_id].stack_size = item ? item->StackSize : 1000;
currency_id++;
}
@@ -6636,11 +6687,8 @@ void Client::SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null)
uint32 Client::GetAlternateCurrencyValue(uint32 currency_id) const
{
auto iter = alternate_currency.find(currency_id);
if(iter == alternate_currency.end()) {
return 0;
} else {
return (*iter).second;
}
return iter == alternate_currency.end() ? 0 : (*iter).second;
}
void Client::ProcessAlternateCurrencyQueue() {
@@ -8565,37 +8613,37 @@ bool Client::CanMedOnHorse()
void Client::EnableAreaHPRegen(int value)
{
AreaHPRegen = value * 0.001f;
SendAppearancePacket(AT_AreaHPRegen, value, false);
SendAppearancePacket(AppearanceType::AreaHealthRegen, value, false);
}
void Client::DisableAreaHPRegen()
{
AreaHPRegen = 1.0f;
SendAppearancePacket(AT_AreaHPRegen, 1000, false);
SendAppearancePacket(AppearanceType::AreaHealthRegen, 1000, false);
}
void Client::EnableAreaManaRegen(int value)
{
AreaManaRegen = value * 0.001f;
SendAppearancePacket(AT_AreaManaRegen, value, false);
SendAppearancePacket(AppearanceType::AreaManaRegen, value, false);
}
void Client::DisableAreaManaRegen()
{
AreaManaRegen = 1.0f;
SendAppearancePacket(AT_AreaManaRegen, 1000, false);
SendAppearancePacket(AppearanceType::AreaManaRegen, 1000, false);
}
void Client::EnableAreaEndRegen(int value)
{
AreaEndRegen = value * 0.001f;
SendAppearancePacket(AT_AreaEndRegen, value, false);
SendAppearancePacket(AppearanceType::AreaEnduranceRegen, value, false);
}
void Client::DisableAreaEndRegen()
{
AreaEndRegen = 1.0f;
SendAppearancePacket(AT_AreaEndRegen, 1000, false);
SendAppearancePacket(AppearanceType::AreaEnduranceRegen, 1000, false);
}
void Client::EnableAreaRegens(int value)
@@ -10011,7 +10059,7 @@ void Client::SetAnon(uint8 anon_flag) {
auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
SpawnAppearance_Struct* spawn_appearance = (SpawnAppearance_Struct*)outapp->pBuffer;
spawn_appearance->spawn_id = GetID();
spawn_appearance->type = AT_Anon;
spawn_appearance->type = AppearanceType::Anonymous;
spawn_appearance->parameter = anon_flag;
entity_list.QueueClients(this, outapp);
Save();
@@ -10024,7 +10072,7 @@ void Client::SetAFK(uint8 afk_flag) {
auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
SpawnAppearance_Struct* spawn_appearance = (SpawnAppearance_Struct*)outapp->pBuffer;
spawn_appearance->spawn_id = GetID();
spawn_appearance->type = AT_AFK;
spawn_appearance->type = AppearanceType::AFK;
spawn_appearance->parameter = afk_flag;
entity_list.QueueClients(this, outapp);
safe_delete(outapp);
@@ -10269,7 +10317,7 @@ void Client::RemoveItem(uint32 item_id, uint32 quantity)
}
}
void Client::SetGMStatus(int16 new_status) {
void Client::SetGMStatus(int new_status) {
if (Admin() != new_status) {
database.UpdateGMStatus(AccountID(), new_status);
UpdateAdmin();
@@ -10546,9 +10594,9 @@ void Client::ReadBookByName(std::string book_name, uint8 book_type)
memcpy(out->booktext, book_text.c_str(), length);
if (book_language > 0 && book_language < MAX_PP_LANGUAGE) {
if (m_pp.languages[book_language] < 100) {
GarbleMessage(out->booktext, (100 - m_pp.languages[book_language]));
if (EQ::ValueWithin(book_language, Language::CommonTongue, Language::Unknown27)) {
if (m_pp.languages[book_language] < Language::MaxValue) {
GarbleMessage(out->booktext, (Language::MaxValue - m_pp.languages[book_language]));
}
}
@@ -10697,22 +10745,22 @@ void Client::SaveSpells()
void Client::SaveDisciplines()
{
std::vector<CharacterDisciplinesRepository::CharacterDisciplines> character_discs = {};
std::vector<CharacterDisciplinesRepository::CharacterDisciplines> v;
for (int index = 0; index < MAX_PP_DISCIPLINES; index++) {
if (IsValidSpell(m_pp.disciplines.values[index])) {
auto discipline = CharacterDisciplinesRepository::NewEntity();
discipline.id = CharacterID();
discipline.slot_id = index;
discipline.disc_id = m_pp.disciplines.values[index];
character_discs.emplace_back(discipline);
for (int slot_id = 0; slot_id < MAX_PP_DISCIPLINES; slot_id++) {
if (IsValidSpell(m_pp.disciplines.values[slot_id])) {
auto e = CharacterDisciplinesRepository::NewEntity();
e.id = CharacterID();
e.slot_id = slot_id;
e.disc_id = m_pp.disciplines.values[slot_id];
v.emplace_back(e);
}
}
CharacterDisciplinesRepository::DeleteWhere(database, fmt::format("id = {}", CharacterID()));
if (!character_discs.empty()) {
CharacterDisciplinesRepository::InsertMany(database, character_discs);
if (!v.empty()) {
CharacterDisciplinesRepository::ReplaceMany(database, v);
}
}
@@ -10883,8 +10931,8 @@ void Client::ReconnectUCS()
buffer = StringFormat(
"%s,%i,%s.%s,%c%s",
Config->ChatHost.c_str(),
Config->ChatPort,
Config->GetUCSHost().c_str(),
Config->GetUCSPort(),
Config->ShortName.c_str(),
GetName(),
connection_type,
@@ -10910,8 +10958,8 @@ void Client::ReconnectUCS()
buffer = StringFormat(
"%s,%i,%s.%s,%c%s",
Config->MailHost.c_str(),
Config->MailPort,
Config->GetUCSHost().c_str(),
Config->GetUCSPort(),
Config->ShortName.c_str(),
GetName(),
connection_type,
@@ -11224,7 +11272,7 @@ void Client::Undye()
SendWearChange(slot);
}
database.DeleteCharacterDye(CharacterID());
database.DeleteCharacterMaterialColor(CharacterID());
}
void Client::SetTrackingID(uint32 entity_id)
@@ -11944,3 +11992,26 @@ std::string GetZoneModeString(ZoneMode mode)
return "Unknown";
}
}
void Client::ClearXTargets()
{
if (!XTargettingAvailable()) {
return;
}
for (int i = 0; i < GetMaxXTargets(); ++i) {
if (XTargets[i].ID) {
Mob* m = entity_list.GetMob(XTargets[i].ID);
if (m) {
RemoveXTarget(m, false);
}
XTargets[i].ID = 0;
XTargets[i].Name[0] = 0;
XTargets[i].dirty = false;
SendXTargetPacket(i, nullptr);
}
}
}
+9 -7
View File
@@ -335,7 +335,7 @@ public:
void QueuePacket(const EQApplicationPacket* app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL, eqFilterType filter=FilterNone);
void FastQueuePacket(EQApplicationPacket** app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL);
void ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_skill, const char* orig_message, const char* targetname = nullptr, bool is_silent = false);
void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...);
void ChannelMessageSend(const char* from, const char* to, uint8 channel_id, uint8 language_id, uint8 language_skill, const char* message, ...);
void Message(uint32 type, const char* message, ...);
void FilteredMessage(Mob *sender, uint32 type, eqFilterType filter, const char* message, ...);
void VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber);
@@ -382,7 +382,7 @@ public:
inline ExtendedProfile_Struct& GetEPP() { return m_epp; }
inline EQ::InventoryProfile& GetInv() { return m_inv; }
inline const EQ::InventoryProfile& GetInv() const { return m_inv; }
inline PetInfo* GetPetInfo(uint16 pet) { return (pet==1)?&m_suspendedminion:&m_petinfo; }
inline PetInfo* GetPetInfo(int pet_info_type) { return pet_info_type == PetInfoType::Suspended ? &m_suspendedminion : &m_petinfo; }
inline InspectMessage_Struct& GetInspectMessage() { return m_inspect_message; }
inline const InspectMessage_Struct& GetInspectMessage() const { return m_inspect_message; }
void ReloadExpansionProfileSetting();
@@ -452,7 +452,7 @@ public:
void SendSingleTraderItem(uint32 char_id, int uniqueid);
void BulkSendMerchantInventory(int merchant_id, int npcid);
inline uint8 GetLanguageSkill(uint16 n) const { return m_pp.languages[n]; }
inline uint8 GetLanguageSkill(uint8 language_id) const { return m_pp.languages[language_id]; }
void SendPickPocketResponse(Mob *from, uint32 amt, int type, const EQ::ItemData* item = nullptr);
@@ -767,7 +767,7 @@ public:
void SetSkillPoints(int inp) { m_pp.points = inp;}
void IncreaseSkill(int skill_id, int value = 1) { if (skill_id <= EQ::skills::HIGHEST_SKILL) { m_pp.skills[skill_id] += value; } }
void IncreaseLanguageSkill(int skill_id, int value = 1);
void IncreaseLanguageSkill(uint8 language_id, uint8 increase = 1);
virtual uint16 GetSkill(EQ::skills::SkillType skill_id) const { if (skill_id <= EQ::skills::HIGHEST_SKILL) { return(itembonuses.skillmod[skill_id] > 0 ? (itembonuses.skillmodmax[skill_id] > 0 ? std::min(m_pp.skills[skill_id] + itembonuses.skillmodmax[skill_id], m_pp.skills[skill_id] * (100 + itembonuses.skillmod[skill_id]) / 100) : m_pp.skills[skill_id] * (100 + itembonuses.skillmod[skill_id]) / 100) : m_pp.skills[skill_id]); } return 0; }
uint32 GetRawSkill(EQ::skills::SkillType skill_id) const { if (skill_id <= EQ::skills::HIGHEST_SKILL) { return(m_pp.skills[skill_id]); } return 0; }
bool HasSkill(EQ::skills::SkillType skill_id) const;
@@ -777,8 +777,8 @@ public:
void CheckSpecializeIncrease(uint16 spell_id);
void CheckSongSkillIncrease(uint16 spell_id);
bool CheckIncreaseSkill(EQ::skills::SkillType skillid, Mob *against_who, int chancemodi = 0);
void CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill);
void SetLanguageSkill(int langid, int value);
void CheckLanguageSkillIncrease(uint8 language_id, uint8 teacher_skill);
void SetLanguageSkill(uint8 language_id, uint8 language_skill);
void SetHoTT(uint32 mobid);
void ShowSkillsWindow();
@@ -988,6 +988,7 @@ public:
void IncStats(uint8 type,int16 increase_val);
void DropItem(int16 slot_id, bool recurse = true);
void DropItemQS(EQ::ItemInstance* inst, bool pickup);
bool HasItemOnCorpse(uint32 item_id);
bool IsAugmentRestricted(uint8 item_type, uint32 augment_restriction);
@@ -1527,6 +1528,7 @@ public:
void JoinGroupXTargets(Group *g);
void LeaveGroupXTargets(Group *g);
void LeaveRaidXTargets(Raid *r);
void ClearXTargets();
bool GroupFollow(Client* inviter);
inline bool GetRunMode() const { return runmode; }
@@ -1605,7 +1607,7 @@ public:
void SetAccountFlag(const std::string& flag, const std::string& value);
std::string GetAccountFlag(const std::string& flag);
std::vector<std::string> GetAccountFlags();
void SetGMStatus(int16 new_status);
void SetGMStatus(int new_status);
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);
+1 -1
View File
@@ -1455,7 +1455,7 @@ int32 Client::CalcATK()
uint32 Mob::GetInstrumentMod(uint16 spell_id)
{
if (GetClass() != Class::Bard) {
if (GetClass() != Class::Bard || spells[spell_id].is_discipline || spell_id == SPELL_AMPLIFICATION) {
//Other classes can get a base effects mod using SPA 413
if (HasBaseEffectFocus()) {
return (10 + (GetFocusEffect(focusFcBaseEffects, spell_id) / 10));//TODO: change action->instrument mod to float to support < 10% focus values
+170 -110
View File
@@ -547,8 +547,8 @@ void Client::CompleteConnect()
default: { break; } // GUILD_NONE
}
}
SendAppearancePacket(AT_GuildID, GuildID(), false);
SendAppearancePacket(AT_GuildRank, rank, false);
SendAppearancePacket(AppearanceType::GuildID, GuildID(), false);
SendAppearancePacket(AppearanceType::GuildRank, rank, false);
}
// moved to dbload and translators since we iterate there also .. keep m_pp values whatever they are when they get here
@@ -681,8 +681,11 @@ void Client::CompleteConnect()
break;
}
case SE_SummonHorse: {
SummonHorse(buffs[j1].spellid);
//hasmount = true; //this was false, is that the correct thing?
if (RuleB(Character, PreventMountsFromZoning)) {
BuffFadeByEffect(SE_SummonHorse);
} else {
SummonHorse(buffs[j1].spellid);
}
break;
}
case SE_Silence:
@@ -703,7 +706,7 @@ void Client::CompleteConnect()
case SE_Invisibility2:
case SE_Invisibility:
{
SendAppearancePacket(AT_Invis, Invisibility::Invisible);
SendAppearancePacket(AppearanceType::Invisibility, Invisibility::Invisible);
break;
}
case SE_Levitate:
@@ -712,17 +715,17 @@ void Client::CompleteConnect()
{
if (!GetGM())
{
SendAppearancePacket(AT_Levitate, 0);
SendAppearancePacket(AppearanceType::FlyMode, 0);
BuffFadeByEffect(SE_Levitate);
Message(Chat::Red, "You can't levitate in this zone.");
}
}
else {
if (spell.limit_value[x1] == 1) {
SendAppearancePacket(AT_Levitate, EQ::constants::GravityBehavior::LevitateWhileRunning, true, true);
SendAppearancePacket(AppearanceType::FlyMode, EQ::constants::GravityBehavior::LevitateWhileRunning, true, true);
}
else {
SendAppearancePacket(AT_Levitate, EQ::constants::GravityBehavior::Levitating, true, true);
SendAppearancePacket(AppearanceType::FlyMode, EQ::constants::GravityBehavior::Levitating, true, true);
}
}
break;
@@ -747,7 +750,7 @@ void Client::CompleteConnect()
}
}
/* Sends appearances for all mobs not doing anim_stand aka sitting, looting, playing dead */
/* Sends appearances for all mobs not doing Animation::Standing aka sitting, looting, playing dead */
entity_list.SendZoneAppearance(this);
/* Sends the Nimbus particle effects (up to 3) for any mob using them */
entity_list.SendNimbusEffects(this);
@@ -1268,7 +1271,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */
database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */
database.LoadCharacterMaterialColor(cid, &m_pp); /* Load Character Material */
database.LoadCharacterPotions(cid, &m_pp); /* Load Character Potion Belt */
database.LoadCharacterPotionBelt(cid, &m_pp); /* Load Character Potion Belt */
database.LoadCharacterCurrency(cid, &m_pp); /* Load Character Currency into PP */
database.LoadCharacterData(cid, &m_pp, &m_epp); /* Load Character Data from DB into PP as well as E_PP */
database.LoadCharacterSkills(cid, &m_pp); /* Load Character Skills */
@@ -1277,7 +1280,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
database.LoadCharacterMemmedSpells(cid, &m_pp); /* Load Character Memorized Spells */
database.LoadCharacterDisciplines(cid, &m_pp); /* Load Character Disciplines */
database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */
database.LoadCharacterLeadershipAA(cid, &m_pp); /* Load Character Leadership AA's */
database.LoadCharacterLeadershipAbilities(cid, &m_pp); /* Load Character Leadership AA's */
database.LoadCharacterTribute(this); /* Load CharacterTribute */
// this pattern is strange
@@ -1316,7 +1319,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
/* If GM, not trackable */
if (gm_hide_me) { trackable = false; }
if (gminvul) { invulnerable = true; }
if (flymode > 0) { SendAppearancePacket(AT_Levitate, flymode); }
if (flymode > 0) { SendAppearancePacket(AppearanceType::FlyMode, flymode); }
/* Set Con State for Reporting */
conn_state = PlayerProfileLoaded;
@@ -2737,6 +2740,7 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app)
}
if (!RuleB(Merchant, EnableAltCurrencySell)) {
Message(Chat::Red, "Selling alternate currency items is disabled.");
return;
}
@@ -4326,6 +4330,14 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
LogSpells("OP CastSpell: slot [{}] spell [{}] target [{}] inv [{}]", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot);
CastingSlot slot = static_cast<CastingSlot>(castspell->slot);
if (RuleB(Spells, RequireMnemonicRetention)) {
if (EQ::ValueWithin(castspell->slot, 8, 11) && GetAA(aaMnemonicRetention) < (castspell->slot - 7)) {
InterruptSpell(castspell->spell_id);
Message(Chat::Red, "You do not have the required AA to use this spell slot.");
return;
}
}
/* Memorized Spell */
if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id) {
uint16 spell_to_cast = 0;
@@ -4505,23 +4517,24 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app)
{
ChannelMessage_Struct* cm = (ChannelMessage_Struct*)app->pBuffer;
auto* cm = (ChannelMessage_Struct*) app->pBuffer;
if (app->size < sizeof(ChannelMessage_Struct)) {
std::cout << "Wrong size " << app->size << ", should be " << sizeof(ChannelMessage_Struct) << "+ on 0x" << std::hex << std::setfill('0') << std::setw(4) << app->GetOpcode() << std::dec << std::endl;
return;
}
if (IsAIControlled() && !GetGM()) {
Message(Chat::Red, "You try to speak but cant move your mouth!");
LogDebug("Size mismatch in OP_ChannelMessage expected [{}] got [{}]", sizeof(ChannelMessage_Struct), app->size);
return;
}
uint8 skill_in_language = 100;
if (cm->language < MAX_PP_LANGUAGE)
{
skill_in_language = m_pp.languages[cm->language];
if (IsAIControlled() && !GetGM()) {
Message(Chat::Red, "You try to speak but can't move your mouth!");
return;
}
ChannelMessageReceived(cm->chan_num, cm->language, skill_in_language, cm->message, cm->targetname);
uint8 language_skill = Language::MaxValue;
if (EQ::ValueWithin(cm->language, Language::CommonTongue, Language::Unknown27)) {
language_skill = m_pp.languages[cm->language];
}
ChannelMessageReceived(cm->chan_num, cm->language, language_skill, cm->message, cm->targetname);
return;
}
@@ -4887,8 +4900,13 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
CheckIncreaseSkill(EQ::skills::SkillTracking, nullptr, -20);
}
/* Break Hide if moving without sneaking and set rewind timer if moved */
if (cy != m_Position.y || cx != m_Position.x) {
// End trader mode if we move
if (Trader) {
Trader_EndTrader();
}
/* Break Hide if moving without sneaking and set rewind timer if moved */
if ((hidden || improved_hidden) && !sneaking) {
hidden = false;
improved_hidden = false;
@@ -5053,7 +5071,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
}
if (zone->watermap) {
if (zone->watermap->InLiquid(glm::vec3(m_Position))) {
if (zone->watermap->InLiquid(glm::vec3(m_Position)) && IsMoving()) {
CheckIncreaseSkill(EQ::skills::SkillSwimming, nullptr, -17);
// Dismount horses when entering water
@@ -5770,7 +5788,7 @@ void Client::Handle_OP_DeleteSpell(const EQApplicationPacket *app)
if (m_pp.spell_book[dss->spell_slot] != SPELLBOOK_UNKNOWN) {
m_pp.spell_book[dss->spell_slot] = SPELLBOOK_UNKNOWN;
database.DeleteCharacterSpell(CharacterID(), m_pp.spell_book[dss->spell_slot], dss->spell_slot);
database.DeleteCharacterSpell(CharacterID(), dss->spell_slot);
dss->success = 1;
}
else
@@ -6233,6 +6251,13 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app)
}
if (ed->dmgtype == EQ::constants::EnvironmentalDamage::Falling) {
if (zone->HasWaterMap()) {
auto target_position = glm::vec3(GetX(), GetY(), GetZ());
if (!zone->watermap->InLiquid(target_position)) {
return;
}
}
uint32 mod = spellbonuses.ReduceFallDamage + itembonuses.ReduceFallDamage + aabonuses.ReduceFallDamage;
damage -= damage * mod / 100;
}
@@ -6481,7 +6506,7 @@ void Client::Handle_OP_GMBecomeNPC(const EQApplicationPacket *app)
t->SetGM(false);
}
m->SendAppearancePacket(AT_NPCName, 1, true);
m->SendAppearancePacket(AppearanceType::NPCName, 1, true);
t->SetBecomeNPC(true);
t->SetBecomeNPCLevel(b->maxlevel);
m->MessageString(Chat::White, TOGGLE_OFF);
@@ -6598,17 +6623,26 @@ void Client::Handle_OP_GMGoto(const EQApplicationPacket *app)
auto *gmg = (GMSummon_Struct *) app->pBuffer;
Mob *gt = entity_list.GetMob(gmg->charname);
if (!gt) {
MovePC(zone->GetZoneID(), zone->GetInstanceID(), gt->GetX(), gt->GetY(), gt->GetZ(), gt->GetHeading());
if (gt) {
MovePC(
zone->GetZoneID(),
zone->GetInstanceID(),
gt->GetX(),
gt->GetY(),
gt->GetZ(),
gt->GetHeading()
);
} else if (!worldserver.Connected()) {
Message(Chat::Red, "Error: World server disconnected.");
} else {
auto pack = new ServerPacket(ServerOP_GMGoto, sizeof(ServerGMGoto_Struct));
memset(pack->pBuffer, 0, pack->size);
ServerGMGoto_Struct *wsgmg = (ServerGMGoto_Struct *) pack->pBuffer;
strcpy(wsgmg->myname, GetName());
strcpy(wsgmg->gotoname, gmg->charname);
wsgmg->admin = admin;
auto* g = (ServerGMGoto_Struct *) pack->pBuffer;
strcpy(g->myname, GetName());
strcpy(g->gotoname, gmg->charname);
g->admin = admin;
worldserver.SendPacket(pack);
safe_delete(pack);
}
@@ -7303,64 +7337,81 @@ void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *app)
}
GroupInvite_Struct* gis = (GroupInvite_Struct*)app->pBuffer;
Mob* invitee = nullptr;
Mob *Invitee = entity_list.GetMob(gis->invitee_name);
if (RuleB(Character, GroupInvitesRequireTarget)) {
// We can only invite the current target.
invitee = GetTarget();
} else {
invitee = entity_list.GetMob(gis->invitee_name);
}
if (Invitee == this)
{
if (invitee == this) {
MessageString(Chat::LightGray, GROUP_INVITEE_SELF);
return;
}
if (Invitee)
{
if (Invitee->IsClient())
{
if (Invitee->CastToClient()->MercOnlyOrNoGroup() && !Invitee->IsRaidGrouped())
{
if (app->GetOpcode() == OP_GroupInvite2)
{
if (invitee) {
if (invitee->IsClient()) {
if (invitee->CastToClient()->MercOnlyOrNoGroup() && !invitee->IsRaidGrouped()) {
if (app->GetOpcode() == OP_GroupInvite2) {
//Make a new packet using all the same information but make sure it's a fixed GroupInvite opcode so we
//Don't have to deal with GroupFollow2 crap.
auto outapp =
new EQApplicationPacket(OP_GroupInvite, sizeof(GroupInvite_Struct));
memcpy(outapp->pBuffer, app->pBuffer, outapp->size);
Invitee->CastToClient()->QueuePacket(outapp);
invitee->CastToClient()->QueuePacket(outapp);
safe_delete(outapp);
return;
}
else
{
} else {
//The correct opcode, no reason to bother wasting time reconstructing the packet
Invitee->CastToClient()->QueuePacket(app);
invitee->CastToClient()->QueuePacket(app);
}
}
else {
} else if (invitee->IsRaidGrouped()) {
Raid* inviter_raid = GetRaid();
Raid* invitee_raid = invitee->CastToClient()->GetRaid();
bool leader = false;
if (invitee_raid) {
leader = invitee_raid->IsGroupLeader(invitee->GetName());
}
if (inviter_raid != invitee_raid || leader) {
MessageString(Chat::Default, ALREADY_IN_GRP_RAID, invitee->GetCleanName());
} else {
MessageString(Chat::Default, TARGET_ALREADY_IN_GROUP, invitee->GetCleanName());
}
return;
} else {
if (RuleB(Character, OnInviteReceiveAlreadyinGroupMessage)) {
if (!Invitee->CastToClient()->MercOnlyOrNoGroup()) {
Message(Chat::LightGray, "%s is already in another group.", Invitee->GetCleanName());
if (!invitee->CastToClient()->MercOnlyOrNoGroup()) {
MessageString(Chat::Default, TARGET_ALREADY_IN_GROUP, invitee->GetCleanName());
}
}
}
}
else if (Invitee->IsBot()) {
} else if (invitee->IsBot()) {
Client* inviter = entity_list.GetClientByName(gis->inviter_name);
if (inviter && inviter->IsRaidGrouped() && !Invitee->HasRaid()) {
Bot::ProcessRaidInvite(Invitee->CastToBot(), inviter, true);
}
else if (!Invitee->HasRaid()) {
Bot::ProcessBotGroupInvite(this, std::string(Invitee->GetName()));
if (inviter && inviter->IsRaidGrouped() && !invitee->HasRaid()) {
Bot::ProcessRaidInvite(invitee->CastToBot(), inviter, true);
} else if (!invitee->HasRaid()) {
Bot::ProcessBotGroupInvite(this, std::string(invitee->GetName()));
} else {
MessageString(Chat::LightGray, ALREADY_IN_RAID, Invitee->GetCleanName());
MessageString(Chat::LightGray, ALREADY_IN_RAID, invitee->GetCleanName());
}
}
}
else
{
auto pack = new ServerPacket(ServerOP_GroupInvite, sizeof(GroupInvite_Struct));
memcpy(pack->pBuffer, gis, sizeof(GroupInvite_Struct));
worldserver.SendPacket(pack);
safe_delete(pack);
} else {
if (RuleB(Character, GroupInvitesRequireTarget)) {
Message(Chat::White, "You must target a player first to invite to join your group.");
} else {
auto pack = new ServerPacket(ServerOP_GroupInvite, sizeof(GroupInvite_Struct));
memcpy(pack->pBuffer, gis, sizeof(GroupInvite_Struct));
worldserver.SendPacket(pack);
safe_delete(pack);
}
}
return;
}
@@ -8853,7 +8904,7 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app)
Message(Chat::LightGray, "You say, '%s'", response.c_str());
}
ChannelMessageReceived(ChatChannel_Say, 0, 100, response.c_str(), nullptr, true);
ChannelMessageReceived(ChatChannel_Say, Language::CommonTongue, Language::MaxValue, response.c_str(), nullptr, true);
return;
}
@@ -10716,7 +10767,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
}
if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) {
if (target != this && DistanceSquaredNoZ(mypet->GetPosition(), target->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) {
if (target != this && DistanceSquared(mypet->GetPosition(), target->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) {
mypet->SetFeigned(false);
if (mypet->IsPetStop()) {
mypet->SetPetStop(false);
@@ -10842,7 +10893,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
// Set Sit button to unpressed - send stand anim/end hpregen
mypet->SetFeigned(false);
SetPetCommandState(PET_BUTTON_SIT, 0);
mypet->SendAppearancePacket(AT_Anim, ANIM_STAND);
mypet->SendAppearancePacket(AppearanceType::Animation, Animation::Standing);
mypet->SayString(this, Chat::PetResponse, PET_GUARDINGLIFE);
mypet->SetPetOrder(SPO_Guard);
@@ -10867,7 +10918,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
// fix GUI sit button to be unpressed - send stand anim/end hpregen
SetPetCommandState(PET_BUTTON_SIT, 0);
mypet->SendAppearancePacket(AT_Anim, ANIM_STAND);
mypet->SendAppearancePacket(AppearanceType::Animation, Animation::Standing);
if (mypet->IsPetStop()) {
mypet->SetPetStop(false);
@@ -10915,7 +10966,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
// Set Sit button to unpressed - send stand anim/end hpregen
SetPetCommandState(PET_BUTTON_SIT, 0);
mypet->SendAppearancePacket(AT_Anim, ANIM_STAND);
mypet->SendAppearancePacket(AppearanceType::Animation, Animation::Standing);
if (mypet->IsPetStop()) {
mypet->SetPetStop(false);
@@ -10933,7 +10984,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
mypet->SetFeigned(false);
mypet->SayString(this, Chat::PetResponse, PET_SIT_STRING);
mypet->SetPetOrder(SPO_Follow);
mypet->SendAppearancePacket(AT_Anim, ANIM_STAND);
mypet->SendAppearancePacket(AppearanceType::Animation, Animation::Standing);
}
else
{
@@ -10943,7 +10994,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
mypet->SetRunAnimSpeed(0);
if (!mypet->UseBardSpellLogic()) //maybe we can have a bard pet
mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting
mypet->SendAppearancePacket(AT_Anim, ANIM_SIT);
mypet->SendAppearancePacket(AppearanceType::Animation, Animation::Sitting);
}
}
break;
@@ -10956,7 +11007,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
mypet->SayString(this, Chat::PetResponse, PET_SIT_STRING);
SetPetCommandState(PET_BUTTON_SIT, 0);
mypet->SetPetOrder(SPO_Follow);
mypet->SendAppearancePacket(AT_Anim, ANIM_STAND);
mypet->SendAppearancePacket(AppearanceType::Animation, Animation::Standing);
}
break;
}
@@ -10971,7 +11022,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
mypet->SetRunAnimSpeed(0);
if (!mypet->UseBardSpellLogic()) //maybe we can have a bard pet
mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting
mypet->SendAppearancePacket(AT_Anim, ANIM_SIT);
mypet->SendAppearancePacket(AppearanceType::Animation, Animation::Sitting);
}
break;
}
@@ -11176,7 +11227,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
mypet->SetPetOrder(SPO_FeignDeath);
mypet->SetRunAnimSpeed(0);
mypet->StopNavigation();
mypet->SendAppearancePacket(AT_Anim, ANIM_DEATH);
mypet->SendAppearancePacket(AppearanceType::Animation, Animation::Lying);
mypet->SetFeigned(true);
mypet->SetTarget(nullptr);
if (!mypet->UseBardSpellLogic()) {
@@ -11564,7 +11615,7 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app)
if (EntityVariableExists(DIAWIND_RESPONSE_ONE_KEY)) {
response = GetEntityVariable(DIAWIND_RESPONSE_ONE_KEY);
if (!response.empty()) {
ChannelMessageReceived(ChatChannel_Say, 0, 100, response.c_str(), nullptr, true);
ChannelMessageReceived(ChatChannel_Say, Language::CommonTongue, Language::MaxValue, response.c_str(), nullptr, true);
}
}
break;
@@ -11573,7 +11624,7 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app)
if (EntityVariableExists(DIAWIND_RESPONSE_TWO_KEY)) {
response = GetEntityVariable(DIAWIND_RESPONSE_TWO_KEY);
if (!response.empty()) {
ChannelMessageReceived(ChatChannel_Say, 0, 100, response.c_str(), nullptr, true);
ChannelMessageReceived(ChatChannel_Say, Language::CommonTongue, Language::MaxValue, response.c_str(), nullptr, true);
}
}
break;
@@ -11671,7 +11722,7 @@ void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app)
m_pp.raid_leadership_points -= cost;
m_pp.leader_abilities.ranks[aaid]++;
database.SaveCharacterLeadershipAA(CharacterID(), &m_pp);
database.SaveCharacterLeadershipAbilities(CharacterID(), &m_pp);
}
else {
//it is a group ability.
@@ -11684,7 +11735,7 @@ void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app)
m_pp.group_leadership_points -= cost;
m_pp.leader_abilities.ranks[aaid]++;
database.SaveCharacterLeadershipAA(CharacterID(), &m_pp);
database.SaveCharacterLeadershipAbilities(CharacterID(), &m_pp);
}
//success, send them an update
@@ -11808,8 +11859,8 @@ void Client::Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app)
}
buffer = StringFormat("%s,%i,%s.%s,%c%s",
Config->ChatHost.c_str(),
Config->ChatPort,
Config->GetUCSHost().c_str(),
Config->GetUCSPort(),
Config->ShortName.c_str(),
GetName(),
ConnectionType,
@@ -11834,8 +11885,8 @@ void Client::Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app)
}
buffer = StringFormat("%s,%i,%s.%s,%c%s",
Config->MailHost.c_str(),
Config->MailPort,
Config->GetUCSHost().c_str(),
Config->GetUCSPort(),
Config->ShortName.c_str(),
GetName(),
ConnectionType,
@@ -12044,6 +12095,13 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app)
{
auto player_sending_invite_group = player_sending_invite->GetGroup();
Group* group = GetGroup();
/* Prevent scenario where player had joined group with Raid invitor before accepting Raid invite */
if (group && group == player_sending_invite_group) {
player_sending_invite->MessageString(Chat::Red, INVITE_GROUP_LEADER);
return;
}
if (group) //if our target has a group
{
raid = new Raid(player_sending_invite);
@@ -14247,14 +14305,14 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
if (sa->spawn_id != GetID())
return;
if (sa->type == AT_Invis) {
if (sa->type == AppearanceType::Invisibility) {
if (sa->parameter != 0)
{
if (!HasSkill(EQ::skills::SkillHide) && GetSkill(EQ::skills::SkillHide) == 0)
{
if (ClientVersion() < EQ::versions::ClientVersion::SoF)
{
auto message = fmt::format("Player sent OP_SpawnAppearance with AT_Invis [{}]", sa->parameter);
auto message = fmt::format("Player sent OP_SpawnAppearance with AppearanceType::Invisibility [{}]", sa->parameter);
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
}
}
@@ -14266,18 +14324,18 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
entity_list.QueueClients(this, app, true);
return;
}
else if (sa->type == AT_Anim) {
else if (sa->type == AppearanceType::Animation) {
if (IsAIControlled())
return;
if (sa->parameter == ANIM_STAND) {
if (sa->parameter == Animation::Standing) {
SetAppearance(eaStanding);
playeraction = 0;
SetFeigned(false);
BindWound(this, false, true);
camp_timer.Disable();
}
else if (sa->parameter == ANIM_SIT) {
else if (sa->parameter == Animation::Sitting) {
SetAppearance(eaSitting);
playeraction = 1;
if (!UseBardSpellLogic())
@@ -14287,19 +14345,19 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
tmSitting = Timer::GetCurrentTime();
BuffFadeBySitModifier();
}
else if (sa->parameter == ANIM_CROUCH) {
else if (sa->parameter == Animation::Crouching) {
if (!UseBardSpellLogic())
InterruptSpell();
SetAppearance(eaCrouching);
playeraction = 2;
SetFeigned(false);
}
else if (sa->parameter == ANIM_DEATH) { // feign death too
else if (sa->parameter == Animation::Lying) { // feign death too
SetAppearance(eaDead);
playeraction = 3;
InterruptSpell();
}
else if (sa->parameter == ANIM_LOOT) {
else if (sa->parameter == Animation::Looting) {
SetAppearance(eaLooting);
playeraction = 4;
SetFeigned(false);
@@ -14312,7 +14370,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
entity_list.QueueClients(this, app, true);
}
else if (sa->type == AT_Anon) {
else if (sa->type == AppearanceType::Anonymous) {
if (!anon_toggle_timer.Check()) {
return;
}
@@ -14334,19 +14392,19 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
entity_list.QueueClients(this, app, true);
UpdateWho();
}
else if ((sa->type == AT_HP) && (dead == 0)) {
else if ((sa->type == AppearanceType::Health) && (dead == 0)) {
return;
}
else if (sa->type == AT_AFK) {
else if (sa->type == AppearanceType::AFK) {
if (afk_toggle_timer.Check()) {
AFK = (sa->parameter == 1);
entity_list.QueueClients(this, app, true);
}
}
else if (sa->type == AT_Split) {
else if (sa->type == AppearanceType::Split) {
m_pp.autosplit = (sa->parameter == 1);
}
else if (sa->type == AT_Sneak) {
else if (sa->type == AppearanceType::Sneak) {
if (sneaking == 0)
return;
@@ -14354,7 +14412,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
{
if (!HasSkill(EQ::skills::SkillSneak))
{
auto message = fmt::format("Player sent OP_SpawnAppearance with AT_Sneak [{}]", sa->parameter);
auto message = fmt::format("Player sent OP_SpawnAppearance with AppearanceType::Sneak [{}]", sa->parameter);
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
}
return;
@@ -14362,38 +14420,38 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
sneaking = 0;
entity_list.QueueClients(this, app, true);
}
else if (sa->type == AT_Size)
else if (sa->type == AppearanceType::Size)
{
auto message = fmt::format("Player sent OP_SpawnAppearance with AT_Size [{}]", sa->parameter);
auto message = fmt::format("Player sent OP_SpawnAppearance with AppearanceType::Size [{}]", sa->parameter);
RecordPlayerEventLog(PlayerEvent::POSSIBLE_HACK, PlayerEvent::PossibleHackEvent{.message = message});
}
else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield)
else if (sa->type == AppearanceType::Light) // client emitting light (lightstone, shiny shield)
{
//don't do anything with this
}
else if (sa->type == AT_Levitate)
else if (sa->type == AppearanceType::FlyMode)
{
// don't do anything with this, we tell the client when it's
// levitating, not the other way around
}
else if (sa->type == AT_ShowHelm)
else if (sa->type == AppearanceType::ShowHelm)
{
if (helm_toggle_timer.Check()) {
m_pp.showhelm = (sa->parameter == 1);
entity_list.QueueClients(this, app, true);
}
}
else if (sa->type == AT_GroupConsent)
else if (sa->type == AppearanceType::GroupAutoConsent)
{
m_pp.groupAutoconsent = (sa->parameter == 1);
ConsentCorpses("Group", (sa->parameter != 1));
}
else if (sa->type == AT_RaidConsent)
else if (sa->type == AppearanceType::RaidAutoConsent)
{
m_pp.raidAutoconsent = (sa->parameter == 1);
ConsentCorpses("Raid", (sa->parameter != 1));
}
else if (sa->type == AT_GuildConsent)
else if (sa->type == AppearanceType::GuildAutoConsent)
{
m_pp.guildAutoconsent = (sa->parameter == 1);
ConsentCorpses("Guild", (sa->parameter != 1));
@@ -14533,10 +14591,10 @@ void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app)
/* Save Spell Swaps */
if (!database.SaveCharacterSpell(CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot)) {
database.DeleteCharacterSpell(CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot);
database.DeleteCharacterSpell(CharacterID(), swapspell->from_slot);
}
if (!database.SaveCharacterSpell(CharacterID(), swapspelltemp, swapspell->to_slot)) {
database.DeleteCharacterSpell(CharacterID(), swapspelltemp, swapspell->to_slot);
database.DeleteCharacterSpell(CharacterID(), swapspell->to_slot);
}
QueuePacket(app);
@@ -15504,6 +15562,8 @@ void Client::Handle_OP_Translocate(const EQApplicationPacket *app)
return;
}
entity_list.ClearAggro(this);
////Was sending the packet back to initiate client zone...
////but that could be abusable, so lets go through proper channels
MovePC(
+26 -14
View File
@@ -572,7 +572,7 @@ bool Client::Process() {
linkdead_timer.Start(RuleI(Zone, ClientLinkdeadMS));
client_state = CLIENT_LINKDEAD;
AI_Start(CLIENT_LD_TIMEOUT);
SendAppearancePacket(AT_Linkdead, 1);
SendAppearancePacket(AppearanceType::Linkdead, 1);
SetDynamicZoneMemberStatus(DynamicZoneMemberStatus::LinkDead);
}
@@ -763,10 +763,12 @@ void Client::BulkSendInventoryItems()
}
}
bool deletenorent = database.NoRentExpired(GetName());
if (deletenorent) { //client was offline for more than 30 minutes, delete no rent items
if (RuleB(Inventory, TransformSummonedBags))
const bool delete_no_rent = database.NoRentExpired(GetName());
if (delete_no_rent) { //client was offline for more than 30 minutes, delete no rent items
if (RuleB(Inventory, TransformSummonedBags)) {
DisenchantSummonedBags(false);
}
RemoveNoRent(false);
}
@@ -1184,10 +1186,10 @@ void Client::CancelSneakHide()
if (hidden || improved_hidden) {
auto app = new EQApplicationPacket(OP_CancelSneakHide, 0);
FastQueuePacket(&app);
// SoF and Tit send back a OP_SpawnAppearance turning off AT_Invis
// SoF and Tit send back a OP_SpawnAppearance turning off AppearanceType::Invisibility
// so we need to handle our sneaking flag only
// The later clients send back a OP_Hide (this has a size but data is 0)
// as well as OP_SpawnAppearance with AT_Invis and one with AT_Sneak
// as well as OP_SpawnAppearance with AppearanceType::Invisibility and one with AppearanceType::Sneak
// So we don't have to handle any of those flags
if (ClientVersionBit() & EQ::versions::maskSoFAndEarlier)
sneaking = false;
@@ -1552,19 +1554,24 @@ void Client::OPGMTraining(const EQApplicationPacket *app)
Mob* pTrainer = entity_list.GetMob(gmtrain->npcid);
if(!pTrainer || !pTrainer->IsNPC() || pTrainer->GetClass() < Class::WarriorGM || pTrainer->GetClass() > Class::BerserkerGM)
if (!pTrainer || !pTrainer->IsNPC() || pTrainer->GetClass() < Class::WarriorGM || pTrainer->GetClass() > Class::BerserkerGM) {
return;
}
//you can only use your own trainer, client enforces this, but why trust it
if (!RuleB(Character, AllowCrossClassTrainers)) {
int trains_class = pTrainer->GetClass() - (Class::WarriorGM - Class::Warrior);
if (GetClass() != trains_class)
if (GetClass() != trains_class) {
safe_delete(outapp);
return;
}
}
//you have to be somewhat close to a trainer to be properly using them
if(DistanceSquared(m_Position,pTrainer->GetPosition()) > USE_NPC_RANGE2)
if (DistanceSquared(m_Position,pTrainer->GetPosition()) > USE_NPC_RANGE2) {
safe_delete(outapp);
return;
}
// if this for-loop acts up again (crashes linux), try enabling the before and after #pragmas
//#pragma GCC push_options
@@ -1657,7 +1664,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app)
// languages go here
if (gmskill->skill_id > 25)
{
std::cout << "Wrong Training Skill (languages)" << std::endl;
LogSkills("Wrong Training Skill (languages)");
DumpPacket(app);
return;
}
@@ -1672,7 +1679,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app)
// normal skills go here
if (gmskill->skill_id > EQ::skills::HIGHEST_SKILL)
{
std::cout << "Wrong Training Skill (abilities)" << std::endl;
LogSkills("Wrong Training Skill (abilities)");
DumpPacket(app);
return;
}
@@ -1691,11 +1698,12 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app)
uint16 skilllevel = GetRawSkill(skill);
if(skilllevel == 0) {
if (skilllevel == 0) {
//this is a new skill..
uint16 t_level = SkillTrainLevel(skill, GetClass());
if (t_level == 0)
{
if (t_level == 0) {
LogSkills("Tried to train a new skill [{}] which is invalid for this race/class.", skill);
return;
}
@@ -1715,6 +1723,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app)
case EQ::skills::SkillPottery:
if(skilllevel >= RuleI(Skills, MaxTrainTradeskills)) {
MessageString(Chat::Red, MORE_SKILLED_THAN_I, pTrainer->GetCleanName());
SetSkill(skill, skilllevel);
return;
}
break;
@@ -1725,6 +1734,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app)
case EQ::skills::SkillSpecializeEvocation:
if(skilllevel >= RuleI(Skills, MaxTrainSpecializations)) {
MessageString(Chat::Red, MORE_SKILLED_THAN_I, pTrainer->GetCleanName());
SetSkill(skill, skilllevel);
return;
}
default:
@@ -1736,6 +1746,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app)
{
// Don't allow training over max skill level
MessageString(Chat::Red, MORE_SKILLED_THAN_I, pTrainer->GetCleanName());
SetSkill(skill, skilllevel);
return;
}
@@ -1746,6 +1757,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app)
{
// Restrict specialization training to follow the rules
MessageString(Chat::Red, MORE_SKILLED_THAN_I, pTrainer->GetCleanName());
SetSkill(skill, skilllevel);
return;
}
}
+2 -2
View File
@@ -100,6 +100,7 @@ int command_init(void)
command_add("camerashake", "[Duration (Milliseconds)] [Intensity (1-10)] - Shakes the camera on everyone's screen globally.", AccountStatus::QuestTroupe, command_camerashake) ||
command_add("castspell", "[Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)] - Cast a spell", AccountStatus::Guide, command_castspell) ||
command_add("chat", "[Channel ID] [Message] - Send a channel message to all zones", AccountStatus::GMMgmt, command_chat) ||
command_add("clearxtargets", "Clears XTargets", AccountStatus::Player, command_clearxtargets) ||
command_add("copycharacter", "[source_char_name] [dest_char_name] [dest_account_name] - Copies character to destination account", AccountStatus::GMImpossible, command_copycharacter) ||
command_add("corpse", "Manipulate corpses, use with no arguments for help", AccountStatus::Guide, command_corpse) ||
command_add("corpsefix", "Attempts to bring corpses from underneath the ground within close proximity of the player", AccountStatus::Player, command_corpsefix) ||
@@ -238,7 +239,6 @@ int command_init(void)
command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", AccountStatus::GMLeadAdmin, command_zonebootup) ||
command_add("zoneinstance", "[Instance ID] [X] [Y] [Z] - Teleport to specified Instance by ID (coordinates are optional)", AccountStatus::Guide, command_zone_instance) ||
command_add("zoneshutdown", "[shortname] - Shut down a zone server", AccountStatus::GMLeadAdmin, command_zoneshutdown) ||
command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", AccountStatus::GMImpossible, command_zopp) ||
command_add("zsave", " Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave)
) {
command_deinit();
@@ -792,6 +792,7 @@ void command_bot(Client *c, const Seperator *sep)
#include "gm_commands/camerashake.cpp"
#include "gm_commands/castspell.cpp"
#include "gm_commands/chat.cpp"
#include "gm_commands/clearxtargets.cpp"
#include "gm_commands/copycharacter.cpp"
#include "gm_commands/corpse.cpp"
#include "gm_commands/corpsefix.cpp"
@@ -925,5 +926,4 @@ void command_bot(Client *c, const Seperator *sep)
#include "gm_commands/zonebootup.cpp"
#include "gm_commands/zoneshutdown.cpp"
#include "gm_commands/zone_instance.cpp"
#include "gm_commands/zopp.cpp"
#include "gm_commands/zsave.cpp"
+3
View File
@@ -35,6 +35,8 @@ void SendNPCEditSubCommands(Client *c);
void SendRuleSubCommands(Client *c);
void SendGuildSubCommands(Client *c);
void SendShowInventorySubCommands(Client *c);
void SendFixMobSubCommands(Client *c);
void SendDataBucketsSubCommands(Client *c);
// Commands
void command_acceptrules(Client *c, const Seperator *sep);
@@ -51,6 +53,7 @@ void command_bugs(Client *c, const Seperator *sep);
void command_camerashake(Client *c, const Seperator *sep);
void command_castspell(Client *c, const Seperator *sep);
void command_chat(Client *c, const Seperator *sep);
void command_clearxtargets(Client *c, const Seperator *sep);
void command_copycharacter(Client *c, const Seperator *sep);
void command_corpse(Client *c, const Seperator *sep);
void command_corpsefix(Client *c, const Seperator *sep);
+1 -1
View File
@@ -927,7 +927,7 @@ void Corpse::RemoveItem(ServerLootItem_Struct* item_data)
UpdateEquipmentLight();
if (UpdateActiveLight())
SendAppearancePacket(AT_Light, GetActiveLightType());
SendAppearancePacket(AppearanceType::Light, GetActiveLightType());
safe_delete(sitem);
return;
+45 -12
View File
@@ -60,15 +60,7 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
bool Critical = false;
int64 base_value = value;
int chance = 0;
// Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
if ((spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40)
value -= (GetLevel() - 40) * 20;
//This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch.
if (spell_id == SPELL_IMP_HARM_TOUCH && IsOfClientBot()) { //Improved Harm Touch
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
}
int legacy_manaburn_cap = RuleI(Spells, LegacyManaburnCap);
chance = RuleI(Spells, BaseCritChance); //Wizard base critical chance is 2% (Does not scale with level)
chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
@@ -138,12 +130,30 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value) * ratio / 100;
}
// legacy manaburn can crit, but is still held to the same cap
if (RuleB(Spells, LegacyManaburn) && spell_id == SPELL_MANA_BURN) {
if (value < -legacy_manaburn_cap) {
value = -legacy_manaburn_cap;
}
}
entity_list.FilteredMessageCloseString(
this, true, 100, Chat::SpellCrit, FilterSpellCrits,
OTHER_CRIT_BLAST, nullptr, GetName(), itoa(-value));
if (IsClient())
if (IsClient()) {
MessageString(Chat::SpellCrit, YOU_CRIT_BLAST, itoa(-value));
}
// Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
if ((spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH) && GetLevel() > 40) {
value -= (GetLevel() - 40) * 20;
}
//This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm Touch.
if (spell_id == SPELL_IMP_HARM_TOUCH && IsOfClientBot()) { //Improved Harm Touch
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
}
return value;
}
@@ -183,6 +193,13 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
value -= GetExtraSpellAmt(spell_id, GetSpellDmg(), base_value);
}
// Apply Manaburn Damage Cap
if (RuleB(Spells, LegacyManaburn) && spell_id == SPELL_MANA_BURN) {
if (value < -legacy_manaburn_cap) {
value = -legacy_manaburn_cap;
}
}
return value;
}
@@ -592,6 +609,22 @@ int32 Mob::GetActSpellCost(uint16 spell_id, int32 cost)
int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration)
{
// focuses don't affect discipline duration (Except War Cries)
if (
IsDiscipline(spell_id) &&
(
spell_id != SPELL_BATTLE_CRY &&
spell_id != SPELL_WAR_CRY &&
spell_id != SPELL_BATTLE_CRY_OF_DRAVEL &&
spell_id != SPELL_WAR_CRY_OF_DRAVEL &&
spell_id != SPELL_BATTLE_CRY_OF_THE_MASTRUQ &&
spell_id != SPELL_ANCIENT_CRY_OF_CHAOS &&
spell_id != SPELL_BLOODTHIRST
)
) {
return duration;
}
int increase = 100;
increase += GetFocusEffect(focusSpellDuration, spell_id);
int tic_inc = 0;
@@ -690,7 +723,7 @@ bool Client::TrainDiscipline(uint32 itemid) {
return false;
} else if (m_pp.disciplines.values[r] == 0) {
m_pp.disciplines.values[r] = spell_id;
database.SaveCharacterDisc(CharacterID(), r, spell_id);
database.SaveCharacterDiscipline(CharacterID(), r, spell_id);
SendDisciplineUpdate();
Message(Chat::White, "You have learned a new discipline!");
return true;
@@ -789,7 +822,7 @@ void Client::TrainDiscBySpellID(int32 spell_id)
for(i = 0; i < MAX_PP_DISCIPLINES; i++) {
if(m_pp.disciplines.values[i] == 0) {
m_pp.disciplines.values[i] = spell_id;
database.SaveCharacterDisc(CharacterID(), i, spell_id);
database.SaveCharacterDiscipline(CharacterID(), i, spell_id);
SendDisciplineUpdate();
Message(Chat::Yellow, "You have learned a new combat ability!");
return;
+1 -1
View File
@@ -1891,6 +1891,7 @@ void PerlembParser::ExportEventVariables(
ExportVar(package_name.c_str(), "killer_damage", sep.arg[1]);
ExportVar(package_name.c_str(), "killer_spell", sep.arg[2]);
ExportVar(package_name.c_str(), "killer_skill", sep.arg[3]);
ExportVar(package_name.c_str(), "killed_entity_id", sep.arg[4]);
if (extra_pointers && extra_pointers->size() >= 1) {
Corpse *corpse = std::any_cast<Corpse *>(extra_pointers->at(0));
@@ -1902,7 +1903,6 @@ void PerlembParser::ExportEventVariables(
if (extra_pointers && extra_pointers->size() >= 2) {
NPC *killed = std::any_cast<NPC *>(extra_pointers->at(1));
if (killed) {
ExportVar(package_name.c_str(), "killed_entity_id", killed->GetID());
ExportVar(package_name.c_str(), "killed_bot_id", killed->IsBot() ? killed->CastToBot()->GetBotID() : 0);
ExportVar(package_name.c_str(), "killed_npc_id", killed->IsNPC() ? killed->GetNPCTypeID() : 0);
ExportVar(package_name.c_str(), "killed_x", killed->GetX());
+12 -12
View File
@@ -93,13 +93,13 @@ void Perl__say(const char* message)
// we currently default to these
opts.speak_mode = Journal::SpeakMode::Say;
opts.journal_mode = Journal::Mode::Log2;
opts.language = 0;
opts.language = Language::CommonTongue;
opts.message_type = Chat::NPCQuestSay;
quest_manager.say(message, opts);
}
void Perl__say(const char* message, int language_id)
void Perl__say(const char* message, uint8 language_id)
{
Journal::Options opts;
opts.speak_mode = Journal::SpeakMode::Say;
@@ -110,7 +110,7 @@ void Perl__say(const char* message, int language_id)
quest_manager.say(message, opts);
}
void Perl__say(const char* message, int language_id, int message_type)
void Perl__say(const char* message, uint8 language_id, int message_type)
{
Journal::Options opts;
opts.speak_mode = Journal::SpeakMode::Say;
@@ -121,7 +121,7 @@ void Perl__say(const char* message, int language_id, int message_type)
quest_manager.say(message, opts);
}
void Perl__say(const char* message, int language_id, int message_type, int speak_mode)
void Perl__say(const char* message, uint8 language_id, int message_type, int speak_mode)
{
Journal::Options opts;
opts.speak_mode = static_cast<Journal::SpeakMode>(speak_mode);
@@ -132,7 +132,7 @@ void Perl__say(const char* message, int language_id, int message_type, int speak
quest_manager.say(message, opts);
}
void Perl__say(const char* message, int language_id, int message_type, int speak_mode, int journal_mode)
void Perl__say(const char* message, uint8 language_id, int message_type, int speak_mode, int journal_mode)
{
Journal::Options opts;
opts.speak_mode = static_cast<Journal::SpeakMode>(speak_mode);
@@ -674,9 +674,9 @@ void Perl__addskill(int skill_id, int value)
quest_manager.addskill(skill_id, value);
}
void Perl__setlanguage(int skill_id, int value)
void Perl__setlanguage(uint8 language_id, uint8 language_skill)
{
quest_manager.setlanguage(skill_id, value);
quest_manager.setlanguage(language_id, language_skill);
}
void Perl__setskill(int skill_id, int value)
@@ -4713,7 +4713,7 @@ std::string Perl__getfactionname(int faction_id)
return quest_manager.getfactionname(faction_id);
}
std::string Perl__getlanguagename(int language_id)
std::string Perl__getlanguagename(uint8 language_id)
{
return quest_manager.getlanguagename(language_id);
}
@@ -6532,10 +6532,10 @@ void perl_register_quest()
package.add("safemove", &Perl__safemove);
package.add("save", &Perl__save);
package.add("say", (void(*)(const char*))&Perl__say);
package.add("say", (void(*)(const char*, int))&Perl__say);
package.add("say", (void(*)(const char*, int, int))&Perl__say);
package.add("say", (void(*)(const char*, int, int, int))&Perl__say);
package.add("say", (void(*)(const char*, int, int, int, int))&Perl__say);
package.add("say", (void(*)(const char*, uint8))&Perl__say);
package.add("say", (void(*)(const char*, uint8, int))&Perl__say);
package.add("say", (void(*)(const char*, uint8, int, int))&Perl__say);
package.add("say", (void(*)(const char*, uint8, int, int, int))&Perl__say);
package.add("saylink", (std::string(*)(const char*))&Perl__saylink);
package.add("saylink", (std::string(*)(const char*, bool))&Perl__saylink);
package.add("saylink", (std::string(*)(const char*, bool, const char*))&Perl__saylink);
+3 -3
View File
@@ -37,9 +37,9 @@ Encounter::Encounter(const char *enc_name) : Mob(
nullptr, // in_lastname
0, // in_cur_hp
0, // in_max_hp
MALE, // in_gender
INVISIBLE_MAN, // in_race
0, // in_class
Gender::Male, // in_gender
Race::InvisibleMan, // in_race
Class::None, // in_class
BT_NoTarget, // in_bodytype
0, // in_deity
0, // in_level
+45 -21
View File
@@ -1282,7 +1282,7 @@ uint16 EntityList::GetFreeID()
// if no language skill is specified, sent with 100 skill
void EntityList::ChannelMessage(Mob *from, uint8 chan_num, uint8 language, const char *message, ...)
{
ChannelMessage(from, chan_num, language, 100, message);
ChannelMessage(from, chan_num, language, Language::MaxValue, message);
}
void EntityList::ChannelMessage(Mob *from, uint8 chan_num, uint8 language,
@@ -1411,7 +1411,7 @@ void EntityList::SendZonePVPUpdates(Client *to)
while (it != client_list.end()) {
Client *c = it->second;
if(c->GetPVP())
c->SendAppearancePacket(AT_PVP, c->GetPVP(), true, false, to);
c->SendAppearancePacket(AppearanceType::PVP, c->GetPVP(), true, false, to);
++it;
}
}
@@ -1491,18 +1491,21 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets)
Mob *m = it->second;
++it;
if (!m)
if (!m) {
continue;
}
if (RemoveFromXTargets && mob) {
if (m->IsClient() && (mob->CheckAggro(m) || mob->IsOnFeignMemory(m)))
if (m->IsClient() && (mob->CheckAggro(m) || mob->IsOnFeignMemory(m))) {
m->CastToClient()->RemoveXTarget(mob, false);
// FadingMemories calls this function passing the client.
else if (mob->IsClient() && (m->CheckAggro(mob) || m->IsOnFeignMemory(mob)))
} else if (mob->IsClient() && (m->CheckAggro(mob) || m->IsOnFeignMemory(mob))) {
mob->CastToClient()->RemoveXTarget(m, false);
}
}
m->RemoveFromHateList(mob);
m->RemoveFromRampageList(mob);
}
}
@@ -1515,20 +1518,24 @@ void EntityList::RemoveFromTargetsFadingMemories(Mob *spell_target, bool RemoveF
continue;
}
if (max_level && mob->GetLevel() > max_level)
if (max_level && mob->GetLevel() > max_level) {
continue;
}
if (mob->GetSpecialAbility(IMMUNE_FADING_MEMORIES))
if (mob->GetSpecialAbility(IMMUNE_FADING_MEMORIES)) {
continue;
}
if (RemoveFromXTargets && spell_target) {
if (mob->IsClient() && (spell_target->CheckAggro(mob) || spell_target->IsOnFeignMemory(mob)))
if (mob->IsClient() && (spell_target->CheckAggro(mob) || spell_target->IsOnFeignMemory(mob))) {
mob->CastToClient()->RemoveXTarget(spell_target, false);
else if (spell_target->IsClient() && (mob->CheckAggro(spell_target) || mob->IsOnFeignMemory(spell_target)))
} else if (spell_target->IsClient() && (mob->CheckAggro(spell_target) || mob->IsOnFeignMemory(spell_target))) {
spell_target->CastToClient()->RemoveXTarget(mob, false);
}
}
mob->RemoveFromHateList(spell_target);
mob->RemoveFromRampageList(spell_target);
}
}
@@ -1814,14 +1821,14 @@ void EntityList::DuelMessage(Mob *winner, Mob *loser, bool flee)
}
}
Client *EntityList::GetClientByName(const char *checkname)
Client *EntityList::GetClientByName(const char* name)
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (strcasecmp(it->second->GetName(), checkname) == 0)
return it->second;
++it;
for (const auto& e : client_list) {
if (e.second && Strings::EqualFold(e.second->GetName(), name)) {
return e.second;
}
}
return nullptr;
}
@@ -3188,8 +3195,10 @@ void EntityList::RemoveFromHateLists(Mob *mob, bool settoone)
if (it->second->CheckAggro(mob)) {
if (!settoone) {
it->second->RemoveFromHateList(mob);
if (mob->IsClient())
it->second->RemoveFromRampageList(mob);
if (mob->IsClient()) {
mob->CastToClient()->RemoveXTarget(it->second, false); // gotta do book keeping
}
} else {
it->second->SetHateAmountOnEnt(mob, 1);
}
@@ -3536,20 +3545,29 @@ void EntityList::HalveAggro(Mob *who)
}
//removes "targ" from all hate lists, including feigned, in the zone
void EntityList::ClearAggro(Mob* targ)
void EntityList::ClearAggro(Mob* targ, bool clear_caster_id)
{
Client *c = nullptr;
if (targ->IsClient()) {
c = targ->CastToClient();
}
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (clear_caster_id) {
it->second->BuffDetachCaster(targ);
}
if (it->second->CheckAggro(targ)) {
if (c) {
c->RemoveXTarget(it->second, false);
}
it->second->RemoveFromHateList(targ);
it->second->RemoveFromRampageList(targ, true);
}
if (c && it->second->IsOnFeignMemory(c)) {
it->second->RemoveFromFeignMemory(c); //just in case we feigned
c->RemoveXTarget(it->second, false);
@@ -3573,6 +3591,7 @@ void EntityList::ClearWaterAggro(Mob* targ)
c->RemoveXTarget(it->second, false);
}
it->second->RemoveFromHateList(targ);
it->second->RemoveFromRampageList(targ);
}
if (c && it->second->IsOnFeignMemory(c)) {
it->second->RemoveFromFeignMemory(c); //just in case we feigned
@@ -3618,6 +3637,11 @@ void EntityList::ClearFeignAggro(Mob *targ)
}
it->second->RemoveFromHateList(targ);
if (it->second->GetSpecialAbility(SPECATK_RAMPAGE)) {
it->second->RemoveFromRampageList(targ, true);
}
if (targ->IsClient()) {
if (it->second->GetLevel() >= 35 && zone->random.Roll(60)) {
it->second->AddFeignMemory(targ);
@@ -4588,7 +4612,7 @@ void EntityList::GroupMessage(uint32 gid, const char *from, const char *message)
g = it->second->GetGroup();
if (g) {
if (g->GetID() == gid)
it->second->ChannelMessageSend(from, it->second->GetName(), ChatChannel_Group, 0, 100, message);
it->second->ChannelMessageSend(from, it->second->GetName(), ChatChannel_Group, Language::CommonTongue, Language::MaxValue, message);
}
}
++it;
@@ -4704,10 +4728,10 @@ void EntityList::SendZoneAppearance(Client *c)
continue;
}
if (cur->GetAppearance() != eaStanding) {
cur->SendAppearancePacket(AT_Anim, cur->GetAppearanceValue(cur->GetAppearance()), false, true, c);
cur->SendAppearancePacket(AppearanceType::Animation, cur->GetAppearanceValue(cur->GetAppearance()), false, true, c);
}
if (cur->GetSize() != cur->GetBaseSize()) {
cur->SendAppearancePacket(AT_Size, (uint32) cur->GetSize(), false, true, c);
cur->SendAppearancePacket(AppearanceType::Size, (uint32) cur->GetSize(), false, true, c);
}
}
++it;
@@ -4781,7 +4805,7 @@ void EntityList::SendAppearanceEffects(Client *c)
++it;
continue;
}
cur->SendSavedAppearenceEffects(c);
cur->SendSavedAppearanceEffects(c);
}
++it;
}
+2 -2
View File
@@ -171,7 +171,7 @@ public:
return it->second;
return nullptr;
}
Client *GetClientByName(const char *name);
Client *GetClientByName(const char* name);
Client *GetClientByAccID(uint32 accid);
inline Client *GetClientByID(uint16 id)
{
@@ -463,7 +463,7 @@ public:
void UpdateHoTT(Mob* target);
void Process();
void ClearAggro(Mob* targ);
void ClearAggro(Mob* targ, bool clear_caster_id = false);
void ClearWaterAggro(Mob* targ);
void ClearFeignAggro(Mob* targ);
void ClearZoneFeignAggro(Mob* targ);
-60
View File
@@ -1,60 +0,0 @@
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA */
/* Error messages for mysql clients */
/* error messages for the demon is in share/language/errmsg.sys */
#ifdef __cplusplus
extern "C" {
#endif
void init_client_errs(void);
extern const char *client_errors[]; /* Error messages */
#ifdef __cplusplus
}
#endif
#define CR_MIN_ERROR 2000 /* For easier client code */
#define CR_MAX_ERROR 2999
#if defined(OS2) && defined( MYSQL_SERVER)
#define CER(X) client_errors[(X)-CR_MIN_ERROR]
#else
#define ER(X) client_errors[(X)-CR_MIN_ERROR]
#endif
#define CLIENT_ERRMAP 2 /* Errormap used by my_error() */
#define CR_UNKNOWN_ERROR 2000
#define CR_SOCKET_CREATE_ERROR 2001
#define CR_CONNECTION_ERROR 2002
#define CR_CONN_HOST_ERROR 2003
#define CR_IPSOCK_ERROR 2004
#define CR_UNKNOWN_HOST 2005
#define CR_SERVER_GONE_ERROR 2006
#define CR_VERSION_ERROR 2007
#define CR_OUT_OF_MEMORY 2008
#define CR_WRONG_HOST_INFO 2009
#define CR_LOCALHOST_CONNECTION 2010
#define CR_TCP_CONNECTION 2011
#define CR_SERVER_HANDSHAKE_ERR 2012
#define CR_SERVER_LOST 2013
#define CR_COMMANDS_OUT_OF_SYNC 2014
#define CR_NAMEDPIPE_CONNECTION 2015
#define CR_NAMEDPIPEWAIT_ERROR 2016
#define CR_NAMEDPIPEOPEN_ERROR 2017
#define CR_NAMEDPIPESETSTATE_ERROR 2018
#define CR_CANT_READ_CHARSET 2019
#define CR_NET_PACKET_TOO_LARGE 2020
+1 -1
View File
@@ -950,7 +950,7 @@ void Client::SetLevel(uint8 set_level, bool command)
QueuePacket(outapp);
safe_delete(outapp);
SendAppearancePacket(AT_WhoLevel, set_level); // who level change
SendAppearancePacket(AppearanceType::WhoLevel, set_level); // who level change
LogInfo("Setting Level for [{}] to [{}]", GetName(), set_level);
+1 -1
View File
@@ -1171,7 +1171,7 @@ bool Expedition::HasLockoutByCharacterID(
{
auto lockouts = Expedition::GetExpeditionLockoutsByCharacterID(character_id);
return std::any_of(lockouts.begin(), lockouts.end(), [&](const ExpeditionLockoutTimer& lockout) {
return lockout.IsSameLockout(expedition_name, event_name);
return !lockout.IsExpired() && lockout.IsSameLockout(expedition_name, event_name);
});
}
+1 -1
View File
@@ -8,6 +8,6 @@ void command_acceptrules(Client *c, const Seperator *sep)
}
database.SetAgreementFlag(c->AccountID());
c->SendAppearancePacket(AT_Anim, ANIM_STAND);
c->SendAppearancePacket(AppearanceType::Animation, Animation::Standing);
c->Message(Chat::White, "It is recorded you have agreed to the rules.");
}
+37 -19
View File
@@ -2,25 +2,43 @@
void command_appearance(Client *c, const Seperator *sep)
{
Mob *t = c->CastToMob();
const int arguments = sep->argnum;
if (!arguments || !sep->IsNumber(1) || !sep->IsNumber(2)) {
c->Message(Chat::White, "Usage: #appearance [Type] [Value]");
c->Message(Chat::White, "Note: Types are as follows:");
// sends any appearance packet
// Dev debug command, for appearance types
if (sep->arg[2][0] == 0) {
c->Message(Chat::White, "Usage: #appearance type value");
}
else {
if ((c->GetTarget())) {
t = c->GetTarget();
for (const auto& a : EQ::constants::GetAppearanceTypeMap()) {
c->Message(
Chat::White,
fmt::format(
"Appearance Type {} | {}",
a.first,
a.second
).c_str()
);
}
t->SendAppearancePacket(Strings::ToInt(sep->arg[1]), Strings::ToInt(sep->arg[2]));
c->Message(
Chat::White,
"Sending appearance packet: target=%s, type=%s, value=%s",
t->GetName(),
sep->arg[1],
sep->arg[2]
);
}
}
return;
}
Mob *t = c;
if (c->GetTarget()) {
t = c->GetTarget();
}
const uint32 type = Strings::ToUnsignedInt(sep->arg[1]);
const uint32 value = Strings::ToUnsignedInt(sep->arg[2]);
t->SendAppearancePacket(type, value);
c->Message(
Chat::White,
fmt::format(
"Appearance Sent to {} | Type: {} ({}) Value: {}",
c->GetTargetDescription(t, TargetDescriptionType::UCSelf),
EQ::constants::GetAppearanceTypeName(type),
type,
value
).c_str()
);
}
+1 -1
View File
@@ -86,7 +86,7 @@ void command_appearanceeffects(Client *c, const Seperator *sep)
.texture = t->GetTexture(),
}
);
t->ClearAppearenceEffects();
t->ClearAppearanceEffects();
c->Message(
Chat::White,
fmt::format(

Some files were not shown because too many files have changed in this diff Show More