Compare commits

..

65 Commits

Author SHA1 Message Date
Chris Miles 703862d977 [Release] 22.37.0 (#3789) 2023-12-18 15:43:09 -06:00
nytmyr 6e325c1ee3 [Bots] Fix unnecessary failed to save timer error (#3788) 2023-12-18 15:39:14 -06:00
nytmyr 933b83add6 [Bots] Fix ^defensive from checking aggressive disciplines. (#3787)
Bots were checking for aggressive disciplines with both ^aggressive and ^defensive.
2023-12-18 15:38:37 -06:00
Alex King b3cd4e63f1 [Bug Fix] Drop Invisibility when hit by traps (#3785)
# Notes
- Resolves https://github.com/EQEmu/Server/issues/663.

# Video
[Drop Invisibility Trap Test](https://github.com/EQEmu/Server/assets/89047260/d6e96704-ee6d-4b16-bd52-4122e3b37577)
2023-12-18 15:37:53 -06:00
nytmyr 3c894cb533 [Bots] Add ScanCloseMobs support to fix AEs (#3786)
Previously bots were only scanning for nearby clients so any AE spells or procs didn't have mobs to hit.

This changes that to scan for mobs instead of clients so their close entity list supports AEs with mobs to hit.
2023-12-18 15:37:07 -06:00
Alex King b19ad64800 [Bug Fix] Send Entity ID in Death Events to resolve #3721 (#3779)
* [Bug Fix] Send Entity ID in Death Events to resolve #3721

# Notes
- Due to some pointers becoming invalid you may get an invalid entity ID on the killed mob if we don't just directly send the entity ID in the export string.

* Update attack.cpp

* Remove GetID() export.
2023-12-17 19:43:41 -06:00
Alex King 2cd3d27c67 [Quest API] Add GetNPCAggro() and SetNPCAggro() to Perl/Lua (#3781)
* [Quest API] Add GetNPCAggro() and SetNPCAggro() to Perl/Lua

# Perl
- Add `$npc->GetNPCAggro()`.
- Add `$npc->SetNPCAggro(in_npc_aggro)`.

# Lua
- Add `npc:GetNPCAggro()`.
- Add `npc:SetNPCAggro(in_npc_aggro)`.

# Notes
- Allows operators to enable or disable an NPC's NPC aggro capability dynamically.

* Update api_service.cpp
2023-12-17 20:34:06 -05:00
Alex King d3b46becd0 [Bug Fix] Fix NPCs routing to 0.0, 0.0 on #summon (#3780)
# Notes
- Resolves #2474.
2023-12-17 20:24:24 -05:00
Chris Miles 286479198f [Compilation] Use pre-compiled headers for Windows (speed) (#3778)
* Experiment with PCH

* Another run

* GCC test

* Different test

* Another one

* Another one

* Lua headers

* PCH main zone primitives

* Tweaks

* Tweaks

* Tweaks

* Add EQEMU_BUILD_PCH option default to ON
2023-12-17 19:04:21 -06:00
Chris Miles 21ec832ca6 [CI] Switch to use clang for Linux builds (speed) (#3777) 2023-12-17 15:17:29 -06:00
nytmyr bdf5f8b4a3 [Bots] [Quest API] Add ^clickitem, ^timer, fix GetBestBotSpellForCure (#3755)
* [Bots][Quest API] Add ^clickitem, ^timer, revamp bot timers, fix GetBestBotSpellForCure

This adds the command **^clickitem** for bots.
Bots can click items they are wearing with the provided slot ID, players can use **^invlist** on their bots to see items and slot IDs.
This supports actionables.
**^itemclick 13 byclass 11** would command all Necromancer bots to attempt to click their Primary item.

This adds and supports charges for items to bots, when an item is used, it will lose a charge and cannot be clicked once no charges remain.

This adds the following rules:
**Bots, BotsClickItemsMinLvl** - Minimum level bots can use **^clickitem**.
**Bots, BotsCanClickItems** - Whether or not **^clickitem** is allowed for bots.
**Bots, CanClickMageEpicV1** - Whether or not players are allowed to command their bots to use the Magician Epic 1.0

This adds quest methods to Perl/Lua for:
ClearDisciplineReuseTimer, ClearItemReuseTimer, ClearSpellRecastTimer
GetDisciplineReuseTimer, GetItemReuseTimer, GetSpellRecastTimer
SetDisciplineReuseTimer, SetItemReuseTimer, SetSpellRecastTimer

Discipline and Spell methods use the spell_id to check, get and set. Item uses the item_id.
Clear and Get support wildcards (no spell/item id) to clear all timers of the type or get the first timer of the type.
Get will return the remaining time on the chosen timer, if any.
Set supports a wildcard (no recast/reuse provided) to use the default of the provided type, you can also specify a recast/reuse timer to set that timer to the chosen value.

**^timer** has been added as a bot command, defaulted for GM access.
This can be used to set, get and clear timers of different types. Use **^timer help** for info.

This revamps the way timers are set, stored, loaded for bots.

**GetBestBotSpellForCure** was previously checking only the first spell found and not properly iterating through the checks.

This requires modifications to the **bot_timers** table and is included in this commit.

* Rebase Conflicts

* Update queries to use repositories

* Minor adjustment

* Formatting

* Handle delete as well

* Cleanup.

* Adjust primary keys to prevent conflicts

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2023-12-17 14:53:34 -05:00
Alex King 4ca6485398 [Bug Fix] Fix issue with HOTBonusHealingSplitOverDuration Rule (#3776)
# Notes
- This rule didn't function properly since we didn't add extra heal regardless of rule setting.
2023-12-16 22:48:19 -06:00
Alex King 0c9c2e25c1 [Quest API] Add EVENT_CRYSTAL_GAIN and EVENT_CRYSTAL_LOSS to Perl/Lua (#3735)
* [Quest API] Add EVENT_CRYSTAL_GAIN and EVENT_CRYSTAL_LOSS

- Add `$client->AddEbonCrystals(amount)`.
- Add `$client->AddRadiantCrystals(amount)`.
- Add `$client->RemoveEbonCrystals(amount)`.
- Add `$client->RemoveRadiantCrystals(amount)`.
- Add `EVENT_CRYSTAL_GAIN`.
- Add `EVENT_CRYSTAL_LOSS`.
- Export `$ebon_amount`, `$radiant_amount`, and `$is_reclaim`.

- Add `client:AddEbonCrystals(amount)`.
- Add `client:AddRadiantCrystals(amount)`.
- Add `client:RemoveEbonCrystals(amount)`.
- Add `client:RemoveRadiantCrystals(amount)`.
- Add `event_crystal_gain`.
- Add `event_crystal_loss`.
- Export `e.ebon_amount`, `e.radiant_amount`, and `e.is_reclaim`.

- Allows operators to add or remove Ebon/Radiant Crystals directly.
- Allows operators to track gain/loss of Ebon/Radiant Crystals.

* Update perl_client.cpp

* Update lua_client.cpp
2023-12-16 22:47:13 -06:00
regneq 7e651877c7 [Bug Fix] Fixed the discrepacy with time using command #time and in quests. (#3767)
* [BUG] Fixed the discrepacy with time using command #time and in quests. https://github.com/EQEmu/Server/issues/3700

* removed comments and paratheses from previous commit.

* fixed typos.

* made some adjustment so #time, /time, scripting, and log all match.

* Update lua_general.cpp
2023-12-16 22:40:40 -06:00
Alex King 9739c1c8ef [Quest API] Add EVENT_ALT_CURRENCY_GAIN and EVENT_ALT_CURRENCY_LOSS to Perl/Lua (#3734)
* [Quest API] Add EVENT_ALT_CURRENCY_GAIN and EVENT_ALT_CURRENCY_LOSS

- Add `EVENT_ALT_CURRENCY_GAIN`.
- Add `EVENT_ALT_CURRENCY_LOSS`.
- Export `$currency_id`, `$amount`, and `$total.

- Add `event_alt_currency_gain`.
- Add `event_alt_currency_loss`.
- Export `e.currency_id`, `e.amount`, and `e.total.

- Convert `int8 method` to `bool is_scripted` in `Client::AddAlternateCurrencyValue`.
- Properly utilize `is_scripted` parameter in `perl_client.cpp`.
- Allows operators to perform events on alternate currency gains/losses.

* Update lua_general.cpp

* Cleanup types.

* Update lua_client.cpp
2023-12-16 22:40:24 -06:00
Alex King 8aae59eebe [Quest API] Add EVENT_LDON_POINTS_GAIN and EVENT_LDON_POINTS_LOSS to Perl/Lua (#3742)
* [Quest API] Add EVENT_LDON_POINTS_GAIN and EVENT_LDON_POINTS_LOSS to Perl/Lua

- Add `EVENT_LDON_POINTS_GAIN`.
- Add `EVENT_LDON_POINTS_LOSS`.
- Exports `$theme_id` and `$points`.

- Add `event_ldon_points_gain`.
- Add `event_ldon_points_loss`.
- Exports `e.theme_id` and `e.points`.

- Allows operators to track gain/loss of LDoN Points of any theme.

* Update client.cpp
2023-12-16 22:31:25 -06:00
Alex King c1b07afae9 [Quest API] Add EVENT_LOOT_ADDED to Perl/Lua (#3739)
* [Quest API] Add EVENT_ADDED_LOOT to Perl/Lua

# Perl
- Add `EVENT_ADDED_LOOT`.
- Exports `$item`, `$item_id`, `$item_name`, `$item_charges`, `$augment_one`, `$augment_two`, `$augment_three`, `$augment_four`, `$augment_five`, and `$augment_six`.

# Lua
- Add `event_added_loot`.
- Exports `e.item`, `e.item_id`, `e.item_name`, `e.item_charges`, `e.augment_one`, `e.augment_two`, `e.augment_three`, `e.augment_four`, `e.augment_five`, and `e.augment_six`.

# Notes
- Allows operators to perform events when loot is added to an NPC, such as removing the loot or keeping track of it.

* Update lua_parser_events.cpp

* Rename event.

* loot_added

* AddItem changese
2023-12-16 22:25:09 -06:00
Alex King 9c238cd08d [Quest API] Add EVENT_LEVEL_UP and EVENT_LEVEL_DOWN to Bots (#3750)
* [Quest API] Add EVENT_LEVEL_UP and EVENT_LEVEL_DOWN to bots

# Notes
- Bots did not have these events, this will allow operators to perform events on bot level up/down.

* Update bot.cpp
2023-12-16 22:23:38 -06:00
Akkadius 33adb9bcc1 [Hotfix] Fix bad merge 2023-12-16 21:06:36 -06:00
nytmyr 2e8bf82861 [Bots] Expand ^itemuse options (#3756)
Adds additional options to **^itemuse** to narrow down the list of accepting bots.

You can now specify by class, casters, hybrids, melee, WIS caster, INT caster or plate/chain/leather/cloth wearing.
2023-12-16 20:54:23 -05:00
nytmyr 4d118ab978 [Bots] Fix ^oo autodefend from sending bots/pets to invalid haters (#3772)
Bots and especially their pets could get in a loop where they try to attack an invalid target. This would result in their pets spamming "That is not a legal target" messages.
2023-12-16 16:08:16 -05:00
Chris Miles fcb40daaf1 [Release] 22.36.0 (#3774) 2023-12-16 01:07:19 -06:00
Alex King 553bafdbe1 [Bug Fix] Fix Starting Items SQL (#3766)
* [Bug Fix] Fix Starting Items SQL

# Notes
- `race_list` comes before `class_list`,query had them in the wrong order.

* Real fix.

* Update database_update_manifest.cpp

* Manifest change

* Repository generate

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2023-12-16 00:45:27 -06:00
Chris Miles d0443db199 [Database] When database version is greater than binary, we are up to date (#3771) 2023-12-16 00:44:40 -06:00
Chris Miles 9206163190 [Database] Make it clearer to users that a database backup is occurring (#3769) 2023-12-16 00:44:25 -06:00
Chris Miles e504482b94 [Corpse] Fix /corpse command regression from #3727 (#3770) 2023-12-16 00:44:11 -06:00
Alex King 77b88e3dec [Rules] Add DOT and HOT Rules (#3760)
* [Rules] Add DOT and HOT Rules

# Notes
- Add `Spells:DOTDamageBonusSplitOverDuration` rule.
- Allows operators to disable the DOT bonus damage being split over duration and instead adds the full amount every tic.
- Add `Spells:HOTBonusHealingSplitOverDuration` rule.
- Allows operators to disable the HOT bonus healing being split over duration and instead adds the full amount every tic.

* Update effects.cpp

* Update effects.cpp
2023-12-15 19:25:38 -06:00
Alex King aeeb350068 [Cleanup] Cleanup classes.cpp/classes.h (#3752)
* [Cleanup] Cleanup classes.cpp/classes.h

# Notes
- Remove unused methods and cleanup logic.

* Update classes.cpp

* Final push.
2023-12-13 18:38:45 -05:00
nytmyr df83113cea [Bots] Enable auto-saving of bots. (#3758)
Adds the rule **Bots, AutosaveIntervalSeconds** to control the frequency of the autosave of bots. Currently they only fully save on camp/zone.

Co-authored-by: Alex King <89047260+Kinglykrab@users.noreply.github.com>
2023-12-12 22:22:44 -05:00
nytmyr 940abfaf7a [Logging] Change empty object loading to warning (#3759) 2023-12-11 17:06:29 -05:00
nytmyr a46443b95e [Bots] Add rule to toggle DT hitting owner (#3757)
**Bots, CazicTouchBotsOwner** is a rule that will toggle whether or not DT will hit the targeted bot or the owner. Similar to how **Spells, CazicTouchTargetsPetOwner** functions except for  bots.
2023-12-11 14:31:33 -05:00
JJ 871f320311 [Release] 22.35.0 (#3754)
### Bots

* Add BotHealOnLevel to fully heal/mana on level. ([#3745](https://github.com/EQEmu/Server/pull/3745)) @nytmyr 2023-12-08
* Fix bots learning spells on level ([#3744](https://github.com/EQEmu/Server/pull/3744)) @nytmyr 2023-12-08

### Bug

* Fix blocked spells regression from #3638 ([#3753](https://github.com/EQEmu/Server/pull/3753)) @joligario 2023-12-11
* PR 3638 Missed the blocked spells repository updates ([#3748](https://github.com/EQEmu/Server/pull/3748)) @fryguy503 2023-12-08

### CMake

* Update minimum version of CMake ([#3743](https://github.com/EQEmu/Server/pull/3743)) @joligario 2023-12-08

### Code

* Remove hard-coded Status Checks ([#3727](https://github.com/EQEmu/Server/pull/3727)) @Kinglykrab 2023-12-03

### Commands

* #guild set CharName 0 did not remove char from guild. ([#3717](https://github.com/EQEmu/Server/pull/3717)) @noudess 2023-11-25
* #petname changes PC to Nobody if selected. ([#3720](https://github.com/EQEmu/Server/pull/3720)) @noudess 2023-11-26
* Add #show aas Command ([#3710](https://github.com/EQEmu/Server/pull/3710)) @Kinglykrab 2023-11-26
* Add #task complete Command ([#3711](https://github.com/EQEmu/Server/pull/3711)) @Kinglykrab 2023-11-26
* Cleanup #acceptrules Command ([#3716](https://github.com/EQEmu/Server/pull/3716)) @Kinglykrab 2023-11-26
* Cleanup #disarmtrap Command ([#3713](https://github.com/EQEmu/Server/pull/3713)) @Kinglykrab 2023-11-26
* Cleanup #list Command ([#3714](https://github.com/EQEmu/Server/pull/3714)) @Kinglykrab 2023-11-26
* Cleanup #movement Command ([#3715](https://github.com/EQEmu/Server/pull/3715)) @Kinglykrab 2023-11-26
* Cleanup #object Command ([#3722](https://github.com/EQEmu/Server/pull/3722)) @Kinglykrab 2023-12-03
* Cleanup #zonebootup and #zoneshutdown Commands ([#3729](https://github.com/EQEmu/Server/pull/3729)) @Kinglykrab 2023-12-03
* Fix formatting of #wpinfo output. ([#3728](https://github.com/EQEmu/Server/pull/3728)) @noudess 2023-12-01

### Database

* Add primary key to keyring table ([#3746](https://github.com/EQEmu/Server/pull/3746)) @Kinglykrab 2023-12-08
* Consolidate Starting Items Table ([#3723](https://github.com/EQEmu/Server/pull/3723)) @Kinglykrab 2023-11-30
* Extra whitespace in #3723 ([#3730](https://github.com/EQEmu/Server/pull/3730)) @joligario 2023-12-02
* Minor adjustment to #3726 ([#3732](https://github.com/EQEmu/Server/pull/3732)) @joligario 2023-12-03
* Modify `updated` column in `items` table with proper default. ([#3726](https://github.com/EQEmu/Server/pull/3726)) @joligario 2023-12-02
* Pull Spell Group Cache from Content DB ([#3749](https://github.com/EQEmu/Server/pull/3749)) @fryguy503 2023-12-08

### Faction

* Alliance line is only allowed 1 faction change at a time. ([#3718](https://github.com/EQEmu/Server/pull/3718)) @noudess 2023-11-26

### Fixes

* Changing Group Leader Invalidated GetLeaderName() ([#3712](https://github.com/EQEmu/Server/pull/3712)) @Kinglykrab 2023-11-26
* Fix 9245 SQL ([#3740](https://github.com/EQEmu/Server/pull/3740)) @Kinglykrab 2023-12-05
* Fix Swarm Pets Requiring NPC Aggros Flag ([#3738](https://github.com/EQEmu/Server/pull/3738)) @Kinglykrab 2023-12-05
* Guild Message Limits ([#3724](https://github.com/EQEmu/Server/pull/3724)) @neckkola 2023-11-29

### Quest API

* Add EVENT_AA_GAIN to AddAAPoints() ([#3733](https://github.com/EQEmu/Server/pull/3733)) @Kinglykrab 2023-12-03
* Add GMMove Overloads to Perl/Lua ([#3719](https://github.com/EQEmu/Server/pull/3719)) @Kinglykrab 2023-11-25

### Scripts

* Import items into `items_new` table instead of writing directly to the existing `items` table. ([#3725](https://github.com/EQEmu/Server/pull/3725)) @joligario 2023-11-30
* Revert database engine change from #3702. ([#3736](https://github.com/EQEmu/Server/pull/3736)) @joligario 2023-12-03
* Update 13th Floor Import Tool ([#3702](https://github.com/EQEmu/Server/pull/3702)) @joligario 2023-11-26
2023-12-10 22:07:09 -05:00
JJ a222128599 [Bug] Fix blocked spells regression from #3638 (#3753)
Fixes #3747
2023-12-10 21:40:06 -05:00
JJ 45b249e33d [CMake] Update minimum version of CMake (#3743)
```
CMake Deprecation Warning at client_files/CMakeLists.txt:1 (CMAKE_MINIMUM_REQUIRED):
  Compatibility with CMake < 3.5 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.
```
2023-12-08 12:48:59 -08:00
Fryguy c24834de5d [Database] Pull Spell Group Cache from Content DB (#3749) 2023-12-08 15:41:09 -05:00
Fryguy 841d7f2700 [Bug] PR 3638 Missed the blocked spells repository updates (#3748)
Ran generator to update blocked_spells Repo
2023-12-08 15:19:54 -05:00
Alex King 56c29154f0 [Database] Add primary key to keyring table (#3746)
* [Database] Add primary key to keyring table

# Notes
- Adds a primary key of `id` to `keyring` table so we can use it with repositories.

* Update version.h

* Update client.cpp

* Update client.cpp
2023-12-07 23:12:01 -05:00
nytmyr 6466c2ff21 [Bots] Add BotHealOnLevel to fully heal/mana on level. (#3745)
Adds the rule ***Bots, BotHealOnLevel***, default false, to control whether or not bots will gain full health and mana when their owner levels.
2023-12-07 22:25:17 -05:00
nytmyr 16dc210cd8 [Bots] Fix bots learning spells on level (#3744)
Previously bots would need to be zoned or camped/spawned to begin using new spells for their level.
2023-12-07 21:19:21 -05:00
Alex King 77045f558e [Bug Fix] Fix 9245 SQL (#3740)
* [Bug Fix] Fix 9245 SQL

# Notes
- Should be a `;` not `,`.

* Update database_update_manifest.cpp
2023-12-04 22:21:31 -05:00
Alex King e1fa2d5bc5 [Bug Fix] Fix Swarm Pets Requiring NPC Aggros Flag (#3738)
# Notes
- https://github.com/EQEmu/Server/pull/3595 made swarm pets require the `npc_aggro` flag to be set to attack.
2023-12-04 19:05:16 -05:00
Alex King b03f52de18 [Commands] Cleanup #object Command (#3722)
* [Commands] Cleanup #object Command

# Notes
- Cleanup messages and logic.
- Introduce enum for object types.
- Set ground work for object manipulation similar to door manipulation.

* Update object_manipulation.cpp

* Final push

* Update client_packet.cpp

* Update object_manipulation.cpp

* Update object_manipulation.cpp

* Update object.h

* Update client_packet.cpp

* Update client_packet.cpp

* Push.

* Update version.h

* Update database_update_manifest.cpp

* Update zone.cpp
2023-12-03 17:42:27 -05:00
JJ 226cc3d6cb [Scripts] Revert database engine change from #3702. (#3736)
InnoDB doesn't like the lengths. Will need to address that in the future.
2023-12-03 14:17:58 -05:00
Alex King 7f54e26dec [Quest API] Add EVENT_AA_GAIN to AddAAPoints() (#3733)
# Notes
- Using this method did not call `EVENT_AA_GAIN`.
2023-12-03 12:41:06 -05:00
Alex King 11a81d8e8a [Commands] Cleanup #zonebootup and #zoneshutdown Commands (#3729)
# Notes
- Cleanup messages and logic.
2023-12-03 12:40:54 -05:00
Alex King e719aa43cf [Cleanup] Remove hard-coded Status Checks (#3727)
* [Cleanup] Remove hard-coded Status Checks

# Notes
- Removed the hard-coded GM status checks since if you have access to the command we can now limit access to subcommands if necessary.

* Update client_packet.cpp
2023-12-03 11:44:30 -05:00
JJ 22994e3264 [Database] Minor adjustment to #3726 (#3732) 2023-12-03 10:18:27 -05:00
JJ cfbdecad19 [Database] Extra whitespace in #3723 (#3730) 2023-12-01 21:13:08 -05:00
JJ 2427f7e034 [Database] Modify updated column in items table with proper default. (#3726)
* [Database] Modify `updated` column in `items` table with proper default.
From https://mariadb.com/kb/en/datetime/, `datetime` can be ZERO_DATE (`0000-00-00` with no time component) or between `1000-01-01 00:00:00.000000` and `9999-12-31 23:59:59.999999`. Currently, `updated` is the only datetime field that is `NOT NULL` and improperly defaulted to `0000-00-00 00:00:00`. This change matches existing structures of the other `datetime` columns.

* Update items_repository.h

* Update base_items_repository.h

* Revert "Update items_repository.h"

This reverts commit 3599f26818.
2023-12-01 21:10:52 -05:00
Paul Coene 9a6403b196 [Commands] Fix formatting of #wpinfo output. (#3728) 2023-12-01 15:28:24 -05:00
JJ d8953c5156 [Scripts] Import items into items_new table instead of writing directly to the existing items table. (#3725) 2023-11-30 17:21:00 -05:00
Alex King 33b40e83b7 [Database] Consolidate Starting Items Table (#3723)
* [Database] Consolidate Starting Items Table

# Notes
- Convert `class`, `deityId`, `race`, and `zoneid` columns to `|` separated columns.
- Consolidates up to 15 rows per item down to a singular row.
- Allows ease of use for operators.
- Entire process is automated and creates a backup of pre-existing table.

* Update shareddb.cpp

* Unnecessary.
2023-11-30 14:37:08 -05:00
Mitch Freeman e75c31d524 [Bug Fix] Guild Message Limits (#3724)
Mail to 'Guild' was failing after after 50ish members.
The buffer was set at 512, which cut truncated the message and caused the issue.
Refactored to adjust the size of the buffer based on the inbound message size.
2023-11-28 19:03:32 -05:00
Paul Coene cf1f8d5460 [Commands] #petname changes PC to Nobody if selected. (#3720) 2023-11-26 14:50:44 -05:00
Paul Coene 690cacdaab [Faction] Alliance line is only allowed 1 faction change at a time. (#3718) 2023-11-26 01:13:16 -05:00
Alex King f9f45eedcd [Commands] Cleanup #acceptrules Command (#3716)
# Note
- Cleanup messages and logic.
- Utilize repositories in database methods.
2023-11-26 01:13:08 -05:00
Alex King 93ddffa57f [Commands] Cleanup #movement Command (#3715)
# Notes
- Cleanup messages and logic.
2023-11-26 01:13:03 -05:00
Alex King c9993fb698 [Commands] Cleanup #list Command (#3714)
* [Commands] Cleanup #list Command

# Notes
- Cleanup messages and logic.
- Introduce a struct so we don't have to duplicate so much code.

* Update list.cpp
2023-11-26 01:12:58 -05:00
Alex King aa0fbb8b45 [Commands] Cleanup #disarmtrap Command (#3713)
# Notes
- Cleanup messages and logic.
2023-11-26 00:27:22 -05:00
Alex King 62532c6bdd [Bug Fix] Changing Group Leader Invalidated GetLeaderName() (#3712)
# Notes
- Utilizes fixes posted in https://github.com/EQEmu/Server/issues/3706 to resolve https://github.com/EQEmu/Server/issues/368.
- Changing group leader caused issues because we assumed `member[0]` was always leader.
2023-11-26 00:27:15 -05:00
Alex King f8c3c03185 [Commands] Add #task complete Command (#3711)
# Notes
- `#task complete [Task ID]` allows operators to complete an entire task without updating individual activities.
2023-11-26 00:27:08 -05:00
Alex King 692a90f3f0 [Commands] Add #show aas Command (#3710)
# Notes
- Allows operators to view AAs a player has purchased from ingame and their ranks.
2023-11-26 00:27:02 -05:00
JJ 3a49d851ca [Scripts] Update 13th Floor Import Tool (#3702) 2023-11-26 00:20:10 -05:00
Alex King fdc5c27061 [Quest API] Add GMMove Overloads to Perl/Lua (#3719)
# Perl
- Add `$mob->GMMove(x, y, z, heading, save_guard_spot)`.

# Lua
- Add `mob:GMMove(x, y, z, heading, save_guard_spot)`.

# Notes
- Operators weren't able to disable saving guard spots, so moving with `GMMove` meant NPCs stayed where they were moved.
2023-11-25 18:15:37 -05:00
Paul Coene 56be69ddb1 [Commands] #guild set CharName 0 did not remove char from guild. (#3717)
* [Commands] #guild set CharName 0 did not remove char from guild.

* Fix cut-n-paste error on this branch
2023-11-25 11:27:43 -05:00
141 changed files with 7934 additions and 5152 deletions
+1 -1
View File
@@ -15,7 +15,7 @@ volumes:
steps:
- name: Build Linux X64
image: akkadius/eqemu-server:v13
image: akkadius/eqemu-server:v14
environment:
GITHUB_TOKEN:
from_secret: GH_RELEASE_GITHUB_API_TOKEN
+134
View File
@@ -1,3 +1,137 @@
## [22.37.0] - 12/18/2023
### Bots
* Add ScanCloseMobs support to fix AEs ([#3786](https://github.com/EQEmu/Server/pull/3786)) @nytmyr 2023-12-18
* Expand ^itemuse options ([#3756](https://github.com/EQEmu/Server/pull/3756)) @nytmyr 2023-12-17
* Fix ^defensive from checking aggressive disciplines. ([#3787](https://github.com/EQEmu/Server/pull/3787)) @nytmyr 2023-12-18
* Fix ^oo autodefend from sending bots/pets to invalid haters ([#3772](https://github.com/EQEmu/Server/pull/3772)) @nytmyr 2023-12-16
* Fix unnecessary failed to save timer error ([#3788](https://github.com/EQEmu/Server/pull/3788)) @nytmyr 2023-12-18
* [Quest API] Add ^clickitem, ^timer, fix GetBestBotSpellForCure ([#3755](https://github.com/EQEmu/Server/pull/3755)) @nytmyr 2023-12-17
### CI
* Switch to use clang for Linux builds (speed) ([#3777](https://github.com/EQEmu/Server/pull/3777)) @Akkadius 2023-12-17
### Compilation
* Use pre-compiled headers for Windows (speed) ([#3778](https://github.com/EQEmu/Server/pull/3778)) @Akkadius 2023-12-18
### Fixes
* Drop Invisibility when hit by traps ([#3785](https://github.com/EQEmu/Server/pull/3785)) @Kinglykrab 2023-12-18
* Fix NPCs routing to 0.0, 0.0 on #summon ([#3780](https://github.com/EQEmu/Server/pull/3780)) @Kinglykrab 2023-12-18
* Fix bad merge @Akkadius 2023-12-17
* Fix issue with HOTBonusHealingSplitOverDuration Rule ([#3776](https://github.com/EQEmu/Server/pull/3776)) @Kinglykrab 2023-12-17
* Fixed the discrepacy with time using command #time and in quests. ([#3767](https://github.com/EQEmu/Server/pull/3767)) @regneq 2023-12-17
* Send Entity ID in Death Events to resolve #3721 ([#3779](https://github.com/EQEmu/Server/pull/3779)) @Kinglykrab 2023-12-18
### Quest API
* Add EVENT_ALT_CURRENCY_GAIN and EVENT_ALT_CURRENCY_LOSS to Perl/Lua ([#3734](https://github.com/EQEmu/Server/pull/3734)) @Kinglykrab 2023-12-17
* Add EVENT_CRYSTAL_GAIN and EVENT_CRYSTAL_LOSS to Perl/Lua ([#3735](https://github.com/EQEmu/Server/pull/3735)) @Kinglykrab 2023-12-17
* Add EVENT_LDON_POINTS_GAIN and EVENT_LDON_POINTS_LOSS to Perl/Lua ([#3742](https://github.com/EQEmu/Server/pull/3742)) @Kinglykrab 2023-12-17
* Add EVENT_LEVEL_UP and EVENT_LEVEL_DOWN to Bots ([#3750](https://github.com/EQEmu/Server/pull/3750)) @Kinglykrab 2023-12-17
* Add EVENT_LOOT_ADDED to Perl/Lua ([#3739](https://github.com/EQEmu/Server/pull/3739)) @Kinglykrab 2023-12-17
* Add GetNPCAggro() and SetNPCAggro() to Perl/Lua ([#3781](https://github.com/EQEmu/Server/pull/3781)) @Kinglykrab 2023-12-18
## [22.36.0] - 12/16/2023
### Bots
* Add rule to toggle DT hitting owner ([#3757](https://github.com/EQEmu/Server/pull/3757)) @nytmyr 2023-12-11
* Enable auto-saving of bots. ([#3758](https://github.com/EQEmu/Server/pull/3758)) @nytmyr 2023-12-13
### Code
* Cleanup classes.cpp/classes.h ([#3752](https://github.com/EQEmu/Server/pull/3752)) @Kinglykrab 2023-12-13
### Corpse
* Fix /corpse command regression from #3727 ([#3770](https://github.com/EQEmu/Server/pull/3770)) @Akkadius 2023-12-16
### Database
* Make it clearer to users that a database backup is occurring ([#3769](https://github.com/EQEmu/Server/pull/3769)) @Akkadius 2023-12-16
* When database version is greater than binary, we are up to date ([#3771](https://github.com/EQEmu/Server/pull/3771)) @Akkadius 2023-12-16
### Fixes
* Fix Starting Items SQL ([#3766](https://github.com/EQEmu/Server/pull/3766)) @Kinglykrab 2023-12-16
### Logging
* Change empty object loading to warning ([#3759](https://github.com/EQEmu/Server/pull/3759)) @nytmyr 2023-12-11
### Rules
* Add DOT and HOT Rules ([#3760](https://github.com/EQEmu/Server/pull/3760)) @Kinglykrab 2023-12-16
## [22.35.0] - 12/10/2023
### Bots
* Add BotHealOnLevel to fully heal/mana on level. ([#3745](https://github.com/EQEmu/Server/pull/3745)) @nytmyr 2023-12-08
* Fix bots learning spells on level ([#3744](https://github.com/EQEmu/Server/pull/3744)) @nytmyr 2023-12-08
### Bug
* Fix blocked spells regression from #3638 ([#3753](https://github.com/EQEmu/Server/pull/3753)) @joligario 2023-12-11
* PR 3638 Missed the blocked spells repository updates ([#3748](https://github.com/EQEmu/Server/pull/3748)) @fryguy503 2023-12-08
### CMake
* Update minimum version of CMake ([#3743](https://github.com/EQEmu/Server/pull/3743)) @joligario 2023-12-08
### Code
* Remove hard-coded Status Checks ([#3727](https://github.com/EQEmu/Server/pull/3727)) @Kinglykrab 2023-12-03
### Commands
* #guild set CharName 0 did not remove char from guild. ([#3717](https://github.com/EQEmu/Server/pull/3717)) @noudess 2023-11-25
* #petname changes PC to Nobody if selected. ([#3720](https://github.com/EQEmu/Server/pull/3720)) @noudess 2023-11-26
* Add #show aas Command ([#3710](https://github.com/EQEmu/Server/pull/3710)) @Kinglykrab 2023-11-26
* Add #task complete Command ([#3711](https://github.com/EQEmu/Server/pull/3711)) @Kinglykrab 2023-11-26
* Cleanup #acceptrules Command ([#3716](https://github.com/EQEmu/Server/pull/3716)) @Kinglykrab 2023-11-26
* Cleanup #disarmtrap Command ([#3713](https://github.com/EQEmu/Server/pull/3713)) @Kinglykrab 2023-11-26
* Cleanup #list Command ([#3714](https://github.com/EQEmu/Server/pull/3714)) @Kinglykrab 2023-11-26
* Cleanup #movement Command ([#3715](https://github.com/EQEmu/Server/pull/3715)) @Kinglykrab 2023-11-26
* Cleanup #object Command ([#3722](https://github.com/EQEmu/Server/pull/3722)) @Kinglykrab 2023-12-03
* Cleanup #zonebootup and #zoneshutdown Commands ([#3729](https://github.com/EQEmu/Server/pull/3729)) @Kinglykrab 2023-12-03
* Fix formatting of #wpinfo output. ([#3728](https://github.com/EQEmu/Server/pull/3728)) @noudess 2023-12-01
### Database
* Add primary key to keyring table ([#3746](https://github.com/EQEmu/Server/pull/3746)) @Kinglykrab 2023-12-08
* Consolidate Starting Items Table ([#3723](https://github.com/EQEmu/Server/pull/3723)) @Kinglykrab 2023-11-30
* Extra whitespace in #3723 ([#3730](https://github.com/EQEmu/Server/pull/3730)) @joligario 2023-12-02
* Minor adjustment to #3726 ([#3732](https://github.com/EQEmu/Server/pull/3732)) @joligario 2023-12-03
* Modify `updated` column in `items` table with proper default. ([#3726](https://github.com/EQEmu/Server/pull/3726)) @joligario 2023-12-02
* Pull Spell Group Cache from Content DB ([#3749](https://github.com/EQEmu/Server/pull/3749)) @fryguy503 2023-12-08
### Faction
* Alliance line is only allowed 1 faction change at a time. ([#3718](https://github.com/EQEmu/Server/pull/3718)) @noudess 2023-11-26
### Fixes
* Changing Group Leader Invalidated GetLeaderName() ([#3712](https://github.com/EQEmu/Server/pull/3712)) @Kinglykrab 2023-11-26
* Fix 9245 SQL ([#3740](https://github.com/EQEmu/Server/pull/3740)) @Kinglykrab 2023-12-05
* Fix Swarm Pets Requiring NPC Aggros Flag ([#3738](https://github.com/EQEmu/Server/pull/3738)) @Kinglykrab 2023-12-05
* Guild Message Limits ([#3724](https://github.com/EQEmu/Server/pull/3724)) @neckkola 2023-11-29
### Quest API
* Add EVENT_AA_GAIN to AddAAPoints() ([#3733](https://github.com/EQEmu/Server/pull/3733)) @Kinglykrab 2023-12-03
* Add GMMove Overloads to Perl/Lua ([#3719](https://github.com/EQEmu/Server/pull/3719)) @Kinglykrab 2023-11-25
### Scripts
* Import items into `items_new` table instead of writing directly to the existing `items` table. ([#3725](https://github.com/EQEmu/Server/pull/3725)) @joligario 2023-11-30
* Revert database engine change from #3702. ([#3736](https://github.com/EQEmu/Server/pull/3736)) @joligario 2023-12-03
* Update 13th Floor Import Tool ([#3702](https://github.com/EQEmu/Server/pull/3702)) @joligario 2023-11-26
## [22.34.2] - 11/23/2023
### Admin
+1
View File
@@ -17,6 +17,7 @@ SET(CMAKE_CXX_STANDARD_REQUIRED ON)
SET(CMAKE_CXX_EXTENSIONS OFF)
OPTION(EQEMU_BUILD_STATIC "Build with static linking" OFF)
OPTION(EQEMU_BUILD_PCH "Build with precompiled headers (Windows)" ON)
IF (EQEMU_BUILD_STATIC)
SET(BUILD_SHARED_LIBS OFF)
+1 -1
View File
@@ -1,4 +1,4 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.2)
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
add_subdirectory(import)
add_subdirectory(export)
+3
View File
@@ -792,5 +792,8 @@ IF (UNIX)
SET_SOURCE_FILES_PROPERTIES("patches/sod.cpp" "patches/sof.cpp" "patches/rof.cpp" "patches/rof2.cpp" "patches/uf.cpp" PROPERTIES COMPILE_FLAGS -O0)
ENDIF (UNIX)
IF (WIN32 AND EQEMU_BUILD_PCH)
TARGET_PRECOMPILE_HEADERS(common PRIVATE pch/pch.h)
ENDIF()
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
+296 -473
View File
File diff suppressed because it is too large Load Diff
+96 -93
View File
@@ -19,99 +19,106 @@
#define CLASSES_CH
#include "../common/types.h"
#include "../common/rulesys.h"
#include <string>
#include <map>
#define NO_CLASS 0
#define WARRIOR 1
#define CLERIC 2
#define PALADIN 3
#define RANGER 4
#define SHADOWKNIGHT 5
#define DRUID 6
#define MONK 7
#define BARD 8
#define ROGUE 9
#define SHAMAN 10
#define NECROMANCER 11
#define WIZARD 12
#define MAGICIAN 13
#define ENCHANTER 14
#define BEASTLORD 15
#define BERSERKER 16
#define WARRIORGM 20
#define CLERICGM 21
#define PALADINGM 22
#define RANGERGM 23
#define SHADOWKNIGHTGM 24
#define DRUIDGM 25
#define MONKGM 26
#define BARDGM 27
#define ROGUEGM 28
#define SHAMANGM 29
#define NECROMANCERGM 30
#define WIZARDGM 31
#define MAGICIANGM 32
#define ENCHANTERGM 33
#define BEASTLORDGM 34
#define BERSERKERGM 35
#define BANKER 40
#define MERCHANT 41
#define DISCORD_MERCHANT 59
#define ADVENTURE_RECRUITER 60
#define ADVENTURE_MERCHANT 61
#define LDON_TREASURE 62 // objects you can use /open on first seen in LDONs, seen on Danvi's Corpse in Akheva
#define TRIBUTE_MASTER 63
#define GUILD_TRIBUTE_MASTER 64 // not sure
#define GUILD_BANKER 66
#define NORRATHS_KEEPERS_MERCHANT 67
#define DARK_REIGN_MERCHANT 68
#define FELLOWSHIP_MASTER 69
#define ALT_CURRENCY_MERCHANT 70
#define MERCENARY_MASTER 71
namespace Class {
constexpr uint8 None = 0;
constexpr uint8 Warrior = 1;
constexpr uint8 Cleric = 2;
constexpr uint8 Paladin = 3;
constexpr uint8 Ranger = 4;
constexpr uint8 ShadowKnight = 5;
constexpr uint8 Druid = 6;
constexpr uint8 Monk = 7;
constexpr uint8 Bard = 8;
constexpr uint8 Rogue = 9;
constexpr uint8 Shaman = 10;
constexpr uint8 Necromancer = 11;
constexpr uint8 Wizard = 12;
constexpr uint8 Magician = 13;
constexpr uint8 Enchanter = 14;
constexpr uint8 Beastlord = 15;
constexpr uint8 Berserker = 16;
constexpr uint8 WarriorGM = 20;
constexpr uint8 ClericGM = 21;
constexpr uint8 PaladinGM = 22;
constexpr uint8 RangerGM = 23;
constexpr uint8 ShadowKnightGM = 24;
constexpr uint8 DruidGM = 25;
constexpr uint8 MonkGM = 26;
constexpr uint8 BardGM = 27;
constexpr uint8 RogueGM = 28;
constexpr uint8 ShamanGM = 29;
constexpr uint8 NecromancerGM = 30;
constexpr uint8 WizardGM = 31;
constexpr uint8 MagicianGM = 32;
constexpr uint8 EnchanterGM = 33;
constexpr uint8 BeastlordGM = 34;
constexpr uint8 BerserkerGM = 35;
constexpr uint8 Banker = 40;
constexpr uint8 Merchant = 41;
constexpr uint8 DiscordMerchant = 59;
constexpr uint8 AdventureRecruiter = 60;
constexpr uint8 AdventureMerchant = 61;
constexpr uint8 LDoNTreasure = 62;
constexpr uint8 TributeMaster = 63;
constexpr uint8 GuildTributeMaster = 64;
constexpr uint8 GuildBanker = 66;
constexpr uint8 NorrathsKeepersMerchant = 67;
constexpr uint8 DarkReignMerchant = 68;
constexpr uint8 FellowshipMaster = 69;
constexpr uint8 AlternateCurrencyMerchant = 70;
constexpr uint8 MercenaryLiaison = 71;
constexpr uint8 PLAYER_CLASS_COUNT = 16;
constexpr uint16 ALL_CLASSES_BITMASK = 65535;
};
// player class values
#define PLAYER_CLASS_UNKNOWN 0
#define PLAYER_CLASS_WARRIOR 1
#define PLAYER_CLASS_CLERIC 2
#define PLAYER_CLASS_PALADIN 3
#define PLAYER_CLASS_RANGER 4
#define PLAYER_CLASS_SHADOWKNIGHT 5
#define PLAYER_CLASS_DRUID 6
#define PLAYER_CLASS_MONK 7
#define PLAYER_CLASS_BARD 8
#define PLAYER_CLASS_ROGUE 9
#define PLAYER_CLASS_SHAMAN 10
#define PLAYER_CLASS_NECROMANCER 11
#define PLAYER_CLASS_WIZARD 12
#define PLAYER_CLASS_MAGICIAN 13
#define PLAYER_CLASS_ENCHANTER 14
#define PLAYER_CLASS_BEASTLORD 15
#define PLAYER_CLASS_BERSERKER 16
static std::map<uint8, uint16> player_class_bitmasks = {
{Class::Warrior, 1},
{Class::Cleric, 2},
{Class::Paladin, 4},
{Class::Ranger, 8},
{Class::ShadowKnight, 16},
{Class::Druid, 32},
{Class::Monk, 64},
{Class::Bard, 128},
{Class::Rogue, 256},
{Class::Shaman, 512},
{Class::Necromancer, 1024},
{Class::Wizard, 2048},
{Class::Magician, 4096},
{Class::Enchanter, 8192},
{Class::Beastlord, 16384},
{Class::Berserker, 32768},
};
#define PLAYER_CLASS_COUNT 16
static std::string shadow_knight_class_name = (
RuleB(World, UseOldShadowKnightClassExport) ?
"Shadowknight" :
"Shadow Knight"
);
// player class bits
#define PLAYER_CLASS_UNKNOWN_BIT 0
#define PLAYER_CLASS_WARRIOR_BIT 1
#define PLAYER_CLASS_CLERIC_BIT 2
#define PLAYER_CLASS_PALADIN_BIT 4
#define PLAYER_CLASS_RANGER_BIT 8
#define PLAYER_CLASS_SHADOWKNIGHT_BIT 16
#define PLAYER_CLASS_DRUID_BIT 32
#define PLAYER_CLASS_MONK_BIT 64
#define PLAYER_CLASS_BARD_BIT 128
#define PLAYER_CLASS_ROGUE_BIT 256
#define PLAYER_CLASS_SHAMAN_BIT 512
#define PLAYER_CLASS_NECROMANCER_BIT 1024
#define PLAYER_CLASS_WIZARD_BIT 2048
#define PLAYER_CLASS_MAGICIAN_BIT 4096
#define PLAYER_CLASS_ENCHANTER_BIT 8192
#define PLAYER_CLASS_BEASTLORD_BIT 16384
#define PLAYER_CLASS_BERSERKER_BIT 32768
#define PLAYER_CLASS_ALL_MASK 65535 // was 65536
static std::map<uint8, std::string> class_names = {
{Class::Warrior, "Warrior"},
{Class::Cleric, "Cleric"},
{Class::Paladin, "Paladin"},
{Class::Ranger, "Ranger"},
{Class::ShadowKnight, shadow_knight_class_name},
{Class::Druid, "Druid"},
{Class::Monk, "Monk"},
{Class::Bard, "Bard"},
{Class::Rogue, "Rogue"},
{Class::Shaman, "Shaman"},
{Class::Necromancer, "Necromancer"},
{Class::Wizard, "Wizard"},
{Class::Magician, "Magician"},
{Class::Enchanter, "Enchanter"},
{Class::Beastlord, "Beastlord"},
{Class::Berserker, "Berserker"},
};
#define ARMOR_TYPE_UNKNOWN 0
@@ -126,16 +133,12 @@
const char* GetClassIDName(uint8 class_id, uint8 level = 0);
const char* GetPlayerClassName(uint32 player_class_value, uint8 level = 0);
bool IsPlayerClass(uint8 class_id);
const std::string GetPlayerClassAbbreviation(uint8 class_id);
uint32 GetPlayerClassValue(uint8 class_id);
uint32 GetPlayerClassBit(uint8 class_id);
uint8 GetClassIDFromPlayerClassValue(uint32 player_class_value);
uint8 GetClassIDFromPlayerClassBit(uint32 player_class_bit);
uint8 GetPlayerClassValue(uint8 class_id);
uint16 GetPlayerClassBit(uint8 class_id);
bool IsFighterClass(uint8 class_id);
bool IsSpellFighterClass(uint8 class_id);
+12 -15
View File
@@ -29,6 +29,8 @@
#include <stdlib.h>
#include <string.h>
#include "../common/repositories/account_repository.h"
// Disgrace: for windows compile
#ifdef _WINDOWS
#include <windows.h>
@@ -1643,25 +1645,20 @@ void Database::ClearGroupLeader(uint32 gid) {
std::cout << "Unable to clear group leader: " << results.ErrorMessage() << std::endl;
}
uint8 Database::GetAgreementFlag(uint32 acctid) {
std::string query = StringFormat("SELECT rulesflag FROM account WHERE id=%i",acctid);
auto results = QueryDatabase(query);
if (!results.Success())
uint8 Database::GetAgreementFlag(uint32 account_id)
{
const auto& e = AccountRepository::FindOne(*this, account_id);
if (!e.id) {
return 0;
}
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
return Strings::ToUnsignedInt(row[0]);
return e.rulesflag;
}
void Database::SetAgreementFlag(uint32 acctid) {
std::string query = StringFormat("UPDATE account SET rulesflag=1 where id=%i", acctid);
QueryDatabase(query);
void Database::SetAgreementFlag(uint32 account_id) {
auto e = AccountRepository::FindOne(*this, account_id);
e.rulesflag = 1;
AccountRepository::UpdateOne(*this, e);
}
void Database::ClearRaid(uint32 rid) {
+2 -2
View File
@@ -188,10 +188,10 @@ public:
uint32 CheckLogin(const char* name, const char* password, const char *loginserver, int16* oStatus = 0);
uint32 CreateAccount(const char* name, const char* password, int16 status, const char* loginserver, uint32 lsaccount_id);
uint32 GetAccountIDFromLSID(const std::string& in_loginserver_id, uint32 in_loginserver_account_id, char* in_account_name = 0, int16* in_status = 0);
uint8 GetAgreementFlag(uint32 acctid);
uint8 GetAgreementFlag(uint32 account_id);
void GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus);
void SetAgreementFlag(uint32 acctid);
void SetAgreementFlag(uint32 account_id);
int GetIPExemption(std::string account_ip);
void SetIPExemption(std::string account_ip, int exemption_amount);
@@ -322,6 +322,10 @@ void DatabaseDumpService::DatabaseDump()
pipe_file
);
LogInfo("Backing up database [{}]", execute_command);
LogInfo("This can take a few minutes depending on the size of your database");
LogInfo("LOADING... PLEASE WAIT...");
BuildCredentialsFile();
std::string execution_result = Process::execute(execute_command);
if (!execution_result.empty() && IsDumpOutputToConsole()) {
+2 -2
View File
@@ -274,9 +274,9 @@ bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b)
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
// server database version is required
bool server_up_to_date = v.server_database_version == b.server_database_version;
bool server_up_to_date = v.server_database_version >= b.server_database_version;
// bots database version is optional, if not enabled then it is always up-to-date
bool bots_up_to_date = RuleB(Bots, Enabled) ? v.bots_database_version == b.bots_database_version : true;
bool bots_up_to_date = RuleB(Bots, Enabled) ? v.bots_database_version >= b.bots_database_version : true;
return server_up_to_date && bots_up_to_date;
}
+113 -7
View File
@@ -5019,17 +5019,123 @@ ALTER TABLE `spawn2` DROP COLUMN `enabled`;
)"
},
ManifestEntry{
.version = 9242,
.description = "2023_11_7_mintime_maxtime_spawnentry.sql",
.check = "SHOW COLUMNS FROM `spawnentry` LIKE 'min_time'",
.condition = "empty",
.match = "",
.sql = R"(
.version = 9242,
.description = "2023_11_7_mintime_maxtime_spawnentry.sql",
.check = "SHOW COLUMNS FROM `spawnentry` LIKE 'min_time'",
.condition = "empty",
.match = "",
.sql = R"(
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`;
)"
},
},
ManifestEntry{
.version = 9243,
.description = "2023_11_27_starting_items_revamp.sql",
.check = "SHOW COLUMNS FROM `starting_items` LIKE 'race_list'",
.condition = "empty",
.match = "",
.sql = R"(
CREATE TABLE `starting_items_backup_9243` LIKE `starting_items`;
INSERT INTO `starting_items_backup_9243` SELECT * FROM `starting_items`;
CREATE TABLE `starting_items_new` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`race_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`class_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`deity_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`zone_id_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
`item_id` int(11) UNSIGNED NOT NULL DEFAULT 0,
`item_charges` tinyint(3) UNSIGNED NOT NULL DEFAULT 1,
`gm` mediumint(3) UNSIGNED NOT NULL DEFAULT 0,
`slot` mediumint(9) NOT NULL DEFAULT -1,
`min_expansion` tinyint(4) NOT NULL DEFAULT -1,
`max_expansion` tinyint(4) NOT NULL DEFAULT -1,
`content_flags` varchar(100) NULL,
`content_flags_disabled` varchar(100) NULL,
PRIMARY KEY (`id`)
);
INSERT INTO
`starting_items_new`
(
SELECT
0 AS `id`,
GROUP_CONCAT(DISTINCT `class` ORDER BY class ASC SEPARATOR '|') AS `class_list`,
GROUP_CONCAT(DISTINCT `race` ORDER BY race ASC SEPARATOR '|') AS `race_list`,
GROUP_CONCAT(DISTINCT `deityid` ORDER BY deityid ASC SEPARATOR '|') AS `deity_list`,
GROUP_CONCAT(DISTINCT `zoneid` ORDER BY zoneid ASC SEPARATOR '|') AS `zone_list`,
`itemid`,
`item_charges`,
`gm`,
`slot`,
`min_expansion`,
`max_expansion`,
`content_flags`,
`content_flags_disabled`
FROM
`starting_items`
GROUP BY
`itemid`
);
DROP TABLE `starting_items`;
RENAME TABLE `starting_items_new` TO `starting_items`;
)"
},
ManifestEntry{
.version = 9244,
.description = "2023_11_30_items_table_schema.sql",
.check = "SHOW COLUMNS FROM `items` LIKE 'updated'",
.condition = "contains",
.match = "0000-00-00 00:00:00",
.sql = R"(
ALTER TABLE `items` MODIFY COLUMN `updated` datetime NULL DEFAULT NULL;
)"
},
ManifestEntry{
.version = 9245,
.description = "2023_12_03_object_incline.sql",
.check = "SHOW COLUMNS FROM `object` LIKE 'incline'",
.condition = "empty",
.match = "",
.sql = R"(
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`;
)"
},
ManifestEntry{
.version = 9246,
.description = "2023_12_07_keyring_id.sql",
.check = "SHOW COLUMNS FROM `keyring` LIKE 'id'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `keyring`
ADD COLUMN `id` int UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
ADD PRIMARY KEY (`id`);
)"
},
ManifestEntry{
.version = 9247,
.description = "2023_12_14_starting_items_fix.sql",
.check = "SHOW COLUMNS FROM `starting_items` LIKE 'inventory_slot'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `starting_items`
CHANGE COLUMN `race_list` `temporary` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `id`,
CHANGE COLUMN `class_list` `race_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `temporary`,
CHANGE COLUMN `gm` `status` mediumint(3) NOT NULL DEFAULT 0 AFTER `item_charges`,
CHANGE COLUMN `slot` `inventory_slot` mediumint(9) NOT NULL DEFAULT -1 AFTER `status`;
ALTER TABLE `starting_items`
CHANGE COLUMN `temporary` `class_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `id`;
)"
}
// -- template; copy/paste this when you need to create a new entry
// ManifestEntry{
// .version = 9228,
@@ -82,6 +82,28 @@ CREATE TABLE `bot_starting_items` (
`content_flags_disabled` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci;
)",
},
ManifestEntry{
.version = 9041,
.description = "2023_12_04_bot_timers.sql",
.check = "SHOW COLUMNS FROM `bot_timers` LIKE 'recast_time'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `bot_timers`
ADD COLUMN `recast_time` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `timer_value`,
ADD COLUMN `is_spell` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0 AFTER `recast_time`,
ADD COLUMN `is_disc` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0 AFTER `is_spell`,
ADD COLUMN `spell_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `is_disc`,
ADD COLUMN `is_item` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0 AFTER `spell_id`,
ADD COLUMN `item_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `is_item`;
ALTER TABLE `bot_timers`
DROP FOREIGN KEY `FK_bot_timers_1`;
ALTER TABLE `bot_timers`
DROP PRIMARY KEY;
ALTER TABLE `bot_timers`
ADD PRIMARY KEY (`bot_id`, `timer_id`, `spell_id`, `item_id`);
)"
}
// -- template; copy/paste this when you need to create a new entry
+8 -2
View File
@@ -124,6 +124,12 @@ struct LDoNTrapTemplate
uint8 locked;
};
enum CrystalReclaimTypes
{
Ebon = 5,
Radiant = 4,
};
///////////////////////////////////////////////////////////////////////////////
@@ -2594,11 +2600,11 @@ struct BookButton_Struct
struct Object_Struct {
/*00*/ uint32 linked_list_addr[2];// They are, get this, prev and next, ala linked list
/*08*/ float size; //
/*10*/ uint16 solidtype; //
/*10*/ uint16 solid_type; //
/*12*/ uint32 drop_id; // Unique object id for zone
/*16*/ uint16 zone_id; // Redudant, but: Zone the object appears in
/*18*/ uint16 zone_instance; //
/*20*/ uint32 unknown020; //
/*20*/ uint32 incline; //
/*24*/ uint32 unknown024; //
/*28*/ float tilt_x;
/*32*/ float tilt_y;
-19
View File
@@ -238,25 +238,6 @@ enum { //some random constants
// Timer to update aggrometer
#define AGGRO_METER_UPDATE_MS 1000
//Some hard coded statuses from commands and other places:
enum {
minStatusToBeGM = 40,
minStatusToUseGMCommands = 80,
minStatusToKick = 150,
minStatusToAvoidFalling = 100,
minStatusToIgnoreZoneFlags = 80,
minStatusToSeeOthersZoneFlags = 80,
minStatusToEditOtherGuilds = 80,
commandMovecharSelfOnly = 80, //below this == only self move allowed
commandMovecharToSpecials = 200, //ability to send people to cshom/load zones
commandCastSpecials = 100, //can cast special spells
commandDoAnimOthers = 100, //can #doanim on others
commandEditPlayerCorpses = 150, //can Edit Player Corpses
commandInterrogateInv = 100, //below this == only log on error state and self-only target dump
commandInvSnapshot = 150 //ability to clear/restore snapshots
};
// This is the item ID we use for say links, we use the max that fits in 5 ASCII chars
#define SAYLINK_ITEM_ID 0xFFFFF
+1 -1
View File
@@ -132,7 +132,7 @@ namespace EQ
// Swap items in inventory
enum SwapItemFailState : int8 { swapInvalid = -1, swapPass = 0, swapNotAllowed, swapNullData, swapRaceClass, swapDeity, swapLevel };
bool SwapItem(int16 source_slot, int16 destination_slot, SwapItemFailState& fail_state, uint16 race_id = RACE_DOUG_0, uint8 class_id = NO_CLASS, uint16 deity_id = deity::DeityType::DeityUnknown, uint8 level = 0);
bool SwapItem(int16 source_slot, int16 destination_slot, SwapItemFailState& fail_state, uint16 race_id = RACE_DOUG_0, uint8 class_id = Class::None, uint16 deity_id = deity::DeityType::DeityUnknown, uint8 level = 0);
// Remove item from inventory
bool DeleteItem(int16 slot_id, int16 quantity = 0);
+4 -4
View File
@@ -2706,7 +2706,7 @@ namespace RoF2
switch (raid_gen->action)
{
case raidAdd:
case raidAdd:
{
RaidAddMember_Struct* emu = (RaidAddMember_Struct*)__emu_buffer;
@@ -3986,7 +3986,7 @@ namespace RoF2
if (strlen(emu->suffix))
PacketSize += strlen(emu->suffix) + 1;
if (emu->DestructibleObject || emu->class_ == LDON_TREASURE)
if (emu->DestructibleObject || emu->class_ == Class::LDoNTreasure)
{
if (emu->DestructibleObject)
PacketSize = PacketSize - 4; // No bodytype
@@ -4076,7 +4076,7 @@ namespace RoF2
// actually part of bitfields
uint8 OtherData = 0;
if (emu->class_ == LDON_TREASURE) //LDoN Chest
if (emu->class_ == Class::LDoNTreasure) //LDoN Chest
{
OtherData = OtherData | 0x04;
}
@@ -4104,7 +4104,7 @@ namespace RoF2
// int DefaultEmitterID
VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4
if (emu->DestructibleObject || emu->class_ == LDON_TREASURE)
if (emu->DestructibleObject || emu->class_ == Class::LDoNTreasure)
{
VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleModel);
VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleName2);
+3 -3
View File
@@ -2758,7 +2758,7 @@ namespace UF
if (strlen(emu->suffix))
PacketSize += strlen(emu->suffix) + 1;
if (emu->DestructibleObject || emu->class_ == LDON_TREASURE)
if (emu->DestructibleObject || emu->class_ == Class::LDoNTreasure)
{
if (emu->DestructibleObject)
PacketSize = PacketSize - 4; // No bodytype
@@ -2847,7 +2847,7 @@ namespace UF
uint8 OtherData = 0;
if (emu->class_ == LDON_TREASURE) //Ldon chest
if (emu->class_ == Class::LDoNTreasure) //Ldon chest
{
OtherData = OtherData | 0x01;
}
@@ -2873,7 +2873,7 @@ namespace UF
}
VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4
if (emu->DestructibleObject || emu->class_ == LDON_TREASURE)
if (emu->DestructibleObject || emu->class_ == Class::LDoNTreasure)
{
VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleModel);
VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleName2);
+34
View File
@@ -0,0 +1,34 @@
// types
#include <limits>
#include <string>
#include <cctype>
#include <sstream>
// containers
#include <iterator>
#include <set>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <list>
#include <vector>
// utilities
#include <iostream>
#include <cassert>
#include <cmath>
#include <memory>
#include <functional>
#include <algorithm>
#include <utility>
#include <tuple>
#include <fstream>
#include <cstdio>
// fmt
#include <fmt/format.h>
// lua
#include "lua.hpp"
#include <luabind/luabind.hpp>
#include <luabind/object.hpp>
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseBlockedSpellsRepository {
public:
struct BlockedSpells {
@@ -31,6 +32,10 @@ public:
float z_diff;
std::string message;
std::string description;
int8_t min_expansion;
int8_t max_expansion;
std::string content_flags;
std::string content_flags_disabled;
};
static std::string PrimaryKey()
@@ -53,6 +58,10 @@ public:
"z_diff",
"message",
"description",
"min_expansion",
"max_expansion",
"content_flags",
"content_flags_disabled",
};
}
@@ -71,6 +80,10 @@ public:
"z_diff",
"message",
"description",
"min_expansion",
"max_expansion",
"content_flags",
"content_flags_disabled",
};
}
@@ -111,18 +124,22 @@ public:
{
BlockedSpells e{};
e.id = 0;
e.spellid = 0;
e.type = 0;
e.zoneid = 0;
e.x = 0;
e.y = 0;
e.z = 0;
e.x_diff = 0;
e.y_diff = 0;
e.z_diff = 0;
e.message = "";
e.description = "";
e.id = 0;
e.spellid = 0;
e.type = 0;
e.zoneid = 0;
e.x = 0;
e.y = 0;
e.z = 0;
e.x_diff = 0;
e.y_diff = 0;
e.z_diff = 0;
e.message = "";
e.description = "";
e.min_expansion = -1;
e.max_expansion = -1;
e.content_flags = "";
e.content_flags_disabled = "";
return e;
}
@@ -148,8 +165,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
blocked_spells_id
)
);
@@ -158,18 +176,22 @@ public:
if (results.RowCount() == 1) {
BlockedSpells e{};
e.id = static_cast<int32_t>(atoi(row[0]));
e.spellid = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.type = static_cast<int8_t>(atoi(row[2]));
e.zoneid = static_cast<int32_t>(atoi(row[3]));
e.x = strtof(row[4], nullptr);
e.y = strtof(row[5], nullptr);
e.z = strtof(row[6], nullptr);
e.x_diff = strtof(row[7], nullptr);
e.y_diff = strtof(row[8], nullptr);
e.z_diff = strtof(row[9], nullptr);
e.message = row[10] ? row[10] : "";
e.description = row[11] ? row[11] : "";
e.id = static_cast<int32_t>(atoi(row[0]));
e.spellid = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.type = static_cast<int8_t>(atoi(row[2]));
e.zoneid = static_cast<int32_t>(atoi(row[3]));
e.x = strtof(row[4], nullptr);
e.y = strtof(row[5], nullptr);
e.z = strtof(row[6], nullptr);
e.x_diff = strtof(row[7], nullptr);
e.y_diff = strtof(row[8], nullptr);
e.z_diff = strtof(row[9], nullptr);
e.message = row[10] ? row[10] : "";
e.description = row[11] ? row[11] : "";
e.min_expansion = static_cast<int8_t>(atoi(row[12]));
e.max_expansion = static_cast<int8_t>(atoi(row[13]));
e.content_flags = row[14] ? row[14] : "";
e.content_flags_disabled = row[15] ? row[15] : "";
return e;
}
@@ -214,6 +236,10 @@ public:
v.push_back(columns[9] + " = " + std::to_string(e.z_diff));
v.push_back(columns[10] + " = '" + Strings::Escape(e.message) + "'");
v.push_back(columns[11] + " = '" + Strings::Escape(e.description) + "'");
v.push_back(columns[12] + " = " + std::to_string(e.min_expansion));
v.push_back(columns[13] + " = " + std::to_string(e.max_expansion));
v.push_back(columns[14] + " = '" + Strings::Escape(e.content_flags) + "'");
v.push_back(columns[15] + " = '" + Strings::Escape(e.content_flags_disabled) + "'");
auto results = db.QueryDatabase(
fmt::format(
@@ -247,6 +273,10 @@ public:
v.push_back(std::to_string(e.z_diff));
v.push_back("'" + Strings::Escape(e.message) + "'");
v.push_back("'" + Strings::Escape(e.description) + "'");
v.push_back(std::to_string(e.min_expansion));
v.push_back(std::to_string(e.max_expansion));
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
auto results = db.QueryDatabase(
fmt::format(
@@ -288,6 +318,10 @@ public:
v.push_back(std::to_string(e.z_diff));
v.push_back("'" + Strings::Escape(e.message) + "'");
v.push_back("'" + Strings::Escape(e.description) + "'");
v.push_back(std::to_string(e.min_expansion));
v.push_back(std::to_string(e.max_expansion));
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -321,18 +355,22 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
BlockedSpells e{};
e.id = static_cast<int32_t>(atoi(row[0]));
e.spellid = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.type = static_cast<int8_t>(atoi(row[2]));
e.zoneid = static_cast<int32_t>(atoi(row[3]));
e.x = strtof(row[4], nullptr);
e.y = strtof(row[5], nullptr);
e.z = strtof(row[6], nullptr);
e.x_diff = strtof(row[7], nullptr);
e.y_diff = strtof(row[8], nullptr);
e.z_diff = strtof(row[9], nullptr);
e.message = row[10] ? row[10] : "";
e.description = row[11] ? row[11] : "";
e.id = static_cast<int32_t>(atoi(row[0]));
e.spellid = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.type = static_cast<int8_t>(atoi(row[2]));
e.zoneid = static_cast<int32_t>(atoi(row[3]));
e.x = strtof(row[4], nullptr);
e.y = strtof(row[5], nullptr);
e.z = strtof(row[6], nullptr);
e.x_diff = strtof(row[7], nullptr);
e.y_diff = strtof(row[8], nullptr);
e.z_diff = strtof(row[9], nullptr);
e.message = row[10] ? row[10] : "";
e.description = row[11] ? row[11] : "";
e.min_expansion = static_cast<int8_t>(atoi(row[12]));
e.max_expansion = static_cast<int8_t>(atoi(row[13]));
e.content_flags = row[14] ? row[14] : "";
e.content_flags_disabled = row[15] ? row[15] : "";
all_entries.push_back(e);
}
@@ -357,18 +395,22 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
BlockedSpells e{};
e.id = static_cast<int32_t>(atoi(row[0]));
e.spellid = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.type = static_cast<int8_t>(atoi(row[2]));
e.zoneid = static_cast<int32_t>(atoi(row[3]));
e.x = strtof(row[4], nullptr);
e.y = strtof(row[5], nullptr);
e.z = strtof(row[6], nullptr);
e.x_diff = strtof(row[7], nullptr);
e.y_diff = strtof(row[8], nullptr);
e.z_diff = strtof(row[9], nullptr);
e.message = row[10] ? row[10] : "";
e.description = row[11] ? row[11] : "";
e.id = static_cast<int32_t>(atoi(row[0]));
e.spellid = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.type = static_cast<int8_t>(atoi(row[2]));
e.zoneid = static_cast<int32_t>(atoi(row[3]));
e.x = strtof(row[4], nullptr);
e.y = strtof(row[5], nullptr);
e.z = strtof(row[6], nullptr);
e.x_diff = strtof(row[7], nullptr);
e.y_diff = strtof(row[8], nullptr);
e.z_diff = strtof(row[9], nullptr);
e.message = row[10] ? row[10] : "";
e.description = row[11] ? row[11] : "";
e.min_expansion = static_cast<int8_t>(atoi(row[12]));
e.max_expansion = static_cast<int8_t>(atoi(row[13]));
e.content_flags = row[14] ? row[14] : "";
e.content_flags_disabled = row[15] ? row[15] : "";
all_entries.push_back(e);
}
@@ -16,12 +16,19 @@
#include "../../strings.h"
#include <ctime>
class BaseBotTimersRepository {
public:
struct BotTimers {
uint32_t bot_id;
uint32_t timer_id;
uint32_t timer_value;
uint32_t recast_time;
uint8_t is_spell;
uint8_t is_disc;
uint32_t spell_id;
uint8_t is_item;
uint32_t item_id;
};
static std::string PrimaryKey()
@@ -35,6 +42,12 @@ public:
"bot_id",
"timer_id",
"timer_value",
"recast_time",
"is_spell",
"is_disc",
"spell_id",
"is_item",
"item_id",
};
}
@@ -44,6 +57,12 @@ public:
"bot_id",
"timer_id",
"timer_value",
"recast_time",
"is_spell",
"is_disc",
"spell_id",
"is_item",
"item_id",
};
}
@@ -87,6 +106,12 @@ public:
e.bot_id = 0;
e.timer_id = 0;
e.timer_value = 0;
e.recast_time = 0;
e.is_spell = 0;
e.is_disc = 0;
e.spell_id = 0;
e.is_item = 0;
e.item_id = 0;
return e;
}
@@ -112,8 +137,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
bot_timers_id
)
);
@@ -125,6 +151,12 @@ public:
e.bot_id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.timer_id = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.timer_value = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
e.recast_time = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
e.is_spell = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
e.is_disc = static_cast<uint8_t>(strtoul(row[5], nullptr, 10));
e.spell_id = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
e.is_item = static_cast<uint8_t>(strtoul(row[7], nullptr, 10));
e.item_id = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
return e;
}
@@ -161,6 +193,12 @@ public:
v.push_back(columns[0] + " = " + std::to_string(e.bot_id));
v.push_back(columns[1] + " = " + std::to_string(e.timer_id));
v.push_back(columns[2] + " = " + std::to_string(e.timer_value));
v.push_back(columns[3] + " = " + std::to_string(e.recast_time));
v.push_back(columns[4] + " = " + std::to_string(e.is_spell));
v.push_back(columns[5] + " = " + std::to_string(e.is_disc));
v.push_back(columns[6] + " = " + std::to_string(e.spell_id));
v.push_back(columns[7] + " = " + std::to_string(e.is_item));
v.push_back(columns[8] + " = " + std::to_string(e.item_id));
auto results = db.QueryDatabase(
fmt::format(
@@ -185,6 +223,12 @@ public:
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.timer_id));
v.push_back(std::to_string(e.timer_value));
v.push_back(std::to_string(e.recast_time));
v.push_back(std::to_string(e.is_spell));
v.push_back(std::to_string(e.is_disc));
v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.is_item));
v.push_back(std::to_string(e.item_id));
auto results = db.QueryDatabase(
fmt::format(
@@ -217,6 +261,12 @@ public:
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.timer_id));
v.push_back(std::to_string(e.timer_value));
v.push_back(std::to_string(e.recast_time));
v.push_back(std::to_string(e.is_spell));
v.push_back(std::to_string(e.is_disc));
v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.is_item));
v.push_back(std::to_string(e.item_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -253,6 +303,12 @@ public:
e.bot_id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.timer_id = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.timer_value = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
e.recast_time = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
e.is_spell = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
e.is_disc = static_cast<uint8_t>(strtoul(row[5], nullptr, 10));
e.spell_id = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
e.is_item = static_cast<uint8_t>(strtoul(row[7], nullptr, 10));
e.item_id = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
all_entries.push_back(e);
}
@@ -280,6 +336,12 @@ public:
e.bot_id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.timer_id = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
e.timer_value = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
e.recast_time = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
e.is_spell = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
e.is_disc = static_cast<uint8_t>(strtoul(row[5], nullptr, 10));
e.spell_id = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
e.is_item = static_cast<uint8_t>(strtoul(row[7], nullptr, 10));
e.item_id = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
all_entries.push_back(e);
}
@@ -75,7 +75,7 @@ public:
int32_t damageshield;
int32_t deity;
int32_t delay;
int32_t augdistiller;
uint32_t augdistiller;
int32_t dotshielding;
int32_t dr;
int32_t clicktype;
@@ -227,7 +227,7 @@ public:
int32_t focusunk5;
std::string focusunk6;
int32_t focusunk7;
int32_t scrollunk1;
uint32_t scrollunk1;
int32_t scrollunk2;
int32_t scrollunk3;
int32_t scrollunk4;
@@ -266,10 +266,10 @@ public:
std::string created;
int16_t elitematerial;
int16_t ldonsellbackrate;
int16_t scriptfileid;
int32_t scriptfileid;
int16_t expendablearrow;
int16_t powersourcecapacity;
int16_t bardeffect;
int32_t powersourcecapacity;
int32_t bardeffect;
int16_t bardeffecttype;
int16_t bardlevel2;
int16_t bardlevel;
@@ -1307,7 +1307,7 @@ public:
e.damageshield = static_cast<int32_t>(atoi(row[52]));
e.deity = static_cast<int32_t>(atoi(row[53]));
e.delay = static_cast<int32_t>(atoi(row[54]));
e.augdistiller = static_cast<int32_t>(atoi(row[55]));
e.augdistiller = static_cast<uint32_t>(strtoul(row[55], nullptr, 10));
e.dotshielding = static_cast<int32_t>(atoi(row[56]));
e.dr = static_cast<int32_t>(atoi(row[57]));
e.clicktype = static_cast<int32_t>(atoi(row[58]));
@@ -1459,7 +1459,7 @@ public:
e.focusunk5 = static_cast<int32_t>(atoi(row[204]));
e.focusunk6 = row[205] ? row[205] : "";
e.focusunk7 = static_cast<int32_t>(atoi(row[206]));
e.scrollunk1 = static_cast<int32_t>(atoi(row[207]));
e.scrollunk1 = static_cast<uint32_t>(strtoul(row[207], nullptr, 10));
e.scrollunk2 = static_cast<int32_t>(atoi(row[208]));
e.scrollunk3 = static_cast<int32_t>(atoi(row[209]));
e.scrollunk4 = static_cast<int32_t>(atoi(row[210]));
@@ -1498,10 +1498,10 @@ public:
e.created = row[243] ? row[243] : "";
e.elitematerial = static_cast<int16_t>(atoi(row[244]));
e.ldonsellbackrate = static_cast<int16_t>(atoi(row[245]));
e.scriptfileid = static_cast<int16_t>(atoi(row[246]));
e.scriptfileid = static_cast<int32_t>(atoi(row[246]));
e.expendablearrow = static_cast<int16_t>(atoi(row[247]));
e.powersourcecapacity = static_cast<int16_t>(atoi(row[248]));
e.bardeffect = static_cast<int16_t>(atoi(row[249]));
e.powersourcecapacity = static_cast<int32_t>(atoi(row[248]));
e.bardeffect = static_cast<int32_t>(atoi(row[249]));
e.bardeffecttype = static_cast<int16_t>(atoi(row[250]));
e.bardlevel2 = static_cast<int16_t>(atoi(row[251]));
e.bardlevel = static_cast<int16_t>(atoi(row[252]));
@@ -2563,7 +2563,7 @@ public:
e.damageshield = static_cast<int32_t>(atoi(row[52]));
e.deity = static_cast<int32_t>(atoi(row[53]));
e.delay = static_cast<int32_t>(atoi(row[54]));
e.augdistiller = static_cast<int32_t>(atoi(row[55]));
e.augdistiller = static_cast<uint32_t>(strtoul(row[55], nullptr, 10));
e.dotshielding = static_cast<int32_t>(atoi(row[56]));
e.dr = static_cast<int32_t>(atoi(row[57]));
e.clicktype = static_cast<int32_t>(atoi(row[58]));
@@ -2715,7 +2715,7 @@ public:
e.focusunk5 = static_cast<int32_t>(atoi(row[204]));
e.focusunk6 = row[205] ? row[205] : "";
e.focusunk7 = static_cast<int32_t>(atoi(row[206]));
e.scrollunk1 = static_cast<int32_t>(atoi(row[207]));
e.scrollunk1 = static_cast<uint32_t>(strtoul(row[207], nullptr, 10));
e.scrollunk2 = static_cast<int32_t>(atoi(row[208]));
e.scrollunk3 = static_cast<int32_t>(atoi(row[209]));
e.scrollunk4 = static_cast<int32_t>(atoi(row[210]));
@@ -2754,10 +2754,10 @@ public:
e.created = row[243] ? row[243] : "";
e.elitematerial = static_cast<int16_t>(atoi(row[244]));
e.ldonsellbackrate = static_cast<int16_t>(atoi(row[245]));
e.scriptfileid = static_cast<int16_t>(atoi(row[246]));
e.scriptfileid = static_cast<int32_t>(atoi(row[246]));
e.expendablearrow = static_cast<int16_t>(atoi(row[247]));
e.powersourcecapacity = static_cast<int16_t>(atoi(row[248]));
e.bardeffect = static_cast<int16_t>(atoi(row[249]));
e.powersourcecapacity = static_cast<int32_t>(atoi(row[248]));
e.bardeffect = static_cast<int32_t>(atoi(row[249]));
e.bardeffecttype = static_cast<int16_t>(atoi(row[250]));
e.bardlevel2 = static_cast<int16_t>(atoi(row[251]));
e.bardlevel = static_cast<int16_t>(atoi(row[252]));
@@ -2872,7 +2872,7 @@ public:
e.damageshield = static_cast<int32_t>(atoi(row[52]));
e.deity = static_cast<int32_t>(atoi(row[53]));
e.delay = static_cast<int32_t>(atoi(row[54]));
e.augdistiller = static_cast<int32_t>(atoi(row[55]));
e.augdistiller = static_cast<uint32_t>(strtoul(row[55], nullptr, 10));
e.dotshielding = static_cast<int32_t>(atoi(row[56]));
e.dr = static_cast<int32_t>(atoi(row[57]));
e.clicktype = static_cast<int32_t>(atoi(row[58]));
@@ -3024,7 +3024,7 @@ public:
e.focusunk5 = static_cast<int32_t>(atoi(row[204]));
e.focusunk6 = row[205] ? row[205] : "";
e.focusunk7 = static_cast<int32_t>(atoi(row[206]));
e.scrollunk1 = static_cast<int32_t>(atoi(row[207]));
e.scrollunk1 = static_cast<uint32_t>(strtoul(row[207], nullptr, 10));
e.scrollunk2 = static_cast<int32_t>(atoi(row[208]));
e.scrollunk3 = static_cast<int32_t>(atoi(row[209]));
e.scrollunk4 = static_cast<int32_t>(atoi(row[210]));
@@ -3063,10 +3063,10 @@ public:
e.created = row[243] ? row[243] : "";
e.elitematerial = static_cast<int16_t>(atoi(row[244]));
e.ldonsellbackrate = static_cast<int16_t>(atoi(row[245]));
e.scriptfileid = static_cast<int16_t>(atoi(row[246]));
e.scriptfileid = static_cast<int32_t>(atoi(row[246]));
e.expendablearrow = static_cast<int16_t>(atoi(row[247]));
e.powersourcecapacity = static_cast<int16_t>(atoi(row[248]));
e.bardeffect = static_cast<int16_t>(atoi(row[249]));
e.powersourcecapacity = static_cast<int32_t>(atoi(row[248]));
e.bardeffect = static_cast<int32_t>(atoi(row[249]));
e.bardeffecttype = static_cast<int16_t>(atoi(row[250]));
e.bardlevel2 = static_cast<int16_t>(atoi(row[251]));
e.bardlevel = static_cast<int16_t>(atoi(row[252]));
@@ -0,0 +1,344 @@
/**
* DO NOT MODIFY THIS FILE
*
* This repository was automatically generated and is NOT to be modified directly.
* Any repository modifications are meant to be made to the repository extending the base.
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
*/
#ifndef EQEMU_BASE_KEYRING_REPOSITORY_H
#define EQEMU_BASE_KEYRING_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseKeyringRepository {
public:
struct Keyring {
uint32_t id;
int32_t char_id;
int32_t item_id;
};
static std::string PrimaryKey()
{
return std::string("id");
}
static std::vector<std::string> Columns()
{
return {
"id",
"char_id",
"item_id",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"id",
"char_id",
"item_id",
};
}
static std::string ColumnsRaw()
{
return std::string(Strings::Implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(Strings::Implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("keyring");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static Keyring NewEntity()
{
Keyring e{};
e.id = 0;
e.char_id = 0;
e.item_id = 0;
return e;
}
static Keyring GetKeyring(
const std::vector<Keyring> &keyrings,
int keyring_id
)
{
for (auto &keyring : keyrings) {
if (keyring.id == keyring_id) {
return keyring;
}
}
return NewEntity();
}
static Keyring FindOne(
Database& db,
int keyring_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
keyring_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
Keyring e{};
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.char_id = static_cast<int32_t>(atoi(row[1]));
e.item_id = static_cast<int32_t>(atoi(row[2]));
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int keyring_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
keyring_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const Keyring &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[1] + " = " + std::to_string(e.char_id));
v.push_back(columns[2] + " = " + std::to_string(e.item_id));
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static Keyring InsertOne(
Database& db,
Keyring e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.char_id));
v.push_back(std::to_string(e.item_id));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", v)
)
);
if (results.Success()) {
e.id = results.LastInsertedID();
return e;
}
e = NewEntity();
return e;
}
static int InsertMany(
Database& db,
const std::vector<Keyring> &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.char_id));
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 {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<Keyring> All(Database& db)
{
std::vector<Keyring> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
Keyring e{};
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.char_id = static_cast<int32_t>(atoi(row[1]));
e.item_id = static_cast<int32_t>(atoi(row[2]));
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<Keyring> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<Keyring> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {}",
BaseSelect(),
where_filter
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
Keyring e{};
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.char_id = static_cast<int32_t>(atoi(row[1]));
e.item_id = static_cast<int32_t>(atoi(row[2]));
all_entries.push_back(e);
}
return all_entries;
}
static int DeleteWhere(Database& db, const std::string &where_filter)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {}",
TableName(),
where_filter
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int Truncate(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"TRUNCATE TABLE {}",
TableName()
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int64 GetMaxId(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COALESCE(MAX({}), 0) FROM {}",
PrimaryKey(),
TableName()
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static int64 Count(Database& db, const std::string &where_filter = "")
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COUNT(*) FROM {} {}",
TableName(),
(where_filter.empty() ? "" : "WHERE " + where_filter)
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
};
#endif //EQEMU_BASE_KEYRING_REPOSITORY_H
@@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseObjectRepository {
public:
struct Object {
@@ -31,9 +32,9 @@ public:
std::string objectname;
int32_t type;
int32_t icon;
int32_t unknown08;
int32_t unknown10;
int32_t unknown20;
float size_percentage;
int32_t solid_type;
int32_t incline;
int32_t unknown24;
int32_t unknown60;
int32_t unknown64;
@@ -71,9 +72,9 @@ public:
"objectname",
"type",
"icon",
"unknown08",
"unknown10",
"unknown20",
"size_percentage",
"solid_type",
"incline",
"unknown24",
"unknown60",
"unknown64",
@@ -107,9 +108,9 @@ public:
"objectname",
"type",
"icon",
"unknown08",
"unknown10",
"unknown20",
"size_percentage",
"solid_type",
"incline",
"unknown24",
"unknown60",
"unknown64",
@@ -177,9 +178,9 @@ public:
e.objectname = "";
e.type = 0;
e.icon = 0;
e.unknown08 = 0;
e.unknown10 = 0;
e.unknown20 = 0;
e.size_percentage = 0;
e.solid_type = 0;
e.incline = 0;
e.unknown24 = 0;
e.unknown60 = 0;
e.unknown64 = 0;
@@ -220,8 +221,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
object_id
)
);
@@ -242,9 +244,9 @@ public:
e.objectname = row[9] ? row[9] : "";
e.type = static_cast<int32_t>(atoi(row[10]));
e.icon = static_cast<int32_t>(atoi(row[11]));
e.unknown08 = static_cast<int32_t>(atoi(row[12]));
e.unknown10 = static_cast<int32_t>(atoi(row[13]));
e.unknown20 = static_cast<int32_t>(atoi(row[14]));
e.size_percentage = strtof(row[12], nullptr);
e.solid_type = static_cast<int32_t>(atoi(row[13]));
e.incline = static_cast<int32_t>(atoi(row[14]));
e.unknown24 = static_cast<int32_t>(atoi(row[15]));
e.unknown60 = static_cast<int32_t>(atoi(row[16]));
e.unknown64 = static_cast<int32_t>(atoi(row[17]));
@@ -304,9 +306,9 @@ public:
v.push_back(columns[9] + " = '" + Strings::Escape(e.objectname) + "'");
v.push_back(columns[10] + " = " + std::to_string(e.type));
v.push_back(columns[11] + " = " + std::to_string(e.icon));
v.push_back(columns[12] + " = " + std::to_string(e.unknown08));
v.push_back(columns[13] + " = " + std::to_string(e.unknown10));
v.push_back(columns[14] + " = " + std::to_string(e.unknown20));
v.push_back(columns[12] + " = " + std::to_string(e.size_percentage));
v.push_back(columns[13] + " = " + std::to_string(e.solid_type));
v.push_back(columns[14] + " = " + std::to_string(e.incline));
v.push_back(columns[15] + " = " + std::to_string(e.unknown24));
v.push_back(columns[16] + " = " + std::to_string(e.unknown60));
v.push_back(columns[17] + " = " + std::to_string(e.unknown64));
@@ -355,9 +357,9 @@ public:
v.push_back("'" + Strings::Escape(e.objectname) + "'");
v.push_back(std::to_string(e.type));
v.push_back(std::to_string(e.icon));
v.push_back(std::to_string(e.unknown08));
v.push_back(std::to_string(e.unknown10));
v.push_back(std::to_string(e.unknown20));
v.push_back(std::to_string(e.size_percentage));
v.push_back(std::to_string(e.solid_type));
v.push_back(std::to_string(e.incline));
v.push_back(std::to_string(e.unknown24));
v.push_back(std::to_string(e.unknown60));
v.push_back(std::to_string(e.unknown64));
@@ -414,9 +416,9 @@ public:
v.push_back("'" + Strings::Escape(e.objectname) + "'");
v.push_back(std::to_string(e.type));
v.push_back(std::to_string(e.icon));
v.push_back(std::to_string(e.unknown08));
v.push_back(std::to_string(e.unknown10));
v.push_back(std::to_string(e.unknown20));
v.push_back(std::to_string(e.size_percentage));
v.push_back(std::to_string(e.solid_type));
v.push_back(std::to_string(e.incline));
v.push_back(std::to_string(e.unknown24));
v.push_back(std::to_string(e.unknown60));
v.push_back(std::to_string(e.unknown64));
@@ -477,9 +479,9 @@ public:
e.objectname = row[9] ? row[9] : "";
e.type = static_cast<int32_t>(atoi(row[10]));
e.icon = static_cast<int32_t>(atoi(row[11]));
e.unknown08 = static_cast<int32_t>(atoi(row[12]));
e.unknown10 = static_cast<int32_t>(atoi(row[13]));
e.unknown20 = static_cast<int32_t>(atoi(row[14]));
e.size_percentage = strtof(row[12], nullptr);
e.solid_type = static_cast<int32_t>(atoi(row[13]));
e.incline = static_cast<int32_t>(atoi(row[14]));
e.unknown24 = static_cast<int32_t>(atoi(row[15]));
e.unknown60 = static_cast<int32_t>(atoi(row[16]));
e.unknown64 = static_cast<int32_t>(atoi(row[17]));
@@ -531,9 +533,9 @@ public:
e.objectname = row[9] ? row[9] : "";
e.type = static_cast<int32_t>(atoi(row[10]));
e.icon = static_cast<int32_t>(atoi(row[11]));
e.unknown08 = static_cast<int32_t>(atoi(row[12]));
e.unknown10 = static_cast<int32_t>(atoi(row[13]));
e.unknown20 = static_cast<int32_t>(atoi(row[14]));
e.size_percentage = strtof(row[12], nullptr);
e.solid_type = static_cast<int32_t>(atoi(row[13]));
e.incline = static_cast<int32_t>(atoi(row[14]));
e.unknown24 = static_cast<int32_t>(atoi(row[15]));
e.unknown60 = static_cast<int32_t>(atoi(row[16]));
e.unknown64 = static_cast<int32_t>(atoi(row[17]));
@@ -16,18 +16,19 @@
#include "../../strings.h"
#include <ctime>
class BaseStartingItemsRepository {
public:
struct StartingItems {
uint32_t id;
int32_t race;
int32_t class_;
int32_t deityid;
int32_t zoneid;
int32_t itemid;
std::string class_list;
std::string race_list;
std::string deity_list;
std::string zone_id_list;
uint32_t item_id;
uint8_t item_charges;
int8_t gm;
int32_t slot;
int32_t status;
int32_t inventory_slot;
int8_t min_expansion;
int8_t max_expansion;
std::string content_flags;
@@ -43,14 +44,14 @@ public:
{
return {
"id",
"race",
"`class`",
"deityid",
"zoneid",
"itemid",
"class_list",
"race_list",
"deity_list",
"zone_id_list",
"item_id",
"item_charges",
"gm",
"slot",
"status",
"inventory_slot",
"min_expansion",
"max_expansion",
"content_flags",
@@ -62,14 +63,14 @@ public:
{
return {
"id",
"race",
"`class`",
"deityid",
"zoneid",
"itemid",
"class_list",
"race_list",
"deity_list",
"zone_id_list",
"item_id",
"item_charges",
"gm",
"slot",
"status",
"inventory_slot",
"min_expansion",
"max_expansion",
"content_flags",
@@ -115,14 +116,14 @@ public:
StartingItems e{};
e.id = 0;
e.race = 0;
e.class_ = 0;
e.deityid = 0;
e.zoneid = 0;
e.itemid = 0;
e.class_list = "";
e.race_list = "";
e.deity_list = "";
e.zone_id_list = "";
e.item_id = 0;
e.item_charges = 1;
e.gm = 0;
e.slot = -1;
e.status = 0;
e.inventory_slot = -1;
e.min_expansion = -1;
e.max_expansion = -1;
e.content_flags = "";
@@ -152,8 +153,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
starting_items_id
)
);
@@ -163,14 +165,14 @@ public:
StartingItems e{};
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.race = static_cast<int32_t>(atoi(row[1]));
e.class_ = static_cast<int32_t>(atoi(row[2]));
e.deityid = static_cast<int32_t>(atoi(row[3]));
e.zoneid = static_cast<int32_t>(atoi(row[4]));
e.itemid = static_cast<int32_t>(atoi(row[5]));
e.class_list = row[1] ? row[1] : "";
e.race_list = row[2] ? row[2] : "";
e.deity_list = row[3] ? row[3] : "";
e.zone_id_list = row[4] ? row[4] : "";
e.item_id = static_cast<uint32_t>(strtoul(row[5], nullptr, 10));
e.item_charges = static_cast<uint8_t>(strtoul(row[6], nullptr, 10));
e.gm = static_cast<int8_t>(atoi(row[7]));
e.slot = static_cast<int32_t>(atoi(row[8]));
e.status = static_cast<int32_t>(atoi(row[7]));
e.inventory_slot = static_cast<int32_t>(atoi(row[8]));
e.min_expansion = static_cast<int8_t>(atoi(row[9]));
e.max_expansion = static_cast<int8_t>(atoi(row[10]));
e.content_flags = row[11] ? row[11] : "";
@@ -208,14 +210,14 @@ public:
auto columns = Columns();
v.push_back(columns[1] + " = " + std::to_string(e.race));
v.push_back(columns[2] + " = " + std::to_string(e.class_));
v.push_back(columns[3] + " = " + std::to_string(e.deityid));
v.push_back(columns[4] + " = " + std::to_string(e.zoneid));
v.push_back(columns[5] + " = " + std::to_string(e.itemid));
v.push_back(columns[1] + " = '" + Strings::Escape(e.class_list) + "'");
v.push_back(columns[2] + " = '" + Strings::Escape(e.race_list) + "'");
v.push_back(columns[3] + " = '" + Strings::Escape(e.deity_list) + "'");
v.push_back(columns[4] + " = '" + Strings::Escape(e.zone_id_list) + "'");
v.push_back(columns[5] + " = " + std::to_string(e.item_id));
v.push_back(columns[6] + " = " + std::to_string(e.item_charges));
v.push_back(columns[7] + " = " + std::to_string(e.gm));
v.push_back(columns[8] + " = " + std::to_string(e.slot));
v.push_back(columns[7] + " = " + std::to_string(e.status));
v.push_back(columns[8] + " = " + std::to_string(e.inventory_slot));
v.push_back(columns[9] + " = " + std::to_string(e.min_expansion));
v.push_back(columns[10] + " = " + std::to_string(e.max_expansion));
v.push_back(columns[11] + " = '" + Strings::Escape(e.content_flags) + "'");
@@ -242,14 +244,14 @@ public:
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.race));
v.push_back(std::to_string(e.class_));
v.push_back(std::to_string(e.deityid));
v.push_back(std::to_string(e.zoneid));
v.push_back(std::to_string(e.itemid));
v.push_back("'" + Strings::Escape(e.class_list) + "'");
v.push_back("'" + Strings::Escape(e.race_list) + "'");
v.push_back("'" + Strings::Escape(e.deity_list) + "'");
v.push_back("'" + Strings::Escape(e.zone_id_list) + "'");
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_charges));
v.push_back(std::to_string(e.gm));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.status));
v.push_back(std::to_string(e.inventory_slot));
v.push_back(std::to_string(e.min_expansion));
v.push_back(std::to_string(e.max_expansion));
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
@@ -284,14 +286,14 @@ public:
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.race));
v.push_back(std::to_string(e.class_));
v.push_back(std::to_string(e.deityid));
v.push_back(std::to_string(e.zoneid));
v.push_back(std::to_string(e.itemid));
v.push_back("'" + Strings::Escape(e.class_list) + "'");
v.push_back("'" + Strings::Escape(e.race_list) + "'");
v.push_back("'" + Strings::Escape(e.deity_list) + "'");
v.push_back("'" + Strings::Escape(e.zone_id_list) + "'");
v.push_back(std::to_string(e.item_id));
v.push_back(std::to_string(e.item_charges));
v.push_back(std::to_string(e.gm));
v.push_back(std::to_string(e.slot));
v.push_back(std::to_string(e.status));
v.push_back(std::to_string(e.inventory_slot));
v.push_back(std::to_string(e.min_expansion));
v.push_back(std::to_string(e.max_expansion));
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
@@ -330,14 +332,14 @@ public:
StartingItems e{};
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.race = static_cast<int32_t>(atoi(row[1]));
e.class_ = static_cast<int32_t>(atoi(row[2]));
e.deityid = static_cast<int32_t>(atoi(row[3]));
e.zoneid = static_cast<int32_t>(atoi(row[4]));
e.itemid = static_cast<int32_t>(atoi(row[5]));
e.class_list = row[1] ? row[1] : "";
e.race_list = row[2] ? row[2] : "";
e.deity_list = row[3] ? row[3] : "";
e.zone_id_list = row[4] ? row[4] : "";
e.item_id = static_cast<uint32_t>(strtoul(row[5], nullptr, 10));
e.item_charges = static_cast<uint8_t>(strtoul(row[6], nullptr, 10));
e.gm = static_cast<int8_t>(atoi(row[7]));
e.slot = static_cast<int32_t>(atoi(row[8]));
e.status = static_cast<int32_t>(atoi(row[7]));
e.inventory_slot = static_cast<int32_t>(atoi(row[8]));
e.min_expansion = static_cast<int8_t>(atoi(row[9]));
e.max_expansion = static_cast<int8_t>(atoi(row[10]));
e.content_flags = row[11] ? row[11] : "";
@@ -367,14 +369,14 @@ public:
StartingItems e{};
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
e.race = static_cast<int32_t>(atoi(row[1]));
e.class_ = static_cast<int32_t>(atoi(row[2]));
e.deityid = static_cast<int32_t>(atoi(row[3]));
e.zoneid = static_cast<int32_t>(atoi(row[4]));
e.itemid = static_cast<int32_t>(atoi(row[5]));
e.class_list = row[1] ? row[1] : "";
e.race_list = row[2] ? row[2] : "";
e.deity_list = row[3] ? row[3] : "";
e.zone_id_list = row[4] ? row[4] : "";
e.item_id = static_cast<uint32_t>(strtoul(row[5], nullptr, 10));
e.item_charges = static_cast<uint8_t>(strtoul(row[6], nullptr, 10));
e.gm = static_cast<int8_t>(atoi(row[7]));
e.slot = static_cast<int32_t>(atoi(row[8]));
e.status = static_cast<int32_t>(atoi(row[7]));
e.inventory_slot = static_cast<int32_t>(atoi(row[8]));
e.min_expansion = static_cast<int8_t>(atoi(row[9]));
e.max_expansion = static_cast<int8_t>(atoi(row[10]));
e.content_flags = row[11] ? row[11] : "";
@@ -120,6 +120,7 @@ public:
{.parent_command = "set", .sub_command = "title_suffix", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "titlesuffix"},
{.parent_command = "set", .sub_command = "weather", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "weather"},
{.parent_command = "set", .sub_command = "zone", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "zclip|zcolor|zheader|zonelock|zsafecoords|zsky|zunderworld"},
{.parent_command = "show", .sub_command = "aas", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "showaas"},
{.parent_command = "show", .sub_command = "aa_points", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "showaapoints|showaapts"},
{.parent_command = "show", .sub_command = "aggro", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "aggro"},
{.parent_command = "show", .sub_command = "buffs", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "showbuffs"},
+50
View File
@@ -0,0 +1,50 @@
#ifndef EQEMU_KEYRING_REPOSITORY_H
#define EQEMU_KEYRING_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_keyring_repository.h"
class KeyringRepository: public BaseKeyringRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* KeyringRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* KeyringRepository::GetWhereNeverExpires()
* KeyringRepository::GetWhereXAndY()
* KeyringRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
};
#endif //EQEMU_KEYRING_REPOSITORY_H
+10
View File
@@ -304,6 +304,8 @@ RULE_BOOL(World, EnableDevTools, true, "Enable or Disable the Developer Tools gl
RULE_BOOL(World, EnableChecksumVerification, false, "Enable or Disable the Checksum Verification for eqgame.exe and spells_us.txt")
RULE_INT(World, MaximumQuestErrors, 30, "Changes the maximum number of quest errors that can be displayed in #questerrors, default is 30")
RULE_INT(World, BootHour, 0, "Sets the in-game hour world will set when it first boots. 0-24 are valid options, where 0 disables this rule")
RULE_BOOL(World, UseItemLinksForKeyRing, false, "Uses item links for Key Ring Listing instead of item name")
RULE_BOOL(World, UseOldShadowKnightClassExport, true, "Disable to have Shadowknight show as Shadow Knight (live-like)")
RULE_CATEGORY_END()
RULE_CATEGORY(Zone)
@@ -457,6 +459,8 @@ RULE_INT(Spells, WizardCritMinimumRandomRatio, 20, "The minimum value for the ra
RULE_INT(Spells, WizardCritMaximumRandomRatio, 70, "The maximum value for the random range which Wizards and Caster DPS Mercs innately have for spell crit ratio. Set to 70 for vanilla values.")
RULE_INT(Spells, DefensiveProcPenaltyLevelGap, 6, "Defensive Proc Penalty Level Gap where procs start losing their proc rate at RuleR(Spells, DefensiveProcPenaltyModifier)% per level difference")
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_CATEGORY_END()
RULE_CATEGORY(Combat)
@@ -649,6 +653,12 @@ RULE_BOOL(Bots, ResurrectionSickness, true, "Use Resurrection Sickness based on
RULE_INT(Bots, OldResurrectionSicknessSpell, 757, "757 is Default Old Resurrection Sickness Spell")
RULE_INT(Bots, ResurrectionSicknessSpell, 756, "756 is Default Resurrection Sickness Spell")
RULE_BOOL(Bots, AllowPickpocketCommand, true, "Allows the use of the bot command 'pickpocket'")
RULE_BOOL(Bots, BotHealOnLevel, false, "Setting whether a bot should heal completely when leveling. Default FALSE.")
RULE_INT(Bots, AutosaveIntervalSeconds, 300, "Number of seconds after which a timer is triggered which stores the bot data. The value 0 means no periodic automatic saving.")
RULE_BOOL(Bots, CazicTouchBotsOwner, true, "Default True. Cazic Touch/DT will hit bot owner rather than bot.")
RULE_INT(Bots, BotsClickItemsMinLvl, 1, "Minimum level for bots to be able to use ^clickitem. Default 1.")
RULE_BOOL(Bots, BotsCanClickItems, true, "Enabled the ability for bots to click items they have equipped. Default TRUE")
RULE_BOOL(Bots, CanClickMageEpicV1, true, "Whether or not bots are allowed to click Mage Epic 1.0. Default TRUE")
RULE_CATEGORY_END()
RULE_CATEGORY(Chat)
+6 -6
View File
@@ -461,12 +461,12 @@ struct SPackSendQueue {
uchar buffer[0];
};
struct ServerZoneStateChange_struct {
uint32 ZoneServerID;
char adminname[64];
uint32 zoneid;
uint16 instanceid;
bool makestatic;
struct ServerZoneStateChange_Struct {
uint32 zone_server_id;
uint32 zone_id;
uint16 instance_id;
bool is_static;
char admin_name[64];
};
struct ServerZoneIncomingClient_Struct {
+69 -32
View File
@@ -41,6 +41,7 @@
#include "repositories/criteria/content_filter_criteria.h"
#include "repositories/account_repository.h"
#include "repositories/faction_association_repository.h"
#include "repositories/starting_items_repository.h"
#include "path_manager.h"
#include "repositories/loottable_repository.h"
@@ -446,45 +447,81 @@ bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) {
return true;
}
bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, EQ::InventoryProfile* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin_level) {
bool SharedDatabase::SetStartingItems(
PlayerProfile_Struct *pp,
EQ::InventoryProfile *inv,
uint32 si_race,
uint32 si_class,
uint32 si_deity,
uint32 si_current_zone,
char *si_name,
int admin_level
)
{
const EQ::ItemData *item_data;
const EQ::ItemData *myitem;
const auto &l = StartingItemsRepository::All(*this);
const std::string query = StringFormat(
"SELECT itemid, item_charges, slot FROM starting_items "
"WHERE (race = %i or race = 0) AND (class = %i or class = 0) AND "
"(deityid = %i or deityid = 0) AND (zoneid = %i or zoneid = 0) AND "
"gm <= %i %s ORDER BY id",
si_race,
si_class,
si_deity,
si_current_zone,
admin_level,
ContentFilterCriteria::apply().c_str()
);
auto results = QueryDatabase(query);
if (!results.Success()) {
if (l.empty()) {
return false;
}
std::vector<StartingItemsRepository::StartingItems> v;
for (auto& row = results.begin(); row != results.end(); ++row) {
const int32 itemid = Strings::ToInt(row[0]);
const int32 charges = Strings::ToInt(row[1]);
int32 slot = Strings::ToInt(row[2]);
myitem = GetItem(itemid);
for (const auto &e : l) {
const auto &classes = Strings::Split(e.class_list, "|");
const auto &deities = Strings::Split(e.deity_list, "|");
const auto &races = Strings::Split(e.race_list, "|");
const auto &zones = Strings::Split(e.zone_id_list, "|");
if(!myitem)
const std::string &all = std::to_string(0);
if (classes[0] != all) {
if (!Strings::Contains(classes, std::to_string(si_class))) {
continue;
}
}
if (deities[0] != all) {
if (!Strings::Contains(deities, std::to_string(si_deity))) {
continue;
}
}
if (races[0] != all) {
if (!Strings::Contains(races, std::to_string(si_race))) {
continue;
}
}
if (zones[0] != all) {
if (!Strings::Contains(zones, std::to_string(si_current_zone))) {
continue;
}
}
v.emplace_back(e);
}
for (const auto &e : v) {
const uint32 item_id = e.item_id;
const uint8 item_charges = e.item_charges;
int32 slot = e.inventory_slot;
item_data = GetItem(item_id);
if (!item_data) {
continue;
}
const EQ::ItemInstance* myinst = CreateBaseItem(myitem, charges);
const auto *inst = CreateBaseItem(item_data, item_charges);
if(slot < 0)
slot = inv->FindFreeSlot(0, 0);
if (slot < EQ::invslot::slotCharm) {
slot = inv->FindFreeSlot(false, false);
}
inv->PutItem(slot, *myinst);
safe_delete(myinst);
inv->PutItem(slot, *inst);
safe_delete(inst);
}
return true;
@@ -1771,7 +1808,7 @@ bool SharedDatabase::LoadSkillCaps(const std::string &prefix) {
}
void SharedDatabase::LoadSkillCaps(void *data) {
const uint32 class_count = PLAYER_CLASS_COUNT;
const uint32 class_count = Class::PLAYER_CLASS_COUNT;
const uint32 skill_count = EQ::skills::HIGHEST_SKILL + 1;
const uint32 level_count = HARD_LEVEL_CAP + 1;
uint16 *skill_caps_table = static_cast<uint16*>(data);
@@ -1811,7 +1848,7 @@ uint16 SharedDatabase::GetSkillCap(uint8 Class_, EQ::skills::SkillType Skill, ui
SkillMaxLevel = RuleI(Character, MaxLevel);
}
const uint32 class_count = PLAYER_CLASS_COUNT;
const uint32 class_count = Class::PLAYER_CLASS_COUNT;
const uint32 skill_count = EQ::skills::HIGHEST_SKILL + 1;
const uint32 level_count = HARD_LEVEL_CAP + 1;
if(Class_ > class_count || static_cast<uint32>(Skill) > skill_count || Level > level_count) {
@@ -1841,7 +1878,7 @@ uint8 SharedDatabase::GetTrainLevel(uint8 Class_, EQ::skills::SkillType Skill, u
SkillMaxLevel = RuleI(Character, MaxLevel);
}
const uint32 class_count = PLAYER_CLASS_COUNT;
const uint32 class_count = Class::PLAYER_CLASS_COUNT;
const uint32 skill_count = EQ::skills::HIGHEST_SKILL + 1;
const uint32 level_count = HARD_LEVEL_CAP + 1;
if(Class_ > class_count || static_cast<uint32>(Skill) > skill_count || Level > level_count) {
@@ -2024,7 +2061,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
sp[tempid].environment_type=Strings::ToInt(row[102]);
sp[tempid].time_of_day=Strings::ToInt(row[103]);
for(y=0; y < PLAYER_CLASS_COUNT;y++)
for(y=0; y < Class::PLAYER_CLASS_COUNT;y++)
sp[tempid].classes[y]=Strings::ToInt(row[104+y]);
sp[tempid].casting_animation=Strings::ToInt(row[120]);
+3 -3
View File
@@ -666,7 +666,7 @@ bool IsBardSong(uint16 spell_id)
const auto& spell = spells[spell_id];
if (
spell.classes[BARD - 1] < UINT8_MAX &&
spell.classes[Class::Bard - 1] < UINT8_MAX &&
!spell.is_discipline
) {
return true;
@@ -780,7 +780,7 @@ uint8 GetSpellMinimumLevel(uint16 spell_id)
const auto& spell = spells[spell_id];
for (int i = 0; i < PLAYER_CLASS_COUNT; i++) {
for (int i = 0; i < Class::PLAYER_CLASS_COUNT; i++) {
if (spell.classes[i] < minimum_level) {
minimum_level = spell.classes[i];
}
@@ -798,7 +798,7 @@ uint8 GetSpellLevel(uint16 spell_id, uint8 class_id)
return UINT8_MAX;
}
if (class_id >= PLAYER_CLASS_COUNT) {
if (class_id >= Class::PLAYER_CLASS_COUNT) {
return UINT8_MAX;
}
+1 -1
View File
@@ -1369,7 +1369,7 @@ struct SPDat_Spell_Struct
/* 101 */ int8 zone_type; // 01=Outdoors, 02=dungeons, ff=Any -- ZONETYPE
/* 102 */ int8 environment_type; // -- ENVIRONMENTTYPE
/* 103 */ int8 time_of_day; // -- TIMEOFDAY
/* 104 */ uint8 classes[PLAYER_CLASS_COUNT]; // Classes, and their min levels -- WARRIORMIN ... BERSERKERMIN
/* 104 */ uint8 classes[Class::PLAYER_CLASS_COUNT]; // Classes, and their min levels -- WARRIORMIN ... BERSERKERMIN
/* 120 */ uint8 casting_animation; // -- CASTINGANIM
/* 121 */ //uint8 TargetAnim; // -- TARGETANIM
/* 122 */ //uint32 TravelType; // -- TRAVELTYPE
+3 -3
View File
@@ -25,7 +25,7 @@
// Build variables
// these get injected during the build pipeline
#define CURRENT_VERSION "22.34.2-dev" // always append -dev to the current version for custom-builds
#define CURRENT_VERSION "22.37.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,9 +42,9 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9242
#define CURRENT_BINARY_DATABASE_VERSION 9247
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9040
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9041
#endif
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "eqemu-server",
"version": "22.34.2",
"version": "22.37.0",
"repository": {
"type": "git",
"url": "https://github.com/EQEmu/Server.git"
+1 -1
View File
@@ -29,7 +29,7 @@ void LoadSkillCaps(SharedDatabase *database, const std::string &prefix) {
EQ::IPCMutex mutex("skill_caps");
mutex.Lock();
uint32 class_count = PLAYER_CLASS_COUNT;
uint32 class_count = Class::PLAYER_CLASS_COUNT;
uint32 skill_count = EQ::skills::HIGHEST_SKILL + 1;
uint32 level_count = HARD_LEVEL_CAP + 1;
uint32 size = (class_count * skill_count * level_count * sizeof(uint16));
+2 -2
View File
@@ -15,8 +15,8 @@ mkdir -p build && cd build && \
-DEQEMU_BUILD_LOGIN=ON \
-DEQEMU_BUILD_LUA=ON \
-DEQEMU_BUILD_PERL=ON \
-DCMAKE_CXX_FLAGS:STRING="-O1 -g" \
-DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING="-O1 -g" \
-DCMAKE_CXX_FLAGS:STRING="-O1 -g -Wno-everything" \
-DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING="-O1 -g -Wno-everything" \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-G 'Unix Makefiles' \
.. && make -j$((`nproc`-4))
+28 -11
View File
@@ -2,8 +2,13 @@
########################################################################
#::: 13th floor import script
#::: Current Source: http://items.sodeq.org/download.php
#::: Authors: (Natedog, Akkadius)
#::: Items Source: http://items.sodeq.org/download.php
#::: How To:
#::: 1. Place this script with your server eqemu_config.json
#::: 2. Download the items.txt file and place with this script
#::: 3. Run this script with Perl
#::: 4. Review new and updated items in the items_new table
#::: 5. Make any desired changes and replace the items table contents
########################################################################
use DBI;
@@ -16,6 +21,7 @@ my $db_user = "";
my $db_pass = "";
my $total_items = 0;
my $read_items_file = "items.txt"; #default
my $keep_temp_items_table = 0; #keeps the imported items table
read_eqemu_config_json();
@@ -24,7 +30,7 @@ my $dbh = DBI->connect("DBI:mysql:database=$db_name;host=$db_host;port=$db_port"
read_items_file_from_13th_floor_text();
update_items_table();
print "\n\nImport complete!\n\n";
print "\n\nImport complete! Review items_new table before replacing into items table.\n\n";
sub read_eqemu_config_json {
use JSON;
@@ -139,7 +145,12 @@ sub read_items_file_from_13th_floor_text {
sub update_items_table {
print "Updating items table...\n";
#::: Establish items_new table
print "Setting up new items table...\n";
$dbh->do("DROP TABLE IF EXISTS items_new");
$dbh->do("CREATE TABLE items_new AS SELECT * FROM items");
print "Updating new items table...\n";
my @matching_table;
my @missing_items_table;
@@ -147,7 +158,7 @@ sub update_items_table {
print "Comparing table structure...\n";
#::: Get columns from `items`
my $sth = $dbh->prepare("SHOW COLUMNS FROM `items`;");
my $sth = $dbh->prepare("SHOW COLUMNS FROM `items_new`;");
$sth->execute();
my @items_table;
while (my @row = $sth->fetchrow_array()) {
@@ -230,7 +241,7 @@ sub update_items_table {
}
my $update_query = "
INSERT INTO items (" . $items_field_list . ")
INSERT INTO items_new (" . $items_field_list . ")
SELECT " . $items_floor_field_list . "
FROM items_floor fi
ON DUPLICATE KEY UPDATE " . $update_fields;
@@ -257,14 +268,20 @@ sub update_items_table {
#::: Update stackables
print "Updating stackable field...\n";
$dbh->do("UPDATE items i SET i.stackable = 1 WHERE i.stacksize > 1");
$dbh->do("UPDATE items_new i SET i.stackable = 1 WHERE i.stacksize > 1");
#::: Update legacy research tome bagtypes
print "Updating legacy research tomes...\n";
$dbh->do("UPDATE items i SET i.bagtype = 24 WHERE i.id IN (17655, 17903)"); #RESEARCHWIZ
$dbh->do("UPDATE items i SET i.bagtype = 25 WHERE i.id IN (17502, 17653)"); #RESEARCHMAG
$dbh->do("UPDATE items i SET i.bagtype = 26 WHERE i.id IN (17501, 17654)"); #RESEARCHNEC
$dbh->do("UPDATE items i SET i.bagtype = 27 WHERE i.id IN (17500, 17652)"); #RESEARCHENC
$dbh->do("UPDATE items_new i SET i.bagtype = 24 WHERE i.id IN (17655, 17903)"); #RESEARCHWIZ
$dbh->do("UPDATE items_new i SET i.bagtype = 25 WHERE i.id IN (17502, 17653)"); #RESEARCHMAG
$dbh->do("UPDATE items_new i SET i.bagtype = 26 WHERE i.id IN (17501, 17654)"); #RESEARCHNEC
$dbh->do("UPDATE items_new i SET i.bagtype = 27 WHERE i.id IN (17500, 17652)"); #RESEARCHENC
#::: Remove temp table
if (!$keep_temp_items_table) {
print "Cleaning up temp items table...\n";
$dbh->do("DROP TABLE items_floor");
}
}
sub trim($) {
+4
View File
@@ -78,6 +78,10 @@ ADD_EXECUTABLE(world ${world_sources} ${world_headers})
INSTALL(TARGETS world RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
IF (WIN32 AND EQEMU_BUILD_PCH)
TARGET_PRECOMPILE_HEADERS(world PRIVATE ../common/pch/pch.h)
ENDIF ()
ADD_DEFINITIONS(-DWORLD)
TARGET_LINK_LIBRARIES(world ${SERVER_LIBS})
+5 -5
View File
@@ -1966,7 +1966,7 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc)
{ /*Drakkin*/ 70, 80, 85, 75, 80, 85, 75}
};
static const int BaseClass[PLAYER_CLASS_COUNT][8] =
static const int BaseClass[Class::PLAYER_CLASS_COUNT][8] =
{ /* STR STA AGI DEX WIS INT CHR ADD*/
{ /*Warrior*/ 10, 10, 5, 0, 0, 0, 0, 25},
{ /*Cleric*/ 5, 5, 0, 0, 10, 0, 0, 30},
@@ -1986,7 +1986,7 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc)
{ /*Berserker*/ 10, 5, 0, 10, 0, 0, 0, 25}
};
static const bool ClassRaceLookupTable[PLAYER_CLASS_COUNT][_TABLE_RACES]=
static const bool ClassRaceLookupTable[Class::PLAYER_CLASS_COUNT][_TABLE_RACES]=
{ /*Human Barbarian Erudite Woodelf Highelf Darkelf Halfelf Dwarf Troll Ogre Halfling Gnome Iksar Vahshir Froglok Drakkin*/
{ /*Warrior*/ true, true, false, true, false, true, true, true, true, true, true, true, true, true, true, true},
{ /*Cleric*/ true, false, true, false, true, true, true, true, false, false, true, true, false, false, true, true},
@@ -2021,7 +2021,7 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc)
// if out of range looking it up in the table would crash stuff
// so we return from these
if (classtemp >= PLAYER_CLASS_COUNT) {
if (classtemp >= Class::PLAYER_CLASS_COUNT) {
LogInfo(" class is out of range");
return false;
}
@@ -2114,7 +2114,7 @@ void Client::SetClassStartingSkills(PlayerProfile_Struct *pp)
}
}
if (cle->GetClientVersion() < static_cast<uint8>(EQ::versions::ClientVersion::RoF2) && pp->class_ == BERSERKER) {
if (cle->GetClientVersion() < static_cast<uint8>(EQ::versions::ClientVersion::RoF2) && pp->class_ == Class::Berserker) {
pp->skills[EQ::skills::Skill1HPiercing] = pp->skills[EQ::skills::Skill2HPiercing];
pp->skills[EQ::skills::Skill2HPiercing] = 0;
}
@@ -2299,7 +2299,7 @@ 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 ROGUE:
case Class::Rogue:
pp->languages[LANG_THIEVES_CANT] = 100;
break;
default:
+10 -10
View File
@@ -578,25 +578,25 @@ void ConsoleZoneShutdown(
strcpy(&tmpname[1], connection->UserName().c_str());
auto pack = new ServerPacket;
pack->size = sizeof(ServerZoneStateChange_struct);
pack->size = sizeof(ServerZoneStateChange_Struct);
pack->pBuffer = new uchar[pack->size];
memset(pack->pBuffer, 0, sizeof(ServerZoneStateChange_struct));
ServerZoneStateChange_struct *s = (ServerZoneStateChange_struct *) pack->pBuffer;
memset(pack->pBuffer, 0, sizeof(ServerZoneStateChange_Struct));
auto *s = (ServerZoneStateChange_Struct *) pack->pBuffer;
pack->opcode = ServerOP_ZoneShutdown;
strcpy(s->adminname, tmpname);
strcpy(s->admin_name, tmpname);
if (Strings::IsNumber(args[0])) {
s->ZoneServerID = Strings::ToInt(args[0]);
s->zone_server_id = Strings::ToInt(args[0]);
}
else {
s->zoneid = ZoneID(args[0].c_str());
s->zone_id = ZoneID(args[0].c_str());
}
ZoneServer *zs = 0;
if (s->ZoneServerID != 0) {
zs = zoneserver_list.FindByID(s->ZoneServerID);
if (s->zone_server_id != 0) {
zs = zoneserver_list.FindByID(s->zone_server_id);
}
else if (s->zoneid != 0) {
zs = zoneserver_list.FindByName(ZoneName(s->zoneid));
else if (s->zone_id != 0) {
zs = zoneserver_list.FindByName(ZoneName(s->zone_id));
}
else {
connection->SendLine("Error: ZoneShutdown: neither ID nor name specified");
+2 -2
View File
@@ -33,7 +33,7 @@ GroupLFP::GroupLFP(uint32 inLeaderID) {
LeaderID = inLeaderID;
for (auto &member : Members) {
member.Name[0] = '\0';
member.Class = NO_CLASS;
member.Class = Class::None;
member.Level = 0;
member.Zone = 0;
}
@@ -77,7 +77,7 @@ void GroupLFP::SetDetails(ServerLFPUpdate_Struct *Update) {
Members[i].GuildID = CLE->GuildID();
}
else {
Members[i].Class = NO_CLASS;
Members[i].Class = Class::None;
Members[i].Level = 0;
Members[i].Zone = 0;
Members[i].GuildID = 0xFFFF;
+1 -1
View File
@@ -434,7 +434,7 @@ int main(int argc, char **argv)
tod.year,
tod.month,
tod.day,
tod.hour,
tod.hour - 1,
tod.minute
);
}
+21 -18
View File
@@ -720,15 +720,15 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
break;
}
case ServerOP_ZoneShutdown: {
auto s = (ServerZoneStateChange_struct*) pack->pBuffer;
auto *s = (ServerZoneStateChange_Struct*) pack->pBuffer;
ZoneServer* zs = 0;
if (s->ZoneServerID) {
zs = zoneserver_list.FindByID(s->ZoneServerID);
} else if (s->zoneid) {
zs = zoneserver_list.FindByName(ZoneName(s->zoneid));
if (s->zone_server_id) {
zs = zoneserver_list.FindByID(s->zone_server_id);
} else if (s->zone_id) {
zs = zoneserver_list.FindByName(ZoneName(s->zone_id));
} else {
zoneserver_list.SendEmoteMessage(
s->adminname,
s->admin_name,
0,
AccountStatus::Player,
Chat::White,
@@ -738,7 +738,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
if (!zs) {
zoneserver_list.SendEmoteMessage(
s->adminname,
s->admin_name,
0,
AccountStatus::Player,
Chat::White,
@@ -751,8 +751,8 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
break;
}
case ServerOP_ZoneBootup: {
auto s = (ServerZoneStateChange_struct*) pack->pBuffer;
zoneserver_list.SOPZoneBootup(s->adminname, s->ZoneServerID, ZoneName(s->zoneid), s->makestatic);
auto *s = (ServerZoneStateChange_Struct*) pack->pBuffer;
zoneserver_list.SOPZoneBootup(s->admin_name, s->zone_server_id, ZoneName(s->zone_id), s->is_static);
break;
}
case ServerOP_ZoneStatus: {
@@ -1606,20 +1606,23 @@ void ZoneServer::ChangeWID(uint32 iCharID, uint32 iWID) {
void ZoneServer::TriggerBootup(uint32 in_zone_id, uint32 in_instance_id, const char* admin_name, bool is_static_zone) {
is_booting_up = true;
is_booting_up = true;
zone_server_zone_id = in_zone_id;
instance_id = in_instance_id;
instance_id = in_instance_id;
auto pack = new ServerPacket(ServerOP_ZoneBootup, sizeof(ServerZoneStateChange_Struct));
auto *s = (ServerZoneStateChange_Struct*) pack->pBuffer;
s->zone_server_id = zone_server_id;
s->zone_id = in_zone_id ? in_zone_id : GetZoneID();
s->instance_id = in_instance_id;
s->is_static = is_static_zone;
auto pack = new ServerPacket(ServerOP_ZoneBootup, sizeof(ServerZoneStateChange_struct));
auto s = (ServerZoneStateChange_struct*) pack->pBuffer;
s->ZoneServerID = zone_server_id;
if (admin_name) {
strn0cpy(s->adminname, admin_name, sizeof(s->adminname));
strn0cpy(s->admin_name, admin_name, sizeof(s->admin_name));
}
s->zoneid = in_zone_id ? in_zone_id : GetZoneID();
s->instanceid = in_instance_id;
s->makestatic = is_static_zone;
SendPacket(pack);
delete pack;
LSBootUpdate(in_zone_id, in_instance_id);
+6
View File
@@ -284,6 +284,12 @@ ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
INSTALL(TARGETS zone RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
IF (WIN32 AND EQEMU_BUILD_PCH)
TARGET_PRECOMPILE_HEADERS(zone PRIVATE ../common/pch/pch.h)
TARGET_PRECOMPILE_HEADERS(zone PRIVATE ../common/types.h ../common/eqemu_logsys.h ../common/eqemu_logsys_log_aliases.h ../common/features.h ../common/global_define.h)
TARGET_PRECOMPILE_HEADERS(zone PRIVATE mob.h npc.h corpse.h doors.h bot.h entity.h client.h zone.h)
ENDIF()
ADD_DEFINITIONS(-DZONE)
TARGET_LINK_LIBRARIES(zone ${ZONE_LIBS})
+111 -17
View File
@@ -373,72 +373,72 @@ void Mob::WakeTheDead(uint16 spell_id, Corpse *corpse_to_use, Mob *tar, uint32 d
//some basic combat mods here too since it's convienent
switch (corpse_to_use->class_)
{
case CLERIC:
case Class::Cleric:
made_npc->npc_spells_id = 1;
break;
case WIZARD:
case Class::Wizard:
made_npc->npc_spells_id = 2;
break;
case NECROMANCER:
case Class::Necromancer:
made_npc->npc_spells_id = 3;
break;
case MAGICIAN:
case Class::Magician:
made_npc->npc_spells_id = 4;
break;
case ENCHANTER:
case Class::Enchanter:
made_npc->npc_spells_id = 5;
break;
case SHAMAN:
case Class::Shaman:
made_npc->npc_spells_id = 6;
break;
case DRUID:
case Class::Druid:
made_npc->npc_spells_id = 7;
break;
case PALADIN:
case Class::Paladin:
//SPECATK_TRIPLE
strcpy(made_npc->special_abilities, "6,1");
made_npc->current_hp = made_npc->current_hp * 150 / 100;
made_npc->max_hp = made_npc->max_hp * 150 / 100;
made_npc->npc_spells_id = 8;
break;
case SHADOWKNIGHT:
case Class::ShadowKnight:
strcpy(made_npc->special_abilities, "6,1");
made_npc->current_hp = made_npc->current_hp * 150 / 100;
made_npc->max_hp = made_npc->max_hp * 150 / 100;
made_npc->npc_spells_id = 9;
break;
case RANGER:
case Class::Ranger:
strcpy(made_npc->special_abilities, "7,1");
made_npc->current_hp = made_npc->current_hp * 135 / 100;
made_npc->max_hp = made_npc->max_hp * 135 / 100;
made_npc->npc_spells_id = 10;
break;
case BARD:
case Class::Bard:
strcpy(made_npc->special_abilities, "6,1");
made_npc->current_hp = made_npc->current_hp * 110 / 100;
made_npc->max_hp = made_npc->max_hp * 110 / 100;
made_npc->npc_spells_id = 11;
break;
case BEASTLORD:
case Class::Beastlord:
strcpy(made_npc->special_abilities, "7,1");
made_npc->current_hp = made_npc->current_hp * 110 / 100;
made_npc->max_hp = made_npc->max_hp * 110 / 100;
made_npc->npc_spells_id = 12;
break;
case ROGUE:
case Class::Rogue:
strcpy(made_npc->special_abilities, "7,1");
made_npc->max_dmg = made_npc->max_dmg * 150 / 100;
made_npc->current_hp = made_npc->current_hp * 110 / 100;
made_npc->max_hp = made_npc->max_hp * 110 / 100;
break;
case MONK:
case Class::Monk:
strcpy(made_npc->special_abilities, "7,1");
made_npc->max_dmg = made_npc->max_dmg * 150 / 100;
made_npc->current_hp = made_npc->current_hp * 135 / 100;
made_npc->max_hp = made_npc->max_hp * 135 / 100;
break;
case WARRIOR:
case BERSERKER:
case Class::Warrior:
case Class::Berserker:
strcpy(made_npc->special_abilities, "7,1");
made_npc->max_dmg = made_npc->max_dmg * 150 / 100;
made_npc->current_hp = made_npc->current_hp * 175 / 100;
@@ -1354,7 +1354,7 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
}
else {
// Bards can cast instant cast AAs while they are casting or channeling item cast.
if (GetClass() == BARD && IsCasting() && spells[rank->spell].cast_time == 0) {
if (GetClass() == Class::Bard && IsCasting() && spells[rank->spell].cast_time == 0) {
if (!DoCastingChecksOnCaster(rank->spell, EQ::spells::CastingSlot::AltAbility)) {
return;
}
@@ -2207,3 +2207,97 @@ bool Client::HasAlreadyPurchasedRank(AA::Rank* rank) {
return false;
}
void Client::ListPurchasedAAs(Client *to, std::string search_criteria)
{
if (!to) {
return;
}
std::map<std::string, uint8> client_aa_ranks;
for (auto &aa : zone->aa_abilities) {
AA::Ability *ability = aa.second.get();
AA::Rank *rank = ability->first;
while (rank) {
if (!CanUseAlternateAdvancementRank(rank)) {
break;
}
if (HasAlreadyPurchasedRank(rank)) {
const std::string aa_name = zone->GetAAName(rank->id);
if (
search_criteria.empty() ||
Strings::Contains(
Strings::ToLower(aa_name),
Strings::ToLower(search_criteria)
)
) {
if (client_aa_ranks.find(aa_name) == client_aa_ranks.end()) {
client_aa_ranks[aa_name] = 1;
} else {
client_aa_ranks[aa_name]++;
}
}
}
rank = rank->next;
}
}
if (client_aa_ranks.empty()) {
to->Message(
Chat::White,
fmt::format(
"{} {} no purchased AAs{}.",
to->GetTargetDescription(this, TargetDescriptionType::UCYou),
this == to ? "have" : "has",
(
!search_criteria.empty() ?
fmt::format(
" matching '{}'",
search_criteria
) :
""
)
).c_str()
);
return;
}
int aa_number = 1;
for (const auto &aa : client_aa_ranks) {
to->Message(
Chat::White,
fmt::format(
"{}. {} (Rank {})",
aa_number,
aa.first,
aa.second
).c_str()
);
aa_number++;
}
to->Message(
Chat::White,
fmt::format(
"{} {} {} purchased AA{}{}.",
to->GetTargetDescription(this, TargetDescriptionType::UCYou),
this == to ? "have" : "has",
client_aa_ranks.size(),
client_aa_ranks.size() > 1 ? "s" : "",
(
!search_criteria.empty() ?
fmt::format(
" matching '{}'",
search_criteria
) :
""
)
).c_str()
);
}
+5 -5
View File
@@ -46,7 +46,7 @@ void EntityList::DescribeAggro(Client *to_who, NPC *from_who, float d, bool verb
);
bool is_engaged = from_who->IsEngaged();
bool will_aggro_npcs = from_who->WillAggroNPCs();
bool will_aggro_npcs = from_who->GetNPCAggro();
if (is_engaged) {
Mob *top = from_who->GetHateTop();
to_who->Message(
@@ -678,7 +678,7 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
if(!isSpellAttack)
{
if(GetClass() == LDON_TREASURE)
if(GetClass() == Class::LDoNTreasure)
{
return false;
}
@@ -1070,10 +1070,10 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage, ExtraAtt
SetPseudoRoot(false);
}
}
if (aeRampage) {
float aeramp_size = RuleR(Combat, AERampageMaxDistance);
LogCombatDetail("AERampage: Default - aeramp_size = [{}] ", aeramp_size);
if (opts) {
@@ -1093,7 +1093,7 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage, ExtraAtt
LogCombatDetail("AE Rampage: ramp_range = [{}] -- (size_mod [{}] * aeramp_size [{}])", ramp_range, size_mod, aeramp_size);
LogCombatDetail("AE Rampage: _DistNoRoot [{}] <= ramp_range [{}]", _DistNoRoot, ramp_range);
if (_DistNoRoot <= ramp_range) {
LogCombatDetail("AE Rampage: Combat Distance returned [true]");
return true;
+1 -1
View File
@@ -245,7 +245,7 @@ Json::Value ApiGetNpcListDetail(EQ::Net::WebsocketServerConnection *connection,
row["swarm_owner"] = npc->GetSwarmOwner();
row["swarm_target"] = npc->GetSwarmTarget();
row["waypoint_max"] = npc->GetWaypointMax();
row["will_aggro_npcs"] = npc->WillAggroNPCs();
row["npc_aggro"] = npc->GetNPCAggro();
response.append(row);
}
+161 -161
View File
@@ -558,7 +558,7 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
}
// dodge
if (CanThisClassDodge() && (InFront || GetClass() == MONK)) {
if (CanThisClassDodge() && (InFront || GetClass() == Class::Monk)) {
if (IsClient())
CastToClient()->CheckIncreaseSkill(EQ::skills::SkillDodge, other, -10);
// check auto discs ... I guess aa/items too :P
@@ -674,28 +674,28 @@ int Mob::GetACSoftcap()
int level = std::min(105, static_cast<int>(GetLevel())) - 1;
switch (GetClass()) {
case WARRIOR:
case Class::Warrior:
return war_softcaps[level];
case CLERIC:
case BARD:
case MONK:
case Class::Cleric:
case Class::Bard:
case Class::Monk:
return clrbrdmnk_softcaps[level];
case PALADIN:
case SHADOWKNIGHT:
case Class::Paladin:
case Class::ShadowKnight:
return palshd_softcaps[level];
case RANGER:
case Class::Ranger:
return rng_softcaps[level];
case DRUID:
case Class::Druid:
return dru_softcaps[level];
case ROGUE:
case SHAMAN:
case BEASTLORD:
case BERSERKER:
case Class::Rogue:
case Class::Shaman:
case Class::Beastlord:
case Class::Berserker:
return rogshmbstber_softcaps[level];
case NECROMANCER:
case WIZARD:
case MAGICIAN:
case ENCHANTER:
case Class::Necromancer:
case Class::Wizard:
case Class::Magician:
case Class::Enchanter:
return necwizmagenc_softcaps[level];
default:
return 350;
@@ -707,28 +707,28 @@ double Mob::GetSoftcapReturns()
// These are based on the dev post, they seem to be correct for every level
// AKA no more hard caps
switch (GetClass()) {
case WARRIOR:
case Class::Warrior:
return 0.35;
case CLERIC:
case BARD:
case MONK:
case Class::Cleric:
case Class::Bard:
case Class::Monk:
return 0.3;
case PALADIN:
case SHADOWKNIGHT:
case Class::Paladin:
case Class::ShadowKnight:
return 0.33;
case RANGER:
case Class::Ranger:
return 0.315;
case DRUID:
case Class::Druid:
return 0.265;
case ROGUE:
case SHAMAN:
case BEASTLORD:
case BERSERKER:
case Class::Rogue:
case Class::Shaman:
case Class::Beastlord:
case Class::Berserker:
return 0.28;
case NECROMANCER:
case WIZARD:
case MAGICIAN:
case ENCHANTER:
case Class::Necromancer:
case Class::Wizard:
case Class::Magician:
case Class::Enchanter:
return 0.25;
default:
return 0.3;
@@ -739,7 +739,7 @@ int Mob::GetClassRaceACBonus()
{
int ac_bonus = 0;
auto level = GetLevel();
if (GetClass() == MONK) {
if (GetClass() == Class::Monk) {
int hardcap = 30;
int softcap = 14;
if (level > 99) {
@@ -824,7 +824,7 @@ int Mob::GetClassRaceACBonus()
}
}
if (GetClass() == ROGUE) {
if (GetClass() == Class::Rogue) {
int level_scaler = level - 26;
if (GetAGI() < 80)
ac_bonus = level_scaler / 4;
@@ -840,7 +840,7 @@ int Mob::GetClassRaceACBonus()
ac_bonus = 12;
}
if (GetClass() == BEASTLORD) {
if (GetClass() == Class::Beastlord) {
int level_scaler = level - 6;
if (GetAGI() < 80)
ac_bonus = level_scaler / 5;
@@ -894,14 +894,14 @@ int Mob::ACSum(bool skip_caps)
ac += GetPetACBonusFromOwner();
auto spell_aa_ac = aabonuses.AC + spellbonuses.AC;
ac += GetSkill(EQ::skills::SkillDefense) / 5;
if (EQ::ValueWithin(static_cast<int>(GetClass()), NECROMANCER, ENCHANTER))
if (EQ::ValueWithin(static_cast<int>(GetClass()), Class::Necromancer, Class::Enchanter))
ac += spell_aa_ac / 3;
else
ac += spell_aa_ac / 4;
}
else { // TODO: so we can't set NPC skills ... so the skill bonus ends up being HUGE so lets nerf them a bit
auto spell_aa_ac = aabonuses.AC + spellbonuses.AC;
if (EQ::ValueWithin(static_cast<int>(GetClass()), NECROMANCER, ENCHANTER))
if (EQ::ValueWithin(static_cast<int>(GetClass()), Class::Necromancer, Class::Enchanter))
ac += GetSkill(EQ::skills::SkillDefense) / 2 + spell_aa_ac / 3;
else
ac += GetSkill(EQ::skills::SkillDefense) / 3 + spell_aa_ac / 4;
@@ -1085,7 +1085,7 @@ int64 Mob::GetWeaponDamage(Mob *against, const EQ::ItemData *weapon_item) {
return 0;
}
}
else if ((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30) {
else if ((GetClass() == Class::Monk || GetClass() == Class::Beastlord) && GetLevel() >= 30) {
dmg = GetHandToHandDamage();
}
else {
@@ -1199,7 +1199,7 @@ int64 Mob::GetWeaponDamage(Mob *against, const EQ::ItemInstance *weapon_item, in
MagicGloves = gloves->GetItemMagical(true);
}
if (GetClass() == MONK || GetClass() == BEASTLORD) {
if (GetClass() == Class::Monk || GetClass() == Class::Beastlord) {
if (MagicGloves || GetLevel() >= 30) {
dmg = GetHandToHandDamage();
if (hate)
@@ -1295,15 +1295,15 @@ int64 Mob::DoDamageCaps(int64 base_damage)
}
else if (level >= 40) {
switch (GetClass()) {
case CLERIC:
case DRUID:
case SHAMAN:
case Class::Cleric:
case Class::Druid:
case Class::Shaman:
cap = 80;
break;
case NECROMANCER:
case WIZARD:
case MAGICIAN:
case ENCHANTER:
case Class::Necromancer:
case Class::Wizard:
case Class::Magician:
case Class::Enchanter:
cap = 40;
break;
default:
@@ -1313,15 +1313,15 @@ int64 Mob::DoDamageCaps(int64 base_damage)
}
else if (level >= 30) {
switch (GetClass()) {
case CLERIC:
case DRUID:
case SHAMAN:
case Class::Cleric:
case Class::Druid:
case Class::Shaman:
cap = 26;
break;
case NECROMANCER:
case WIZARD:
case MAGICIAN:
case ENCHANTER:
case Class::Necromancer:
case Class::Wizard:
case Class::Magician:
case Class::Enchanter:
cap = 18;
break;
default:
@@ -1331,15 +1331,15 @@ int64 Mob::DoDamageCaps(int64 base_damage)
}
else if (level >= 20) {
switch (GetClass()) {
case CLERIC:
case DRUID:
case SHAMAN:
case Class::Cleric:
case Class::Druid:
case Class::Shaman:
cap = 20;
break;
case NECROMANCER:
case WIZARD:
case MAGICIAN:
case ENCHANTER:
case Class::Necromancer:
case Class::Wizard:
case Class::Magician:
case Class::Enchanter:
cap = 12;
break;
default:
@@ -1349,15 +1349,15 @@ int64 Mob::DoDamageCaps(int64 base_damage)
}
else if (level >= 10) {
switch (GetClass()) {
case CLERIC:
case DRUID:
case SHAMAN:
case Class::Cleric:
case Class::Druid:
case Class::Shaman:
cap = 12;
break;
case NECROMANCER:
case WIZARD:
case MAGICIAN:
case ENCHANTER:
case Class::Necromancer:
case Class::Wizard:
case Class::Magician:
case Class::Enchanter:
cap = 10;
break;
default:
@@ -1367,15 +1367,15 @@ int64 Mob::DoDamageCaps(int64 base_damage)
}
else {
switch (GetClass()) {
case CLERIC:
case DRUID:
case SHAMAN:
case Class::Cleric:
case Class::Druid:
case Class::Shaman:
cap = 9;
break;
case NECROMANCER:
case WIZARD:
case MAGICIAN:
case ENCHANTER:
case Class::Necromancer:
case Class::Wizard:
case Class::Magician:
case Class::Enchanter:
cap = 6;
break;
default:
@@ -1476,7 +1476,7 @@ bool Mob::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
LogCombatDetail("Attacking [{}] with hand [{}] [{}]", other->GetName(), Hand, bRiposte ? "this is a riposte" : "");
if (
(IsCasting() && GetClass() != BARD && !IsFromSpell)
(IsCasting() && GetClass() != Class::Bard && !IsFromSpell)
|| ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead))
|| (GetHP() < 0)
|| (!IsAttackAllowed(other))
@@ -2019,7 +2019,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
/*
Reset reuse timer for classic skill based Lay on Hands (For tit I guess)
*/
if (GetClass() == PALADIN) { // we could check if it's not expired I guess, but should be fine not to
if (GetClass() == Class::Paladin) { // we could check if it's not expired I guess, but should be fine not to
p_timers.Clear(&database, pTimerLayHands);
}
@@ -2343,7 +2343,7 @@ void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillTyp
if (!IsEngaged())
zone->AddAggroMob();
if (GetClass() == LDON_TREASURE)
if (GetClass() == Class::LDoNTreasure)
{
if (IsLDoNLocked() && GetLDoNLockedSkill() != LDoNTypeMechanical)
{
@@ -2461,7 +2461,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
BuffFadeAll();
const auto killed_level = GetLevel();
if (GetClass() == LDON_TREASURE) { // open chest
if (GetClass() == Class::LDoNTreasure) { // open chest
auto outapp = new EQApplicationPacket(OP_Animation, sizeof(Animation_Struct));
Animation_Struct* anim = (Animation_Struct*)outapp->pBuffer;
anim->spawnid = GetID();
@@ -2488,7 +2488,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
respawn2->DeathReset(1);
}
if (killer_mob && GetClass() != LDON_TREASURE) {
if (killer_mob && GetClass() != Class::LDoNTreasure) {
hate_list.AddEntToHateList(killer_mob, damage);
}
@@ -2535,7 +2535,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
hate_list.DoFactionHits(GetNPCFactionID(), GetPrimaryFaction(), GetFactionAmount());
}
bool IsLdonTreasure = (GetClass() == LDON_TREASURE);
bool IsLdonTreasure = (GetClass() == Class::LDoNTreasure);
if (give_exp_client && !IsCorpse()) {
Group *kg = entity_list.GetGroupByClient(give_exp_client);
@@ -2701,10 +2701,73 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
}
bool allow_merchant_corpse = RuleB(Merchant, AllowCorpse);
bool is_merchant = (class_ == MERCHANT || class_ == ADVENTURE_MERCHANT || MerchantType != 0);
bool is_merchant = (class_ == Class::Merchant || class_ == Class::AdventureMerchant || MerchantType != 0);
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);
}
}
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);
}
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);
}
if (!HasOwner() && !IsMerc() && !GetSwarmInfo() && (!is_merchant || allow_merchant_corpse) &&
((killer && (killer->IsClient() || (killer->HasOwner() && killer->GetUltimateOwner()->IsClient()) ||
(killer->IsNPC() && killer->CastToNPC()->GetSwarmInfo() && killer->CastToNPC()->GetSwarmInfo()->GetOwner() && killer->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient())))
@@ -2814,38 +2877,6 @@ 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);
}
}
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);
}
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;
@@ -2854,37 +2885,6 @@ 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)
);
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);
}
return true;
}
@@ -3295,14 +3295,14 @@ int Mob::GetHandToHandDamage(void)
7, 7, 7, 8, 8, 8, 8, 8, 8, 9, // 21-30
9, 9, 9, 9, 9, 10, 10, 10, 10, 10, // 31-40
10, 11, 11, 11, 11, 11, 11, 12, 12 }; // 41-49
if (GetClass() == MONK) {
if (GetClass() == Class::Monk) {
if (IsClient() && CastToClient()->GetItemIDAt(12) == 10652 && GetLevel() > 50)
return 9;
if (level > 62)
return 15;
return mnk_dmg[level];
}
else if (GetClass() == BEASTLORD) {
else if (GetClass() == Class::Beastlord) {
if (level > 49)
return 13;
return bst_dmg[level];
@@ -3354,7 +3354,7 @@ int Mob::GetHandToHandDelay(void)
28, 28, 28, 27, 27, 27, 27, 27, 26, 26, // 61-70
26, 26, 26 }; // 71-73
if (GetClass() == MONK) {
if (GetClass() == Class::Monk) {
// Have a look to see if we have epic fists on
if (IsClient() && CastToClient()->GetItemIDAt(12) == 10652 && GetLevel() > 50)
return 16;
@@ -3363,7 +3363,7 @@ int Mob::GetHandToHandDelay(void)
return GetRace() == IKSAR ? 21 : 20;
return GetRace() == IKSAR ? mnk_iks_delay[level] : mnk_hum_delay[level];
}
else if (GetClass() == BEASTLORD) {
else if (GetClass() == Class::Beastlord) {
int level = GetLevel();
if (level > 73)
return 25;
@@ -4067,7 +4067,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
}
}
else if (skill_used == EQ::skills::SkillKick &&
(attacker->GetLevel() > 55 || attacker->IsNPC()) && GetClass() == WARRIOR) {
(attacker->GetLevel() > 55 || attacker->IsNPC()) && GetClass() == Class::Warrior) {
can_stun = true;
}
@@ -5053,11 +5053,11 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
// We either require an innate crit chance or some SPA 169 to crit
bool innate_crit = false;
int crit_chance = GetCriticalChanceBonus(hit.skill);
if ((GetClass() == WARRIOR || GetClass() == BERSERKER) && GetLevel() >= 12)
if ((GetClass() == Class::Warrior || GetClass() == Class::Berserker) && GetLevel() >= 12)
innate_crit = true;
else if (GetClass() == RANGER && GetLevel() >= 12 && hit.skill == EQ::skills::SkillArchery)
else if (GetClass() == Class::Ranger && GetLevel() >= 12 && hit.skill == EQ::skills::SkillArchery)
innate_crit = true;
else if (GetClass() == ROGUE && GetLevel() >= 12 && hit.skill == EQ::skills::SkillThrowing)
else if (GetClass() == Class::Rogue && GetLevel() >= 12 && hit.skill == EQ::skills::SkillThrowing)
innate_crit = true;
// we have a chance to crit!
@@ -5077,7 +5077,7 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
dex_bonus += 45; // chances did not match live without a small boost
// so if we have an innate crit we have a better chance, except for ber throwing
if (!innate_crit || (GetClass() == BERSERKER && hit.skill == EQ::skills::SkillThrowing))
if (!innate_crit || (GetClass() == Class::Berserker && hit.skill == EQ::skills::SkillThrowing))
dex_bonus = dex_bonus * 3 / 5;
if (crit_chance)
@@ -5101,7 +5101,7 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
LogCombat("Crit success roll [{}] dex chance [{}] og dmg [{}] crit_mod [{}] new dmg [{}]", roll, dex_bonus, og_damage, crit_mod, hit.damage_done);
// step 3: check deadly strike
if (GetClass() == ROGUE && hit.skill == EQ::skills::SkillThrowing) {
if (GetClass() == Class::Rogue && hit.skill == EQ::skills::SkillThrowing) {
if (BehindMob(defender, GetX(), GetY())) {
int chance = GetLevel() * 12;
if (zone->random.Int(1, 1000) < chance) {
@@ -5284,7 +5284,7 @@ void Mob::DoRiposte(Mob *defender)
if (DoubleRipChance && zone->random.Roll(DoubleRipChance)) {
LogCombat("Preforming a return SPECIAL ATTACK ([{}] percent chance)", DoubleRipChance);
if (defender->GetClass() == MONK)
if (defender->GetClass() == Class::Monk)
defender->MonkSpecialAttack(this, defender->aabonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL]);
else if (defender->IsClient()) // so yeah, even if you don't have the skill you can still do the attack :P (and we don't crash anymore)
defender->CastToClient()->DoClassAttacks(this, defender->aabonuses.GiveDoubleRiposte[SBIndex::DOUBLE_RIPOSTE_SKILL], true);
@@ -5301,7 +5301,7 @@ void Mob::ApplyMeleeDamageMods(uint16 skill, int64 &damage, Mob *defender, Extra
dmgbonusmod += opts->melee_damage_bonus_flat;
if (defender) {
if (defender->IsOfClientBotMerc() && defender->GetClass() == WARRIOR) {
if (defender->IsOfClientBotMerc() && defender->GetClass() == Class::Warrior) {
dmgbonusmod -= 5;
}
// 168 defensive
@@ -5445,7 +5445,7 @@ const DamageTable &Mob::GetDamageTable() const
{ 415, 15, 40 }, // 105
};
bool monk = GetClass() == MONK;
bool monk = GetClass() == Class::Monk;
bool melee = IsWarriorClass();
// tables caped at 105 for now -- future proofed for a while at least :P
int level = std::min(static_cast<int>(GetLevel()), 105);
@@ -5893,7 +5893,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
// BER weren't parsing the halving
if (hit.skill == EQ::skills::SkillArchery ||
(hit.skill == EQ::skills::SkillThrowing && GetClass() != BERSERKER))
(hit.skill == EQ::skills::SkillThrowing && GetClass() != Class::Berserker))
hit.damage_done /= 2;
if (hit.damage_done < 1)
@@ -5906,7 +5906,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
if (headshot > 0) {
hit.damage_done = headshot;
}
else if (GetClass() == RANGER && GetLevel() > 50) { // no double dmg on headshot
else if (GetClass() == Class::Ranger && GetLevel() > 50) { // no double dmg on headshot
if ((defender->IsNPC() && !defender->IsMoving() && !defender->IsRooted()) || !RuleB(Combat, ArcheryBonusRequiresStationary)) {
hit.damage_done *= 2;
MessageString(Chat::MeleeCrit, BOW_DOUBLE_DAMAGE);
@@ -5931,7 +5931,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
hit.damage_done = ass;
}
}
else if (hit.skill == EQ::skills::SkillFrenzy && GetClass() == BERSERKER && GetLevel() > 50) {
else if (hit.skill == EQ::skills::SkillFrenzy && GetClass() == Class::Berserker && GetLevel() > 50) {
extra_mincap = 4 * GetLevel() / 5;
}
+2 -2
View File
@@ -117,7 +117,7 @@ void Client::CalcBonuses()
// hmm maybe a better way to do this
int metabolism = spellbonuses.Metabolism + itembonuses.Metabolism + aabonuses.Metabolism;
int timer = GetClass() == MONK ? CONSUMPTION_MNK_TIMER : CONSUMPTION_TIMER;
int timer = GetClass() == Class::Monk ? CONSUMPTION_MNK_TIMER : CONSUMPTION_TIMER;
timer = timer * (100 + metabolism) / 100;
if (timer != consume_food_timer.GetTimerTime())
consume_food_timer.SetTimer(timer);
@@ -2053,7 +2053,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
}
}
if (GetClass() == BARD)
if (GetClass() == Class::Bard)
newbon->ManaRegen = 0; // Bards do not get mana regen from spells.
}
+837 -182
View File
File diff suppressed because it is too large Load Diff
+35 -17
View File
@@ -42,13 +42,12 @@ constexpr uint32 BOT_FOLLOW_DISTANCE_DEFAULT_MAX = 2500; // as DSq value (50 uni
constexpr uint32 BOT_KEEP_ALIVE_INTERVAL = 5000; // 5 seconds
constexpr uint32 MAG_EPIC_1_0 = 28034;
extern WorldServer worldserver;
constexpr int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this
constexpr int MaxSpellTimer = 15;
constexpr int MaxDisciplineTimer = 10;
constexpr int DisciplineReuseStart = MaxSpellTimer + 1;
constexpr int MaxTimer = MaxSpellTimer + MaxDisciplineTimer;
constexpr int NegativeItemReuse = -1; // Unlinked timer for items
// nHSND negative Healer/Slower/Nuker/Doter
// pH positive Healer
@@ -222,6 +221,8 @@ public:
void SetPullFlag(bool flag = true) { m_pull_flag = flag; }
bool GetPullingFlag() const { return m_pulling_flag; }
bool GetReturningFlag() const { return m_returning_flag; }
bool GetIsUsingItemClick() { return is_using_item_click; }
void SetIsUsingItemClick(bool flag = true) { is_using_item_click = flag; }
bool UseDiscipline(uint32 spell_id, uint32 target);
uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid);
uint8 GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool includePets, Raid* raid);
@@ -286,6 +287,10 @@ public:
void SetEndurance(int32 newEnd) override;
void DoEnduranceUpkeep();
void TryItemClick(uint16 slot_id);
EQ::ItemInstance* GetClickItem(uint16 slot_id);
void DoItemClick(const EQ::ItemData* inst, uint16 slot_id);
bool AI_AddBotSpells(uint32 bot_spell_id);
void AddSpellToBotList(
int16 iPriority,
@@ -394,25 +399,20 @@ public:
// Static Class Methods
static Bot* LoadBot(uint32 botID);
static uint32 SpawnedBotCount(const uint32 owner_id, uint8 class_id = NO_CLASS);
static uint32 SpawnedBotCount(const uint32 owner_id, uint8 class_id = Class::None);
static void LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp);
static bool IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined);
static Bot* GetBotByBotClientOwnerAndBotName(Client* c, const std::string& botName);
static void ProcessBotGroupInvite(Client* c, std::string const& botName);
static void ProcessBotGroupDisband(Client* c, const std::string& botName);
static void BotOrderCampAll(Client* c, uint8 class_id = NO_CLASS);
static void BotOrderCampAll(Client* c, uint8 class_id = Class::None);
static void ProcessBotInspectionRequest(Bot* inspectedBot, Client* client);
static void LoadAndSpawnAllZonedBots(Client* bot_owner);
static bool GroupHasBot(Group* group);
static Bot* GetFirstBotInGroup(Group* group);
static void ProcessClientZoneChange(Client* botOwner);
static void ProcessBotOwnerRefDelete(Mob* botOwner); // Removes a Client* reference when the Client object is destroyed
static int32 GetSpellRecastTimer(Bot *caster, int timer_index);
static bool CheckSpellRecastTimers(Bot *caster, int SpellIndex);
static int32 GetDisciplineRecastTimer(Bot *caster, int timer_index);
static bool CheckDisciplineRecastTimers(Bot *caster, int timer_index);
static uint32 GetDisciplineRemainingTime(Bot *caster, int timer_index);
//Raid methods
static void ProcessRaidInvite(Mob* invitee, Client* invitor, bool group_invite = false);
@@ -604,7 +604,7 @@ public:
void SetBotCharmer(bool c) { _botCharmer = c; }
void SetPetChooser(bool p) { _petChooser = p; }
void SetBotOwner(Mob* botOwner) { this->_botOwner = botOwner; }
void SetRangerAutoWeaponSelect(bool enable) { GetClass() == RANGER ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; }
void SetRangerAutoWeaponSelect(bool enable) { GetClass() == Class::Ranger ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; }
void SetBotStance(EQ::constants::StanceType botStance) {
if (botStance >= EQ::constants::stancePassive && botStance <= EQ::constants::stanceBurnAE)
_botStance = botStance;
@@ -612,8 +612,23 @@ public:
_botStance = EQ::constants::stancePassive;
}
void SetBotCasterRange(uint32 bot_caster_range) { m_bot_caster_range = bot_caster_range; }
void SetSpellRecastTimer(int timer_index, int32 recast_delay);
void SetDisciplineRecastTimer(int timer_index, int32 recast_delay);
uint32 GetSpellRecastTimer(uint16 spell_id = 0);
bool CheckSpellRecastTimer(uint16 spell_id = 0);
uint32 GetSpellRecastRemainingTime(uint16 spell_id = 0);
void SetSpellRecastTimer(uint16 spell_id, int32 recast_delay = 0);
uint32 CalcSpellRecastTimer(uint16 spell_id);
uint32 GetDisciplineReuseTimer(uint16 spell_id = 0);
bool CheckDisciplineReuseTimer(uint16 spell_id = 0);
uint32 GetDisciplineReuseRemainingTime(uint16 spell_id = 0);
void SetDisciplineReuseTimer(uint16 spell_id, int32 reuse_timer = 0);
uint32 GetItemReuseTimer(uint32 item_id = 0);
bool CheckItemReuseTimer(uint32 item_id = 0);
void SetItemReuseTimer(uint32 item_id, uint32 reuse_timer = 0);
void ClearDisciplineReuseTimer(uint16 spell_id = 0);
void ClearItemReuseTimer(uint32 item_id = 0);
void ClearSpellRecastTimer(uint16 spell_id = 0);
uint32 GetItemReuseRemainingTime(uint32 item_id = 0);
void ClearExpiredTimers();
void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;}
void SetShowHelm(bool showhelm) { _showhelm = showhelm; }
void SetBeardColor(uint8 value) { beardcolor = value; }
@@ -713,7 +728,8 @@ public:
// New accessors for BotDatabase access
bool DeleteBot();
uint32* GetTimers() { return timers; }
std::vector<BotTimer_Struct> GetBotTimers() { return bot_timers; }
void SetBotTimers(std::vector<BotTimer_Struct> timers) { bot_timers = timers; }
uint32 GetLastZoneID() const { return _lastZoneId; }
int32 GetBaseAC() const { return _baseAC; }
int32 GetBaseATK() const { return _baseATK; }
@@ -738,7 +754,7 @@ public:
//Raid additions
Raid* p_raid_instance;
static uint8 spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND];
static uint8 spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND];
bool BotCastMez(Mob* tar, uint8 botLevel, bool checked_los, BotSpell& botSpell, Raid* raid);
bool BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, Raid* raid);
@@ -828,6 +844,7 @@ protected:
std::vector<BotSpells_Struct> AIBot_spells;
std::vector<BotSpells_Struct> AIBot_spells_enforced;
std::vector<BotTimer_Struct> bot_timers;
private:
// Class Members
@@ -861,11 +878,11 @@ private:
int32 cur_end;
int32 max_end;
int32 end_regen;
uint32 timers[MaxTimer];
Timer m_evade_timer; // can be moved to pTimers at some point
Timer m_alt_combat_hate_timer;
Timer m_auto_defend_timer;
Timer auto_save_timer;
bool m_dirtyautohaters;
bool m_guard_flag;
bool m_hold_flag;
@@ -874,6 +891,7 @@ private:
bool m_pull_flag;
bool m_pulling_flag;
bool m_returning_flag;
bool is_using_item_click;
eStandingPetOrder m_previous_pet_order;
uint32 m_bot_caster_range;
BotCastingRoles m_CastingRoles;
+435 -98
View File
@@ -84,8 +84,8 @@ namespace
enum { EffectIDFirst = 1, EffectIDLast = 12 };
#define VALIDATECLASSID(x) ((x >= WARRIOR && x <= BERSERKER) ? (x) : (0))
#define CLASSIDTOINDEX(x) ((x >= WARRIOR && x <= BERSERKER) ? (x - 1) : (0))
#define VALIDATECLASSID(x) ((x >= Class::Warrior && x <= Class::Berserker) ? (x) : (0))
#define CLASSIDTOINDEX(x) ((x >= Class::Warrior && x <= Class::Berserker) ? (x - 1) : (0))
#define EFFECTIDTOINDEX(x) ((x >= EffectIDFirst && x <= EffectIDLast) ? (x - 1) : (0))
#define AILMENTIDTOINDEX(x) ((x >= BCEnum::AT_Blindness && x <= BCEnum::AT_Corruption) ? (x - 1) : (0))
#define RESISTANCEIDTOINDEX(x) ((x >= BCEnum::RT_Fire && x <= BCEnum::RT_Corruption) ? (x - 1) : (0))
@@ -196,7 +196,7 @@ public:
uint8 class_levels[16] = {0};
bool player_spell = false;
for (int class_type = WARRIOR; class_type <= BERSERKER; ++class_type) {
for (int class_type = Class::Warrior; class_type <= Class::Berserker; ++class_type) {
int class_index = CLASSIDTOINDEX(class_type);
if (spells[spell_id].classes[class_index] == 0 ||
spells[spell_id].classes[class_index] > HARD_LEVEL_CAP) {
@@ -489,7 +489,7 @@ public:
entry_prototype->target_type = target_type;
bcst_levels& bot_levels = bot_levels_map[entry_prototype->BCST()];
for (int class_type = WARRIOR; class_type <= BERSERKER; ++class_type) {
for (int class_type = Class::Warrior; class_type <= Class::Berserker; ++class_type) {
int class_index = CLASSIDTOINDEX(class_type);
if (!class_levels[class_index])
continue;
@@ -1119,7 +1119,7 @@ private:
}
static void helper_bots_string(BCEnum::SpType type_index, bcst_levels& bot_levels) {
for (int i = WARRIOR; i <= BERSERKER; ++i)
for (int i = Class::Warrior; i <= Class::Berserker; ++i)
required_bots_map_by_class[type_index][i] = "Unavailable...";
if (bot_levels.empty()) {
@@ -1375,6 +1375,7 @@ int bot_command_init(void)
bot_command_add("casterrange", "Controls the range casters will try to stay away from a mob (if too far, they will skip spells that are out-of-range)", AccountStatus::Player, bot_command_caster_range) ||
bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) ||
bot_command_add("circle", "Orders a Druid bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_subcommand_circle) ||
bot_command_add("clickitem", "Orders your targeted bot to click the item in the provided inventory slot.", AccountStatus::Player, bot_command_click_item) ||
bot_command_add("cure", "Orders a bot to remove any ailments", AccountStatus::Player, bot_command_cure) ||
bot_command_add("defensive", "Orders a bot to use a defensive discipline", AccountStatus::Player, bot_command_defensive) ||
bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) ||
@@ -1444,6 +1445,7 @@ int bot_command_init(void)
bot_command_add("summoncorpse", "Orders a bot to summon a corpse to its feet", AccountStatus::Player, bot_command_summon_corpse) ||
bot_command_add("suspend", "Suspends a bot's AI processing until released", AccountStatus::Player, bot_command_suspend) ||
bot_command_add("taunt", "Toggles taunt use by a bot", AccountStatus::Player, bot_command_taunt) ||
bot_command_add("timer", "Checks or clears timers of the chosen type.", AccountStatus::GMMgmt, bot_command_timer) ||
bot_command_add("track", "Orders a capable bot to track enemies", AccountStatus::Player, bot_command_track) ||
bot_command_add("viewcombos", "Views bot race class combinations", AccountStatus::Player, bot_command_view_combos) ||
bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", AccountStatus::Player, bot_command_water_breathing)
@@ -2377,7 +2379,7 @@ namespace ActionableBots
else if (!ab_type_arg.compare("healrotationtargets")) {
ab_type = ABT_HealRotationTargets;
}
else if (!ab_type_arg.compare("byclass")) {
else if (!ab_type_arg.compare("byclass")) {
ab_type = ABT_Class;
}
else if (!ab_type_arg.compare("byrace")) {
@@ -2730,9 +2732,9 @@ namespace ActionableBots
static void Filter_ByHighestPickLock(Client* bot_owner, std::list<Bot*>& sbl, float& pick_lock_value) {
sbl.remove_if([bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); });
sbl.remove_if([bot_owner](const Bot* l) { return (l->GetClass() != ROGUE && l->GetClass() != BARD); });
sbl.remove_if([bot_owner](const Bot* l) { return (l->GetClass() == ROGUE && l->GetLevel() < 5); });
sbl.remove_if([bot_owner](const Bot* l) { return (l->GetClass() == BARD && l->GetLevel() < 40); });
sbl.remove_if([bot_owner](const Bot* l) { return (l->GetClass() != Class::Rogue && l->GetClass() != Class::Bard); });
sbl.remove_if([bot_owner](const Bot* l) { return (l->GetClass() == Class::Rogue && l->GetLevel() < 5); });
sbl.remove_if([bot_owner](const Bot* l) { return (l->GetClass() == Class::Bard && l->GetLevel() < 40); });
ActionableBots::Filter_ByHighestSkill(bot_owner, sbl, EQ::skills::SkillPickLock, pick_lock_value);
}
@@ -2832,7 +2834,7 @@ void bot_command_aggressive(Client *c, const Seperator *sep)
}
}
c->Message(Chat::White, "%i of %i bots have used aggressive disciplines", success_count, candidate_count);
c->Message(Chat::White, "%i of %i bots have attempted to use aggressive disciplines", success_count, candidate_count);
}
void bot_command_apply_poison(Client *c, const Seperator *sep)
@@ -2856,7 +2858,7 @@ void bot_command_apply_poison(Client *c, const Seperator *sep)
t &&
t->IsBot() &&
t->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID() &&
t->GetClass() == ROGUE
t->GetClass() == Class::Rogue
) {
my_rogue_bot = t->CastToBot();
}
@@ -2964,7 +2966,7 @@ void bot_command_apply_potion(Client* c, const Seperator* sep)
if (potion_data->ItemType == EQ::item::ItemTypePotion && potion_data->Click.Effect > 0) {
if (RuleB(Bots, RestrictApplyPotionToRogue) && potion_data->Classes != PLAYER_CLASS_ROGUE_BIT) {
if (RuleB(Bots, RestrictApplyPotionToRogue) && potion_data->Classes != player_class_bitmasks[Class::Rogue]) {
c->Message(Chat::White, "This command is restricted to rogue poison potions only!");
return;
@@ -3300,7 +3302,7 @@ void bot_command_defensive(Client *c, const Seperator *sep)
auto local_entry = list_iter->SafeCastToStance();
if (helper_spell_check_fail(local_entry))
continue;
if (local_entry->stance_type != BCEnum::StT_Aggressive)
if (local_entry->stance_type != BCEnum::StT_Defensive)
continue;
for (auto bot_iter = sbl.begin(); bot_iter != sbl.end(); ) {
@@ -3332,7 +3334,7 @@ void bot_command_defensive(Client *c, const Seperator *sep)
}
}
c->Message(Chat::White, "%i of %i bots have used defensive disciplines", success_count, candidate_count);
c->Message(Chat::White, "%i of %i bots have attempted to use defensive disciplines", success_count, candidate_count);
}
void bot_command_depart(Client *c, const Seperator *sep)
@@ -3353,8 +3355,8 @@ void bot_command_depart(Client *c, const Seperator *sep)
std::string destination = sep->arg[1];
if (!destination.compare("list")) {
Bot* my_druid_bot = ActionableBots::AsGroupMember_ByClass(c, c, DRUID);
Bot* my_wizard_bot = ActionableBots::AsGroupMember_ByClass(c, c, WIZARD);
Bot* my_druid_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Druid);
Bot* my_wizard_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Wizard);
helper_command_depart_list(c, my_druid_bot, my_wizard_bot, local_list, single);
return;
}
@@ -3906,16 +3908,77 @@ void bot_command_invisibility(Client *c, const Seperator *sep)
void bot_command_item_use(Client* c, const Seperator* sep)
{
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s ([empty])", sep->arg[0]);
c->Message(Chat::White, "usage: [%s empty] will display only bots that can use the item in an empty slot.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s byclass classID] - Example: [%s byclass 7] will display only bots that match the class that can use the item. Example is a Monk, use [^create help] for a list of class IDs.", sep->arg[0], sep->arg[0]);
c->Message(Chat::White, "usage: [%s casteronly] will display only caster bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s hybridonly] will display only hybrid bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s meleeonly] will display only melee bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s wiscasteronly] will display only Wisdom-based Caster bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s intcasteronly] will display only Intelligence-based Caster bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s plateonly] will display only Plate-wearing bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s chainonly] will display only Chain-wearing bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s leatheronly] will display only Leather-wearing bots that can use the item.", sep->arg[0]);
c->Message(Chat::White, "usage: [%s clothonly] will display only Cloth-wearing bots that can use the item.", sep->arg[0]);
return;
}
bool empty_only = false;
int8 class_mask = 0;
bool caster_only = false;
bool hybrid_only = false;
bool melee_only = false;
bool wis_caster_only = false;
bool int_caster_only = false;
bool plate_only = false;
bool chain_only = false;
bool leather_only = false;
bool cloth_only = false;
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
if (arg1.compare("empty") == 0) {
empty_only = true;
}
else if (arg1.compare("byclass") == 0) {
if (Strings::IsNumber(sep->arg[2])) {
class_mask = Strings::ToUnsignedInt(sep->arg[2]);
if (!(class_mask >= Class::Warrior && class_mask <= Class::Berserker)) {
c->Message(Chat::White, "Invalid class range, you must choose between 1 (Warrior) and 15 (Beastlord)");
return;
}
}
}
else if (arg1.compare("casteronly") == 0) {
caster_only = true;
}
else if (arg1.compare("hybridonly") == 0) {
hybrid_only = true;
}
else if (arg1.compare("meleeonly") == 0) {
melee_only = true;
}
else if (arg1.compare("wiscasteronly") == 0) {
wis_caster_only = true;
}
else if (arg1.compare("intcasteronly") == 0) {
int_caster_only = true;
}
else if (arg1.compare("plateonly") == 0) {
plate_only = true;
}
else if (arg1.compare("chainonly") == 0) {
chain_only = true;
}
else if (arg1.compare("leatheronly") == 0) {
leather_only = true;
}
else if (arg1.compare("clothonly") == 0) {
cloth_only = true;
}
else if (!arg1.empty()) {
c->Message(Chat::White, "Please choose the correct subtype. For help use %s help.", sep->arg[0]);
return;
}
const auto item_instance = c->GetInv().GetItem(EQ::invslot::slotCursor);
if (!item_instance) {
c->Message(Chat::White, "No item found on cursor!");
@@ -3948,11 +4011,41 @@ void bot_command_item_use(Client* c, const Seperator* sep)
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
if (class_mask) {
ActionableBots::Filter_ByClasses(c, sbl, GetPlayerClassBit(class_mask));
}
for (const auto& bot_iter : sbl) {
if (!bot_iter) {
continue;
}
if (caster_only && !IsCasterClass(bot_iter->GetClass())) {
continue;
}
if (hybrid_only && !IsSpellFighterClass(bot_iter->GetClass())) {
continue;
}
if (melee_only && !IsNonSpellFighterClass(bot_iter->GetClass())) {
continue;
}
if (wis_caster_only && !IsWISCasterClass(bot_iter->GetClass())) {
continue;
}
if (int_caster_only && !IsINTCasterClass(bot_iter->GetClass())) {
continue;
}
if (plate_only && !IsPlateClass(bot_iter->GetClass())) {
continue;
}
if (chain_only && !IsChainClass(bot_iter->GetClass())) {
continue;
}
if (leather_only && !IsLeatherClass(bot_iter->GetClass())) {
continue;
}
if (cloth_only && !IsClothClass(bot_iter->GetClass())) {
continue;
}
if (((~item_data->Races) & GetPlayerRaceBit(bot_iter->GetRace())) || ((~item_data->Classes) & GetPlayerClassBit(bot_iter->GetClass()))) {
continue;
}
@@ -3997,7 +4090,8 @@ void bot_command_item_use(Client* c, const Seperator* sep)
);
bot_iter->DoAnim(29);
} else if (!equipped_item) {
}
else if (!equipped_item) {
c->Message(
Chat::Say,
fmt::format(
@@ -4647,17 +4741,17 @@ void bot_command_pull(Client *c, const Seperator *sep)
}
switch (bot_iter->GetClass()) {
case ROGUE:
case MONK:
case BARD:
case RANGER:
case Class::Rogue:
case Class::Monk:
case Class::Bard:
case Class::Ranger:
bot_puller = bot_iter;
break;
case WARRIOR:
case SHADOWKNIGHT:
case PALADIN:
case BERSERKER:
case BEASTLORD:
case Class::Warrior:
case Class::ShadowKnight:
case Class::Paladin:
case Class::Berserker:
case Class::Beastlord:
if (!bot_puller) {
bot_puller = bot_iter;
@@ -4665,22 +4759,22 @@ void bot_command_pull(Client *c, const Seperator *sep)
}
switch (bot_puller->GetClass()) {
case DRUID:
case SHAMAN:
case CLERIC:
case WIZARD:
case NECROMANCER:
case MAGICIAN:
case ENCHANTER:
case Class::Druid:
case Class::Shaman:
case Class::Cleric:
case Class::Wizard:
case Class::Necromancer:
case Class::Magician:
case Class::Enchanter:
bot_puller = bot_iter;
default:
continue;
}
continue;
case DRUID:
case SHAMAN:
case CLERIC:
case Class::Druid:
case Class::Shaman:
case Class::Cleric:
if (!bot_puller) {
bot_puller = bot_iter;
@@ -4688,20 +4782,20 @@ void bot_command_pull(Client *c, const Seperator *sep)
}
switch (bot_puller->GetClass()) {
case WIZARD:
case NECROMANCER:
case MAGICIAN:
case ENCHANTER:
case Class::Wizard:
case Class::Necromancer:
case Class::Magician:
case Class::Enchanter:
bot_puller = bot_iter;
default:
continue;
}
continue;
case WIZARD:
case NECROMANCER:
case MAGICIAN:
case ENCHANTER:
case Class::Wizard:
case Class::Necromancer:
case Class::Magician:
case Class::Enchanter:
if (!bot_puller) {
bot_puller = bot_iter;
}
@@ -5194,6 +5288,194 @@ void bot_command_taunt(Client *c, const Seperator *sep)
}
}
void bot_command_timer(Client* c, const Seperator* sep)
{
if (helper_command_alias_fail(c, "bot_command_timer", sep->arg[0], "timer"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s [clear | has | set] [disc | item | spell] [timer ID | item ID | spell ID | all] [optional ms for set] [actionable].", sep->arg[0]);
c->Message(Chat::White, "When setting, you can leave the value blank to use the default for the item or specify a value in ms to set the timer to.");
c->Message(Chat::White, "Returns or sets the provided timer(s) for the selected bot(s) or clears the selected timer(s) for the selected bot(s).");
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
std::string arg3 = sep->arg[3];
int ab_arg = 4;
bool clear = false;
bool has = false;
bool set = false;
bool disc = false;
bool item = false;
bool spell = false;
uint32 timer_id = 0;
uint32 timer_value = 0;
bool all = false;
if (!arg1.compare("clear")) {
clear = true;
}
else if (!arg1.compare("has")) {
has = true;
}
else if (!arg1.compare("set")) {
set = true;
}
else {
c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]);
return;
}
if (!arg2.compare("disc")) {
disc = true;
}
else if (!arg2.compare("item")) {
item = true;
}
else if (!arg2.compare("spell")) {
spell = true;
}
else {
c->Message(Chat::White, "Incorrect timer type, use %s help for a list of options.", sep->arg[0]);
return;
}
if (sep->IsNumber(3)) {
timer_id = atoi(sep->arg[3]);
if (timer_id < 0) {
c->Message(Chat::White, "You cannot use negative numbers.");
return;
}
}
else if (!arg3.compare("all")) {
if (has || set) {
c->Message(Chat::White, "You can only use 'all' for clearing timers.");
return;
}
all = true;
}
else {
c->Message(Chat::White, "Incorrect ID option, use %s help for a list of options.", sep->arg[0]);
return;
}
if (set) {
if (sep->IsNumber(4)) {
ab_arg = 5;
timer_value = atoi(sep->arg[4]);
if (timer_value <= 0) {
c->Message(Chat::White, "You cannot use 0 or negative numbers.");
return;
}
}
}
std::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
for (auto my_bot : sbl) {
bool found = false;
if (clear) {
c->Message(
Chat::White,
fmt::format(
"{} says, 'Clearing {} timer{}'",
my_bot->GetCleanName(),
disc ? "Discipline" : item ? "Item" : "Spell",
(all ? "s." : ".")
).c_str()
);
if (disc) {
my_bot->ClearDisciplineReuseTimer(timer_id);
}
else if (item) {
my_bot->ClearItemReuseTimer(timer_id);
}
else if (spell) {
my_bot->ClearSpellRecastTimer(timer_id);
}
}
else if (has) {
uint32 remaining_time;
std::string time_string = "";
if (disc) {
if (!my_bot->CheckDisciplineReuseTimer(timer_id)) {
remaining_time = my_bot->GetDisciplineReuseRemainingTime(timer_id) / 1000;
time_string = Strings::SecondsToTime(remaining_time);
found = true;
}
}
else if (item) {
if (!my_bot->CheckItemReuseTimer(timer_id)) {
remaining_time = my_bot->GetItemReuseRemainingTime(timer_id) / 1000;
time_string = Strings::SecondsToTime(remaining_time);
found = true;
}
}
else if (spell) {
if (!my_bot->CheckSpellRecastTimer(timer_id)) {
remaining_time = my_bot->GetSpellRecastRemainingTime(timer_id) / 1000;
time_string = Strings::SecondsToTime(remaining_time);
found = true;
}
}
c->Message(
Chat::White,
fmt::format(
"{} says, 'I {}{}{}'",
my_bot->GetCleanName(),
(!found ? " do not have that timer currently" : " have "),
(!found ? "" : time_string),
(!found ? "." : " remaining.")
).c_str()
);
}
else if (set) {
c->Message(
Chat::White,
fmt::format(
"{} says, 'Setting {} timer{} for {} to {}.'",
my_bot->GetCleanName(),
disc ? "Discipline" : item ? "Item" : "Spell",
(all ? "s" : ""),
timer_id,
timer_value ? std::to_string(timer_value) : "the default value"
).c_str()
);
if (disc) {
my_bot->ClearDisciplineReuseTimer(timer_id);
my_bot->SetDisciplineReuseTimer(timer_id, timer_value);
}
else if (item) {
my_bot->ClearItemReuseTimer(timer_id);
my_bot->SetItemReuseTimer(timer_id, timer_value);
}
else if (spell) {
my_bot->ClearSpellRecastTimer(timer_id);
my_bot->SetSpellRecastTimer(timer_id, timer_value);
}
}
}
}
void bot_command_track(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_command_track", sep->arg[0], "track"))
@@ -5210,15 +5492,15 @@ void bot_command_track(Client *c, const Seperator *sep)
std::list<Bot*> sbl;
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
uint16 class_mask = (PLAYER_CLASS_RANGER_BIT | PLAYER_CLASS_DRUID_BIT | PLAYER_CLASS_BARD_BIT);
uint16 class_mask = (player_class_bitmasks[Class::Ranger] | player_class_bitmasks[Class::Druid] | player_class_bitmasks[Class::Bard]);
ActionableBots::Filter_ByClasses(c, sbl, class_mask);
Bot* my_bot = ActionableBots::AsSpawned_ByMinLevelAndClass(c, sbl, 1, RANGER);
Bot* my_bot = ActionableBots::AsSpawned_ByMinLevelAndClass(c, sbl, 1, Class::Ranger);
if (tracking_scope.empty()) {
if (!my_bot)
my_bot = ActionableBots::AsSpawned_ByMinLevelAndClass(c, sbl, 20, DRUID);
my_bot = ActionableBots::AsSpawned_ByMinLevelAndClass(c, sbl, 20, Class::Druid);
if (!my_bot)
my_bot = ActionableBots::AsSpawned_ByMinLevelAndClass(c, sbl, 35, BARD);
my_bot = ActionableBots::AsSpawned_ByMinLevelAndClass(c, sbl, 35, Class::Bard);
}
if (!my_bot) {
c->Message(Chat::White, "No bots are capable of performing this action");
@@ -5229,7 +5511,7 @@ void bot_command_track(Client *c, const Seperator *sep)
bool track_named = false;
std::string tracking_msg;
switch (my_bot->GetClass()) {
case RANGER:
case Class::Ranger:
if (!tracking_scope.compare("local")) {
base_distance = 30;
tracking_msg = "Local tracking...";
@@ -5244,11 +5526,11 @@ void bot_command_track(Client *c, const Seperator *sep)
tracking_msg = "Advanced tracking...";
}
break;
case DRUID:
case Class::Druid:
base_distance = 30;
tracking_msg = "Local tracking...";
break;
case BARD:
case Class::Bard:
base_distance = 20;
tracking_msg = "Near tracking...";
break;
@@ -6612,7 +6894,7 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep)
).c_str()
);
for (uint8 class_id = WARRIOR; class_id <= BERSERKER; class_id++) {
for (uint8 class_id = Class::Warrior; class_id <= Class::Berserker; class_id++) {
auto class_creation_limit = c->GetBotCreationLimit(class_id);
if (class_creation_limit != overall_bot_creation_limit) {
@@ -6865,7 +7147,7 @@ void bot_subcommand_bot_spawn(Client *c, const Seperator *sep)
std::string bot_name = sep->arg[1];
uint32 bot_id = 0;
uint8 bot_class = NO_CLASS;
uint8 bot_class = Class::None;
if (!database.botdb.LoadBotID(c->CharacterID(), bot_name, bot_id, bot_class)) {
c->Message(
Chat::White,
@@ -6972,22 +7254,22 @@ void bot_subcommand_bot_spawn(Client *c, const Seperator *sep)
static std::string bot_spawn_message[17] = {
"I am ready to fight!", // DEFAULT
"A solid weapon is my ally!", // WARRIOR
"The pious shall never die!", // CLERIC
"I am the symbol of Light!", // PALADIN
"There are enemies near!", // RANGER
"Out of the shadows, I step!", // SHADOWKNIGHT
"Nature's fury shall be wrought!", // DRUID
"Your punishment will be my fist!", // MONK
"A solid weapon is my ally!", // Class::Warrior
"The pious shall never die!", // Class::Cleric
"I am the symbol of Light!", // Class::Paladin
"There are enemies near!", // Class::Ranger
"Out of the shadows, I step!", // Class::ShadowKnight
"Nature's fury shall be wrought!", // Class::Druid
"Your punishment will be my fist!", // Class::Monk
"Music is the overture of battle! ", // BARD
"Daggers into the backs of my enemies!", // ROGUE
"More bones to grind!", // SHAMAN
"Death is only the beginning!", // NECROMANCER
"I am the harbinger of demise!", // WIZARD
"The elements are at my command!", // MAGICIAN
"No being can resist my charm!", // ENCHANTER
"Battles are won by hand and paw!", // BEASTLORD
"My bloodthirst shall not be quenched!" // BERSERKER
"Daggers into the backs of my enemies!", // Class::Rogue
"More bones to grind!", // Class::Shaman
"Death is only the beginning!", // Class::Necromancer
"I am the harbinger of demise!", // Class::Wizard
"The elements are at my command!", // Class::Magician
"No being can resist my charm!", // Class::Enchanter
"Battles are won by hand and paw!", // Class::Beastlord
"My bloodthirst shall not be quenched!" // Class::Berserker
};
uint8 message_index = 0;
@@ -7257,7 +7539,7 @@ void bot_subcommand_bot_toggle_archer(Client *c, const Seperator *sep)
}
bot_iter->ChangeBotArcherWeapons(bot_iter->IsBotArcher());
if (bot_iter->GetClass() == RANGER && bot_iter->GetLevel() >= 61) {
if (bot_iter->GetClass() == Class::Ranger && bot_iter->GetLevel() >= 61) {
bot_iter->SetRangerAutoWeaponSelect(bot_iter->IsBotArcher());
}
}
@@ -7467,7 +7749,7 @@ void bot_subcommand_circle(Client *c, const Seperator *sep)
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Depart, DRUID);
helper_send_usage_required_bots(c, BCEnum::SpT_Depart, Class::Druid);
return;
}
@@ -7478,7 +7760,7 @@ void bot_subcommand_circle(Client *c, const Seperator *sep)
std::string destination = sep->arg[1];
if (!destination.compare("list")) {
auto my_druid_bot = ActionableBots::AsGroupMember_ByClass(c, c, DRUID);
auto my_druid_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Druid);
helper_command_depart_list(c, my_druid_bot, nullptr, local_list, single);
return;
}
@@ -7497,7 +7779,7 @@ void bot_subcommand_circle(Client *c, const Seperator *sep)
auto local_entry = list_iter->SafeCastToDepart();
if (helper_spell_check_fail(local_entry))
continue;
if (local_entry->caster_class != DRUID)
if (local_entry->caster_class != Class::Druid)
continue;
if (local_entry->single != single)
continue;
@@ -8988,7 +9270,7 @@ void bot_subcommand_pet_remove(Client *c, const Seperator *sep)
if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None)
return;
uint16 class_mask = (PLAYER_CLASS_DRUID_BIT | PLAYER_CLASS_NECROMANCER_BIT | PLAYER_CLASS_ENCHANTER_BIT);
uint16 class_mask = (player_class_bitmasks[Class::Druid] | player_class_bitmasks[Class::Necromancer] | player_class_bitmasks[Class::Enchanter]);
ActionableBots::Filter_ByClasses(c, sbl, class_mask);
if (sbl.empty()) {
c->Message(Chat::White, "You have no spawned bots capable of charming");
@@ -9068,7 +9350,7 @@ void bot_subcommand_pet_set_type(Client *c, const Seperator *sep)
if (ActionableBots::PopulateSBL(c, sep->arg[2], sbl, ab_mask, sep->arg[3]) == ActionableBots::ABT_None)
return;
uint16 class_mask = PLAYER_CLASS_MAGICIAN_BIT;
uint16 class_mask = player_class_bitmasks[Class::Magician];
ActionableBots::Filter_ByClasses(c, sbl, class_mask);
if (sbl.empty()) {
c->Message(Chat::White, "You have no spawned Magician bots");
@@ -9103,7 +9385,7 @@ void bot_subcommand_portal(Client *c, const Seperator *sep)
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]);
helper_send_usage_required_bots(c, BCEnum::SpT_Depart, WIZARD);
helper_send_usage_required_bots(c, BCEnum::SpT_Depart, Class::Wizard);
return;
}
@@ -9114,7 +9396,7 @@ void bot_subcommand_portal(Client *c, const Seperator *sep)
std::string destination = sep->arg[1];
if (!destination.compare("list")) {
auto my_wizard_bot = ActionableBots::AsGroupMember_ByClass(c, c, WIZARD);
auto my_wizard_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Wizard);
helper_command_depart_list(c, nullptr, my_wizard_bot, local_list, single);
return;
}
@@ -9133,7 +9415,7 @@ void bot_subcommand_portal(Client *c, const Seperator *sep)
auto local_entry = list_iter->SafeCastToDepart();
if (helper_spell_check_fail(local_entry))
continue;
if (local_entry->caster_class != WIZARD)
if (local_entry->caster_class != Class::Wizard)
continue;
if (local_entry->single != single)
continue;
@@ -9427,26 +9709,26 @@ void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot)
return;
switch (my_bot->GetClass()) {
case WARRIOR:
case CLERIC:
case PALADIN:
case RANGER:
case SHADOWKNIGHT:
case DRUID:
case MONK:
case Class::Warrior:
case Class::Cleric:
case Class::Paladin:
case Class::Ranger:
case Class::ShadowKnight:
case Class::Druid:
case Class::Monk:
bot_owner->Message(Chat::White, "%s has no out-of-combat behavior defined", my_bot->GetCleanName());
break;
case BARD:
case Class::Bard:
bot_owner->Message(Chat::White, "%s will %s use out-of-combat behavior for bard songs", my_bot->GetCleanName(), ((my_bot->GetAltOutOfCombatBehavior()) ? ("now") : ("no longer")));
break;
case ROGUE:
case SHAMAN:
case NECROMANCER:
case WIZARD:
case MAGICIAN:
case ENCHANTER:
case BEASTLORD:
case BERSERKER:
case Class::Rogue:
case Class::Shaman:
case Class::Necromancer:
case Class::Wizard:
case Class::Magician:
case Class::Enchanter:
case Class::Beastlord:
case Class::Berserker:
bot_owner->Message(Chat::White, "%s has no out-of-combat behavior defined", my_bot->GetCleanName());
break;
default:
@@ -10438,6 +10720,61 @@ void bot_command_caster_range(Client* c, const Seperator* sep)
}
}
void bot_command_click_item(Client* c, const Seperator* sep)
{
if (!RuleB(Bots, BotsCanClickItems)) {
c->Message(Chat::White, "The ability for bots to click equipped items is currently disabled.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: <slot id> %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
c->Message(Chat::White, "This will cause the selected bots to click the item in the given slot ID.");
c->Message(Chat::White, "Use ^invlist to see their items along with slot IDs.");
return;
}
if (!sep->IsNumber(1)) {
c->Message(Chat::Yellow, "You must specify a slot ID. Use %s help for more information.", sep->arg[0]);
return;
}
const int ab_mask = ActionableBots::ABM_Type1;
int ab_arg = 1;
uint32 slot_id = 0;
if (sep->IsNumber(1)) {
ab_arg = 2;
slot_id = atoi(sep->arg[1]);
if (slot_id < EQ::invslot::EQUIPMENT_BEGIN || slot_id > EQ::invslot::EQUIPMENT_END) {
c->Message(Chat::Yellow, "You must specify a valid inventory slot from 0 to 22. Use %s help for more information", sep->arg[0]);
return;
}
}
std::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
return;
}
sbl.remove(nullptr);
for (auto my_bot : sbl) {
if (RuleI(Bots, BotsClickItemsMinLvl) > my_bot->GetLevel()) {
c->Message(Chat::White, "%s must be level %i to use clickable items.", my_bot->GetCleanName(), RuleI(Bots, BotsClickItemsMinLvl));
continue;
}
my_bot->TryItemClick(slot_id);
}
}
void bot_command_pickpocket(Client *c, const Seperator *sep)
{
if (helper_command_disabled(c, RuleB(Bots, AllowPickpocketCommand), "pickpocket")) {
@@ -10457,8 +10794,8 @@ void bot_command_pickpocket(Client *c, const Seperator *sep)
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
// Check for capable rogue
ActionableBots::Filter_ByClasses(c, sbl, PLAYER_CLASS_ROGUE_BIT);
Bot *my_bot = ActionableBots::AsSpawned_ByMinLevelAndClass(c, sbl, 7, ROGUE);
ActionableBots::Filter_ByClasses(c, sbl, player_class_bitmasks[Class::Rogue]);
Bot *my_bot = ActionableBots::AsSpawned_ByMinLevelAndClass(c, sbl, 7, Class::Rogue);
if (!my_bot) {
c->Message(Chat::White, "No bots are capable of performing this action");
return;
+3 -1
View File
@@ -554,6 +554,7 @@ void bot_command_bind_affinity(Client *c, const Seperator *sep);
void bot_command_bot(Client *c, const Seperator *sep);
void bot_command_caster_range(Client* c, const Seperator* sep);
void bot_command_charm(Client *c, const Seperator *sep);
void bot_command_click_item(Client* c, const Seperator* sep);
void bot_command_cure(Client *c, const Seperator *sep);
void bot_command_defensive(Client *c, const Seperator *sep);
void bot_command_depart(Client *c, const Seperator *sep);
@@ -595,6 +596,7 @@ void bot_command_enforce_spell_list(Client* c, const Seperator* sep);
void bot_command_summon_corpse(Client *c, const Seperator *sep);
void bot_command_suspend(Client *c, const Seperator *sep);
void bot_command_taunt(Client *c, const Seperator *sep);
void bot_command_timer(Client* c, const Seperator* sep);
void bot_command_track(Client *c, const Seperator *sep);
void bot_command_view_combos(Client *c, const Seperator *sep);
void bot_command_water_breathing(Client *c, const Seperator *sep);
@@ -677,7 +679,7 @@ void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_b
bool helper_is_help_or_usage(const char* arg);
bool helper_no_available_bots(Client *bot_owner, Bot *my_bot = nullptr);
void helper_send_available_subcommands(Client *bot_owner, const char* command_simile, const std::list<const char*>& subcommand_list);
void helper_send_usage_required_bots(Client *bot_owner, BCEnum::SpType spell_type, uint8 bot_class = NO_CLASS);
void helper_send_usage_required_bots(Client *bot_owner, BCEnum::SpType spell_type, uint8 bot_class = Class::None);
bool helper_spell_check_fail(STBaseEntry* local_entry);
bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::SpType spell_type);
#endif
+79 -56
View File
@@ -24,6 +24,7 @@
#include "../common/repositories/bot_data_repository.h"
#include "../common/repositories/bot_inventories_repository.h"
#include "../common/repositories/bot_timers_repository.h"
#include "zonedb.h"
#include "bot.h"
@@ -141,7 +142,7 @@ bool BotDatabase::LoadBotSpellCastingChances()
if (spell_type_index >= Bot::SPELL_TYPE_COUNT)
continue;
uint8 class_index = Strings::ToInt(row[1]);
if (class_index < WARRIOR || class_index > BERSERKER)
if (class_index < Class::Warrior || class_index > Class::Berserker)
continue;
--class_index;
uint8 stance_index = Strings::ToInt(row[2]);
@@ -224,7 +225,7 @@ bool BotDatabase::QueryBotCount(const uint32 owner_id, int class_id, uint32& bot
auto row = results.begin();
bot_count = Strings::ToUnsignedInt(row[0]);
if (EQ::ValueWithin(class_id, WARRIOR, BERSERKER)) {
if (EQ::ValueWithin(class_id, Class::Warrior, Class::Berserker)) {
query = fmt::format(
"SELECT COUNT(`bot_id`) FROM `bot_data` WHERE `owner_id` = {} AND `class` = {}",
owner_id,
@@ -917,45 +918,39 @@ bool BotDatabase::LoadTimers(Bot* bot_inst)
if (!bot_inst)
return false;
query = StringFormat(
"SELECT"
" IfNull(bt.`timer_id`, '0') As timer_id,"
" IfNull(bt.`timer_value`, '0') As timer_value,"
" IfNull(MAX(sn.`recast_time`), '0') AS MaxTimer"
" FROM `bot_timers` bt, `spells_new` sn"
" WHERE bt.`bot_id` = '%u' AND sn.`EndurTimerIndex` = ("
"SELECT case"
" WHEN timer_id > '%i' THEN timer_id - '%i'"
" ELSE timer_id END AS timer_id"
" FROM `bot_timers` WHERE `timer_id` = bt.`timer_id` AND `bot_id` = bt.`bot_id`" // double-check validity
")"
" AND sn.`classes%i` <= '%i'",
bot_inst->GetBotID(),
(DisciplineReuseStart - 1),
(DisciplineReuseStart - 1),
bot_inst->GetClass(),
bot_inst->GetLevel()
auto timers = BotTimersRepository::GetWhere(
database,
fmt::format("bot_id = {}", bot_inst->GetBotID())
);
auto results = database.QueryDatabase(query);
if (!results.Success())
return false;
if (!results.RowCount())
return true;
uint32* bot_timers = bot_inst->GetTimers();
if (!bot_timers)
return false;
std::vector<BotTimer_Struct> bot_timers;
int timer_id = 0;
uint32 timer_value = 0;
uint32 max_value = 0;
for (auto row = results.begin(); row != results.end(); ++row) {
timer_id = Strings::ToInt(row[0]) - 1;
timer_value = Strings::ToInt(row[1]);
max_value = Strings::ToInt(row[2]);
BotTimer_Struct t{};
t.timer_id = 0;
t.timer_value = 0;
t.recast_time = 0;
t.is_spell = false;
t.is_disc = false;
t.spell_id = 0;
t.is_item = false;
t.item_id = 0;
if (timer_id >= 0 && timer_id < MaxTimer && timer_value < (Timer::GetCurrentTime() + max_value))
bot_timers[timer_id] = timer_value;
for (auto& timer : timers) {
if (t.timer_value < (Timer::GetCurrentTime() + t.recast_time)) {
t.timer_id = timer.timer_id;
t.timer_value = timer.timer_value;
t.recast_time = timer.recast_time;
t.is_spell = timer.is_spell ? true : false;
t.is_disc = timer.is_disc ? true : false;
t.spell_id = timer.spell_id;
t.is_item = timer.is_item ? true : false;
t.item_id = timer.item_id;
bot_timers.push_back(t);
}
}
if (!bot_timers.empty()) {
bot_inst->SetBotTimers(bot_timers);
}
return true;
@@ -963,26 +958,56 @@ bool BotDatabase::LoadTimers(Bot* bot_inst)
bool BotDatabase::SaveTimers(Bot* bot_inst)
{
if (!bot_inst)
if (!bot_inst) {
return false;
}
if (!DeleteTimers(bot_inst->GetBotID()))
if (!DeleteTimers(bot_inst->GetBotID())) {
return false;
}
uint32* bot_timers = bot_inst->GetTimers();
if (!bot_timers)
return false;
std::vector<BotTimer_Struct> bot_timers = bot_inst->GetBotTimers();
for (int timer_index = 0; timer_index < MaxTimer; ++timer_index) {
if (bot_timers[timer_index] <= Timer::GetCurrentTime())
continue;
if (bot_timers.empty()) {
return true;
}
query = fmt::format(
"REPLACE INTO `bot_timers` (`bot_id`, `timer_id`, `timer_value`) VALUES ('{}', '{}', '{}')",
bot_inst->GetBotID(), (timer_index + 1), bot_timers[timer_index]
std::vector<BotTimersRepository::BotTimers> timers;
if (!bot_timers.empty()) {
for (auto & bot_timer : bot_timers) {
if (bot_timer.timer_value <= Timer::GetCurrentTime()) {
continue;
}
auto t = BotTimersRepository::BotTimers{
.bot_id = bot_inst->GetBotID(),
.timer_id = bot_timer.timer_id,
.timer_value = bot_timer.timer_value,
.recast_time = bot_timer.recast_time,
.is_spell = bot_timer.is_spell ? true : false,
.is_disc = bot_timer.is_disc ? true : false,
.spell_id = bot_timer.spell_id,
.is_item = bot_timer.is_item ? true : false,
.item_id = bot_timer.item_id
};
timers.push_back(t);
}
if (timers.empty()) {
return true;
}
// delete existing
BotTimersRepository::DeleteWhere(
database,
fmt::format("bot_id = {}", bot_inst->GetBotID())
);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
// bulk insert current
auto success = BotTimersRepository::InsertMany(database, timers);
if (!success) {
DeleteTimers(bot_inst->GetBotID());
return false;
}
@@ -993,13 +1018,11 @@ bool BotDatabase::SaveTimers(Bot* bot_inst)
bool BotDatabase::DeleteTimers(const uint32 bot_id)
{
if (!bot_id)
if (!bot_id) {
return false;
}
query = StringFormat("DELETE FROM `bot_timers` WHERE `bot_id` = '%u'", bot_id);
auto results = database.QueryDatabase(query);
if (!results.Success())
return false;
BotTimersRepository::DeleteWhere(database, fmt::format("bot_id = {}", bot_id));
return true;
}
@@ -2517,7 +2540,7 @@ uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_ind
{
if (spell_type_index >= Bot::SPELL_TYPE_COUNT)
return 0;
if (class_index >= PLAYER_CLASS_COUNT)
if (class_index >= Class::PLAYER_CLASS_COUNT)
return 0;
if (stance_index >= EQ::constants::STANCE_TYPE_COUNT)
return 0;
+11
View File
@@ -81,4 +81,15 @@ struct BotSpells_Struct {
uint8 bucket_comparison;
};
struct BotTimer_Struct {
uint32 timer_id;
uint32 timer_value;
uint32 recast_time;
bool is_spell;
bool is_disc;
uint16 spell_id;
bool is_item;
uint32 item_id;
};
#endif // BOT_STRUCTS
+112 -177
View File
@@ -101,14 +101,14 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
bool Bot::BotCastSong(Mob* tar, uint8 botLevel) {
bool casted_spell = false;
if (GetClass() != BARD || tar != this || IsEngaged()) // Out-of-Combat songs can not be cast in combat
if (GetClass() != Class::Bard || tar != this || IsEngaged()) // Out-of-Combat songs can not be cast in combat
return casted_spell;
for (auto botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_OutOfCombatBuffSong);
auto iter : botSongList) {
if (!iter.SpellId)
continue;
if (!CheckSpellRecastTimers(this, iter.SpellIndex))
if (!CheckSpellRecastTimer(iter.SpellId))
continue;
if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType()))
continue;
@@ -142,7 +142,7 @@ bool Bot::BotCastCombatSong(Mob* tar, uint8 botLevel) {
auto iter : botSongList) {
if (!iter.SpellId)
continue;
if (!CheckSpellRecastTimers(this, iter.SpellIndex))
if (!CheckSpellRecastTimer(iter.SpellId))
continue;
if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType()))
continue;
@@ -169,12 +169,12 @@ bool Bot::BotCastCombatSong(Mob* tar, uint8 botLevel) {
bool Bot::BotCastHateReduction(Mob* tar, uint8 botLevel, const BotSpell& botSpell) {
bool casted_spell = false;
if (GetClass() == BARD) {
if (GetClass() == Class::Bard) {
std::list<BotSpell_wPriority> botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_HateRedux);
for (auto iter : botSongList) {
if (!iter.SpellId)
continue;
if (!CheckSpellRecastTimers(this, iter.SpellIndex))
if (!CheckSpellRecastTimer(iter.SpellId))
continue;
if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType()))
continue;
@@ -220,7 +220,7 @@ bool Bot::BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, Raid* raid)
casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontCureMeBeforeTime);
if (casted_spell && botClass != BARD) {
if (casted_spell && botClass != Class::Bard) {
if (IsGroupSpell(botSpell.SpellId)) {
if (HasGroup()) {
Group const* group = GetGroup();
@@ -301,7 +301,7 @@ bool Bot::BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
}
switch (botClass) {
case BARD: {
case Class::Bard: {
// probably needs attackable check
for (
auto botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_Slow);
@@ -312,7 +312,7 @@ bool Bot::BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
continue;
}
if (!CheckSpellRecastTimers(this, iter.SpellIndex)) {
if (!CheckSpellRecastTimer(iter.SpellId)) {
continue;
}
@@ -339,12 +339,12 @@ bool Bot::BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
break;
}
case ENCHANTER: {
case Class::Enchanter: {
botSpell = GetBestBotSpellForMagicBasedSlow(this);
break;
}
case SHAMAN:
case BEASTLORD: {
case Class::Shaman:
case Class::Beastlord: {
botSpell = GetBestBotSpellForDiseaseBasedSlow(this);
if (botSpell.SpellId == 0 || ((tar->GetMR() - 50) < (tar->GetDR() + spells[botSpell.SpellId].resist_difficulty)))
@@ -364,7 +364,7 @@ bool Bot::BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
}
if (casted_spell && GetClass() != BARD) {
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);
@@ -391,7 +391,7 @@ bool Bot::BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const b
return casted_spell;
}
if (GetClass() == BARD) {
if (GetClass() == Class::Bard) {
std::list<BotSpell_wPriority> dotList = GetPrioritizedBotSpellsBySpellType(this, SpellType_DOT);
const int maxDotSelect = 5;
@@ -403,7 +403,7 @@ bool Bot::BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const b
continue;
}
if (CheckSpellRecastTimers(this, s.SpellIndex))
if (CheckSpellRecastTimer(s.SpellId))
{
if (!(!tar->IsImmuneToSpell(s.SpellId, this) && tar->CanBuffStack(s.SpellId, botLevel, true) >= 0)) {
@@ -438,7 +438,7 @@ bool Bot::BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const b
continue;
}
if (CheckSpellRecastTimers(this, s.SpellIndex)) {
if (CheckSpellRecastTimer(s.SpellId)) {
if (!(!tar->IsImmuneToSpell(s.SpellId, this) &&
tar->CanBuffStack(s.SpellId, botLevel, true) >= 0)) {
@@ -569,7 +569,7 @@ bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
(tar == this && spells[s.SpellId].target_type != ST_TargetsTarget) ||
spells[s.SpellId].target_type == ST_Group ||
spells[s.SpellId].target_type == ST_GroupTeleport ||
(botClass == BARD && spells[s.SpellId].target_type == ST_AEBard)
(botClass == Class::Bard && spells[s.SpellId].target_type == ST_AEBard)
) &&
!tar->IsImmuneToSpell(s.SpellId, this) &&
tar->CanBuffStack(s.SpellId, botLevel, true) >= 0
@@ -582,7 +582,7 @@ bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
if (
((IsEffectInSpell(s.SpellId, SE_Levitate) && !zone->CanLevitate()) ||
(IsEffectInSpell(s.SpellId, SE_MovementSpeed) && !zone->CanCastOutdoor())) &&
(botClass != BARD || !IsSpellUsableInThisZoneType(s.SpellId, zone->GetZoneType()))
(botClass != Class::Bard || !IsSpellUsableInThisZoneType(s.SpellId, zone->GetZoneType()))
) {
continue;
}
@@ -625,7 +625,7 @@ bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
}
}
// TODO: Add TriggerSpell Support for Exchanter Runes
if (botClass == ENCHANTER && IsEffectInSpell(s.SpellId, SE_Rune)) {
if (botClass == Class::Enchanter && IsEffectInSpell(s.SpellId, SE_Rune)) {
float manaRatioToCast = 75.0f;
switch(GetBotStance()) {
@@ -652,7 +652,7 @@ bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
}
}
if (CheckSpellRecastTimers(this, s.SpellIndex)) {
if (CheckSpellRecastTimer(s.SpellId)) {
uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore();
casted_spell = AIDoSpellCast(s.SpellIndex, tar, s.ManaCost, &TempDontBuffMeBefore);
if (TempDontBuffMeBefore != tar->DontBuffMeBefore()) {
@@ -671,7 +671,7 @@ bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
bool Bot::BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell) {
bool casted_spell = false;
if (!IsPet() && !GetPetID() && !IsBotCharmer()) {
if (botClass == WIZARD) {
if (botClass == Class::Wizard) {
auto buffs_max = GetMaxBuffSlots();
auto my_buffs = GetBuffs();
int familiar_buff_slot = -1;
@@ -694,7 +694,7 @@ bool Bot::BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell) {
botSpell = GetFirstBotSpellBySpellType(this, SpellType_Pet);
}
else if (botClass == MAGICIAN) {
else if (botClass == Class::Magician) {
botSpell = GetBestBotMagicianPetSpell(this);
}
else {
@@ -735,13 +735,13 @@ bool Bot::BotCastDispel(Mob* tar, BotSpell& botSpell, uint32 iSpellTypes, const
bool Bot::BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los) {
bool casted_spell = false;
if ((tar->GetHPRatio() <= 95.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER) || (botClass == PALADIN) || (botClass == SHADOWKNIGHT) || (botClass == WARRIOR)))
if ((tar->GetHPRatio() <= 95.0f) || ((botClass == Class::Bard) || (botClass == Class::Shaman) || (botClass == Class::Enchanter) || (botClass == Class::Paladin) || (botClass == Class::ShadowKnight) || (botClass == Class::Warrior)))
{
if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) {
return casted_spell;
}
if (botClass == CLERIC || botClass == ENCHANTER)
if (botClass == Class::Cleric || botClass == Class::Enchanter)
{
float manaRatioToCast = 75.0f;
@@ -770,17 +770,17 @@ bool Bot::BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
return casted_spell;
}
if (botClass == MAGICIAN || botClass == SHADOWKNIGHT || botClass == NECROMANCER || botClass == PALADIN || botClass == RANGER || botClass == DRUID || botClass == CLERIC) {
if (botClass == Class::Magician || botClass == Class::ShadowKnight || botClass == Class::Necromancer || botClass == Class::Paladin || botClass == Class::Ranger || botClass == Class::Druid || botClass == Class::Cleric) {
if (tar->GetBodyType() == BT_Undead || tar->GetBodyType() == BT_SummonedUndead || tar->GetBodyType() == BT_Vampire)
botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Undead);
else if (tar->GetBodyType() == BT_Summoned || tar->GetBodyType() == BT_Summoned2 || tar->GetBodyType() == BT_Summoned3)
botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Summoned);
}
if ((botClass == PALADIN || botClass == DRUID || botClass == CLERIC || botClass == ENCHANTER || botClass == WIZARD) && !IsValidSpell(botSpell.SpellId)) {
if ((botClass == Class::Paladin || botClass == Class::Druid || botClass == Class::Cleric || botClass == Class::Enchanter || botClass == Class::Wizard) && !IsValidSpell(botSpell.SpellId)) {
uint8 stunChance = (tar->IsCasting() ? 30: 15);
if (botClass == PALADIN) {
if (botClass == Class::Paladin) {
stunChance = 50;
}
@@ -789,7 +789,7 @@ bool Bot::BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
}
}
if (botClass == WIZARD && botSpell.SpellId == 0) {
if (botClass == Class::Wizard && botSpell.SpellId == 0) {
botSpell = GetBestBotWizardNukeSpellByTargetResists(this, tar);
}
@@ -819,7 +819,7 @@ bool Bot::BotCastEscape(Mob*& tar, uint8 botClass, BotSpell& botSpell, uint32 iS
auto hpr = (uint8) GetHPRatio();
bool mayGetAggro = false;
if (hpr > 15 && ((botClass == WIZARD) || (botClass == ENCHANTER) || (botClass == RANGER))) {
if (hpr > 15 && ((botClass == Class::Wizard) || (botClass == Class::Enchanter) || (botClass == Class::Ranger))) {
mayGetAggro = HasOrMayGetAggro();
}
@@ -835,7 +835,7 @@ bool Bot::BotCastEscape(Mob*& tar, uint8 botClass, BotSpell& botSpell, uint32 iS
tar = this; //target self for invul type spells
}
if (IsValidSpellRange(botSpell.SpellId, tar) || botClass == BARD) {
if (IsValidSpellRange(botSpell.SpellId, tar) || botClass == Class::Bard) {
casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
}
}
@@ -877,7 +877,7 @@ bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
(tar == this && spells[s.SpellId].target_type != ST_TargetsTarget) ||
spells[s.SpellId].target_type == ST_Group ||
spells[s.SpellId].target_type == ST_GroupTeleport ||
(botClass == BARD && spells[s.SpellId].target_type == ST_AEBard)
(botClass == Class::Bard && spells[s.SpellId].target_type == ST_AEBard)
) &&
!tar->IsImmuneToSpell(s.SpellId, this) &&
tar->CanBuffStack(s.SpellId, botLevel, true) >= 0
@@ -892,7 +892,7 @@ bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
(IsEffectInSpell(s.SpellId, SE_Levitate) && !zone->CanLevitate()) ||
(IsEffectInSpell(s.SpellId, SE_MovementSpeed) && !zone->CanCastOutdoor())
) &&
(botClass != BARD || !IsSpellUsableInThisZoneType(s.SpellId, zone->GetZoneType()))
(botClass != Class::Bard || !IsSpellUsableInThisZoneType(s.SpellId, zone->GetZoneType()))
) {
continue;
}
@@ -919,7 +919,7 @@ bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
break;
}
if (botClass == ENCHANTER && IsEffectInSpell(s.SpellId, SE_Rune))
if (botClass == Class::Enchanter && IsEffectInSpell(s.SpellId, SE_Rune))
{
float manaRatioToCast = 75.0f;
@@ -947,7 +947,7 @@ bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
}
}
if (CheckSpellRecastTimers(this, s.SpellIndex))
if (CheckSpellRecastTimer(s.SpellId))
{
uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore();
@@ -1003,17 +1003,17 @@ bool Bot::BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
isPrimaryHealer = IsGroupHealer();
}
if (hpr < 95 || tar->IsClient() || botClass == BARD) {
if (tar->GetClass() == NECROMANCER && hpr >= 40) {
if (hpr < 95 || tar->IsClient() || botClass == Class::Bard) {
if (tar->GetClass() == Class::Necromancer && hpr >= 40) {
return false;
}
if (tar->GetClass() == SHAMAN && hpr >= 80) {
if (tar->GetClass() == Class::Shaman && hpr >= 80) {
return false;
}
// Evaluate the situation
if ((IsEngaged()) && ((botClass == CLERIC) || (botClass == DRUID) || (botClass == SHAMAN) || (botClass == PALADIN))) {
if ((IsEngaged()) && ((botClass == Class::Cleric) || (botClass == Class::Druid) || (botClass == Class::Shaman) || (botClass == Class::Paladin))) {
if (tar->GetTarget() && tar->GetTarget()->GetHateTop() && tar->GetTarget()->GetHateTop() == tar) {
hasAggro = true;
}
@@ -1045,7 +1045,7 @@ bool Bot::BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
}
}
}
else if ((botClass == CLERIC) || (botClass == DRUID) || (botClass == SHAMAN) || (botClass == PALADIN)) {
else if ((botClass == Class::Cleric) || (botClass == Class::Druid) || (botClass == Class::Shaman) || (botClass == Class::Paladin)) {
if (GetNumberNeedingHealedInGroup(40, true, raid) >= 2) {
botSpell = GetBestBotSpellForGroupCompleteHeal(this);
@@ -1119,7 +1119,7 @@ bool Bot::BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
if (!IsValidSpell(botSpell.SpellId)) {
botSpell = GetFirstBotSpellForSingleTargetHeal(this);
}
if (botSpell.SpellId == 0 && botClass == BARD) {
if (botSpell.SpellId == 0 && botClass == Class::Bard) {
botSpell = GetFirstBotSpellBySpellType(this, SpellType_Heal);
}
@@ -1134,11 +1134,11 @@ bool Bot::BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore();
if (IsValidSpellRange(botSpell.SpellId, tar) || botClass == BARD) {
if (IsValidSpellRange(botSpell.SpellId, tar) || botClass == Class::Bard) {
casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime);
}
if (casted_spell && botClass != BARD) {
if (casted_spell && botClass != Class::Bard) {
if (IsGroupSpell(botSpell.SpellId)) {
if (HasGroup()) {
Group *group = GetGroup();
@@ -1306,10 +1306,8 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
SetMana(hasMana);
}
else {
AIBot_spells[i].time_cancast = Timer::GetCurrentTime() + spells[AIBot_spells[i].spellid].recast_time;
if (spells[AIBot_spells[i].spellid].timer_id > 0) {
SetSpellRecastTimer(spells[AIBot_spells[i].spellid].timer_id, spells[AIBot_spells[i].spellid].recast_time);
if (CalcSpellRecastTimer(AIBot_spells[i].spellid) > 0) {
SetSpellRecastTimer(AIBot_spells[i].spellid);
}
}
@@ -1362,9 +1360,9 @@ bool Bot::AI_IdleCastCheck() {
//Ok, IdleCastCheck depends of class.
switch (GetClass()) {
// Healers WITHOUT pets will check if a heal is needed before buffing.
case CLERIC:
case PALADIN:
case RANGER: {
case Class::Cleric:
case Class::Paladin:
case Class::Ranger: {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
if (!AICastSpell(this, 100, SpellType_Heal)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
@@ -1379,10 +1377,10 @@ bool Bot::AI_IdleCastCheck() {
result = true;
break;
}
case MONK:
case ROGUE:
case WARRIOR:
case BERSERKER: {
case Class::Monk:
case Class::Rogue:
case Class::Warrior:
case Class::Berserker: {
if (!AICastSpell(this, 100, SpellType_Cure)) {
if (!AICastSpell(this, 100, SpellType_Heal)) {
if (!AICastSpell(this, 100, SpellType_Buff)) {
@@ -1397,10 +1395,10 @@ bool Bot::AI_IdleCastCheck() {
}
// Pets class will first cast their pet, then buffs
case MAGICIAN:
case SHADOWKNIGHT:
case NECROMANCER:
case ENCHANTER: {
case Class::Magician:
case Class::ShadowKnight:
case Class::Necromancer:
case Class::Enchanter: {
if (!AICastSpell(this, 100, SpellType_Pet)) {
if (!AICastSpell(this, 100, SpellType_Cure)) {
if (!AICastSpell(GetPet(), 100, SpellType_Cure)) {
@@ -1417,9 +1415,9 @@ bool Bot::AI_IdleCastCheck() {
result = true;
break;
}
case DRUID:
case SHAMAN:
case BEASTLORD: {
case Class::Druid:
case Class::Shaman:
case Class::Beastlord: {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
if (!AICastSpell(this, 100, SpellType_Pet)) {
if (!AICastSpell(this, 100, SpellType_Heal)) {
@@ -1438,7 +1436,7 @@ bool Bot::AI_IdleCastCheck() {
result = true;
break;
}
case WIZARD: { // This can eventually be move into the BEASTLORD case handler once pre-combat is fully implemented
case Class::Wizard: { // This can eventually be move into the Class::Beastlord case handler once pre-combat is fully implemented
if (pre_combat) {
if (!AICastSpell(this, 100, SpellType_Pet)) {
if (!AICastSpell(this, 100, SpellType_Cure)) {
@@ -1469,7 +1467,7 @@ bool Bot::AI_IdleCastCheck() {
result = true;
break;
}
case BARD: {
case Class::Bard: {
if (pre_combat) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
if (!AICastSpell(this, 100, SpellType_Buff)) {
@@ -1518,7 +1516,7 @@ bool Bot::AI_EngagedCastCheck() {
LogAIDetail("Engaged autocast check triggered (BOTS). Trying to cast healing spells then maybe offensive spells");
if (botClass == CLERIC) {
if (botClass == Class::Cleric) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) {
@@ -1534,7 +1532,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == DRUID) {
else if (botClass == Class::Druid) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) {
@@ -1552,7 +1550,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == SHAMAN) {
else if (botClass == Class::Shaman) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) {
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
@@ -1573,7 +1571,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == RANGER) {
else if (botClass == Class::Ranger) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) {
@@ -1588,7 +1586,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == BEASTLORD) {
else if (botClass == Class::Beastlord) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) {
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
@@ -1609,7 +1607,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == WIZARD) {
else if (botClass == Class::Wizard) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) {
@@ -1620,7 +1618,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == PALADIN) {
else if (botClass == Class::Paladin) {
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
@@ -1633,7 +1631,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == SHADOWKNIGHT) {
else if (botClass == Class::ShadowKnight) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Lifetap), SpellType_Lifetap)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
@@ -1646,7 +1644,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == MAGICIAN) {
else if (botClass == Class::Magician) {
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) {
@@ -1659,7 +1657,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == NECROMANCER) {
else if (botClass == Class::Necromancer) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
@@ -1678,7 +1676,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == ENCHANTER) {
else if (botClass == Class::Enchanter) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Mez), SpellType_Mez)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) {
@@ -1695,7 +1693,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == BARD) {
else if (botClass == Class::Bard) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {// Bards will use their escape songs
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_HateRedux), BotAISpellRange, SpellType_HateRedux)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) {
@@ -1714,7 +1712,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == BERSERKER) {
else if (botClass == Class::Berserker) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
@@ -1731,7 +1729,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == MONK) {
else if (botClass == Class::Monk) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
@@ -1748,7 +1746,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == ROGUE) {
else if (botClass == Class::Rogue) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
@@ -1765,7 +1763,7 @@ bool Bot::AI_EngagedCastCheck() {
}
}
}
else if (botClass == WARRIOR) {
else if (botClass == Class::Warrior) {
if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) {
if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) {
@@ -2044,7 +2042,7 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType) {
continue;
}
if ((botSpellList[i].type & spellType) && CheckSpellRecastTimers(botCaster, i)) {
if ((botSpellList[i].type & spellType) && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) {
result.SpellId = botSpellList[i].spellid;
result.SpellIndex = i;
result.ManaCost = botSpellList[i].manacost;
@@ -2069,7 +2067,7 @@ BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster) {
for (auto botSpellListItr : botSpellList) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (IsFastHealSpell(botSpellListItr.SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr.SpellIndex)) {
if (IsFastHealSpell(botSpellListItr.SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr.SpellId)) {
result.SpellId = botSpellListItr.SpellId;
result.SpellIndex = botSpellListItr.SpellIndex;
result.ManaCost = botSpellListItr.ManaCost;
@@ -2106,7 +2104,7 @@ BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster) {
if (
botSpellList[i].spellid == botSpellListItr.SpellId &&
(botSpellList[i].type & SpellType_Heal) &&
CheckSpellRecastTimers(botCaster, botSpellListItr.SpellIndex)
botCaster->CheckSpellRecastTimer(botSpellListItr.SpellId)
) {
result.SpellId = botSpellListItr.SpellId;
result.SpellIndex = botSpellListItr.SpellIndex;
@@ -2139,7 +2137,7 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) {
continue;
}
if (IsCompleteHealSpell(botSpellList[i].spellid) && CheckSpellRecastTimers(botCaster, i)) {
if (IsCompleteHealSpell(botSpellList[i].spellid) && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) {
result.SpellId = botSpellList[i].spellid;
result.SpellIndex = i;
result.ManaCost = botSpellList[i].manacost;
@@ -2163,7 +2161,7 @@ BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster) {
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
result.ManaCost = botSpellListItr->ManaCost;
@@ -2192,7 +2190,7 @@ BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster) {
IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) ||
IsFastHealSpell(botSpellListItr->SpellId)
) &&
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
@@ -2219,7 +2217,7 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (
IsRegularGroupHealSpell(botSpellListItr->SpellId) &&
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
@@ -2257,7 +2255,7 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster) {
if (
botSpellList[i].spellid == botSpellListItr->SpellId &&
(botSpellList[i].type & SpellType_Heal) &&
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
@@ -2287,7 +2285,7 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (
IsGroupCompleteHealSpell(botSpellListItr->SpellId) &&
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
@@ -2314,7 +2312,7 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (
IsMesmerizeSpell(botSpellListItr->SpellId) &&
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
@@ -2343,7 +2341,7 @@ BotSpell Bot::GetBestBotSpellForMagicBasedSlow(Bot* botCaster) {
if (
IsSlowSpell(botSpellListItr->SpellId) &&
spells[botSpellListItr->SpellId].resist_type == RESIST_MAGIC &&
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
@@ -2371,7 +2369,7 @@ BotSpell Bot::GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster) {
if (
IsSlowSpell(botSpellListItr->SpellId) &&
spells[botSpellListItr->SpellId].resist_type == RESIST_DISEASE &&
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
@@ -2437,7 +2435,7 @@ BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster) {
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
// Assuming all the spells have been loaded into this list by level and in descending order
if (IsSummonPetSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
if (IsSummonPetSpell(botSpellListItr->SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) {
if (!strncmp(spells[botSpellListItr->SpellId].teleport_zone, petType.c_str(), petType.length())) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
@@ -2546,7 +2544,7 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
// Assuming all the spells have been loaded into this list by level and in descending order
if ((IsPureNukeSpell(botSpellListItr->SpellId) || IsDamageSpell(botSpellListItr->SpellId)) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
if ((IsPureNukeSpell(botSpellListItr->SpellId) || IsDamageSpell(botSpellListItr->SpellId)) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) {
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
result.ManaCost = botSpellListItr->ManaCost;
@@ -2574,7 +2572,7 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr)
{
// Assuming all the spells have been loaded into this list by level and in descending order
if (IsStunSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex))
if (IsStunSpell(botSpellListItr->SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId))
{
result.SpellId = botSpellListItr->SpellId;
result.SpellIndex = botSpellListItr->SpellIndex;
@@ -2614,7 +2612,7 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* targ
// Assuming all the spells have been loaded into this list by level and in descending order
bool spellSelected = false;
if (CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
if (botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) {
if (selectLureNuke && (spells[botSpellListItr->SpellId].resist_difficulty < lureResisValue)) {
spellSelected = true;
}
@@ -2682,7 +2680,7 @@ BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar) {
if (((botSpellList[i].type & SpellType_Debuff) || IsDebuffSpell(botSpellList[i].spellid))
&& (!tar->IsImmuneToSpell(botSpellList[i].spellid, botCaster)
&& tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0)
&& CheckSpellRecastTimers(botCaster, i)) {
&& botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) {
result.SpellId = botSpellList[i].spellid;
result.SpellIndex = i;
result.ManaCost = botSpellList[i].manacost;
@@ -2735,7 +2733,7 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) {
|| (needsDiseaseResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistDisease)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)))
&& (!tar->IsImmuneToSpell(botSpellList[i].spellid, botCaster)
&& tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0)
&& CheckSpellRecastTimers(botCaster, i)) {
&& botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) {
result.SpellId = botSpellList[i].spellid;
result.SpellIndex = i;
result.ManaCost = botSpellList[i].manacost;
@@ -2786,31 +2784,31 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) {
for (std::list<BotSpell_wPriority>::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) {
BotSpell selectedBotSpell = *itr;
if (IsGroupSpell(itr->SpellId) && CheckSpellRecastTimers(botCaster, itr->SpellIndex)) {
if (IsGroupSpell(itr->SpellId) && botCaster->CheckSpellRecastTimer(selectedBotSpell.SpellId)) {
if (selectedBotSpell.SpellId == 0)
continue;
if (isPoisoned && IsEffectInSpell(itr->SpellId, SE_PoisonCounter)) {
if (isPoisoned && IsEffectInSpell(selectedBotSpell.SpellId, SE_PoisonCounter)) {
spellSelected = true;
}
else if (isDiseased && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter)) {
else if (isDiseased && IsEffectInSpell(selectedBotSpell.SpellId, SE_DiseaseCounter)) {
spellSelected = true;
}
else if (isCursed && IsEffectInSpell(itr->SpellId, SE_CurseCounter)) {
else if (isCursed && IsEffectInSpell(selectedBotSpell.SpellId, SE_CurseCounter)) {
spellSelected = true;
}
else if (isCorrupted && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter)) {
else if (isCorrupted && IsEffectInSpell(selectedBotSpell.SpellId, SE_CorruptionCounter)) {
spellSelected = true;
}
else if (IsEffectInSpell(itr->SpellId, SE_DispelDetrimental)) {
else if (IsEffectInSpell(selectedBotSpell.SpellId, SE_DispelDetrimental)) {
spellSelected = true;
}
if (spellSelected)
{
result.SpellId = itr->SpellId;
result.SpellIndex = itr->SpellIndex;
result.ManaCost = itr->ManaCost;
result.SpellId = selectedBotSpell.SpellId;
result.SpellIndex = selectedBotSpell.SpellIndex;
result.ManaCost = selectedBotSpell.ManaCost;
break;
}
@@ -2823,31 +2821,31 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) {
for(std::list<BotSpell_wPriority>::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) {
BotSpell selectedBotSpell = *itr;
if (CheckSpellRecastTimers(botCaster, itr->SpellIndex)) {
if (botCaster->CheckSpellRecastTimer(selectedBotSpell.SpellId)) {
if (selectedBotSpell.SpellId == 0)
continue;
if (isPoisoned && IsEffectInSpell(itr->SpellId, SE_PoisonCounter)) {
if (isPoisoned && IsEffectInSpell(selectedBotSpell.SpellId, SE_PoisonCounter)) {
spellSelected = true;
}
else if (isDiseased && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter)) {
else if (isDiseased && IsEffectInSpell(selectedBotSpell.SpellId, SE_DiseaseCounter)) {
spellSelected = true;
}
else if (isCursed && IsEffectInSpell(itr->SpellId, SE_CurseCounter)) {
else if (isCursed && IsEffectInSpell(selectedBotSpell.SpellId, SE_CurseCounter)) {
spellSelected = true;
}
else if (isCorrupted && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter)) {
else if (isCorrupted && IsEffectInSpell(selectedBotSpell.SpellId, SE_CorruptionCounter)) {
spellSelected = true;
}
else if (IsEffectInSpell(itr->SpellId, SE_DispelDetrimental)) {
else if (IsEffectInSpell(selectedBotSpell.SpellId, SE_DispelDetrimental)) {
spellSelected = true;
}
if (spellSelected)
{
result.SpellId = itr->SpellId;
result.SpellIndex = itr->SpellIndex;
result.ManaCost = itr->ManaCost;
result.SpellId = selectedBotSpell.SpellId;
result.SpellIndex = selectedBotSpell.SpellIndex;
result.ManaCost = selectedBotSpell.ManaCost;
break;
}
@@ -2859,69 +2857,6 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) {
return result;
}
void Bot::SetSpellRecastTimer(int timer_index, int32 recast_delay) {
if (timer_index > 0 && timer_index <= MaxSpellTimer) {
timers[timer_index - 1] = Timer::GetCurrentTime() + recast_delay;
}
}
int32 Bot::GetSpellRecastTimer(Bot *caster, int timer_index) {
int32 result = 0;
if (caster) {
if (timer_index > 0 && timer_index <= MaxSpellTimer) {
result = caster->timers[timer_index - 1];
}
}
return result;
}
bool Bot::CheckSpellRecastTimers(Bot *caster, int SpellIndex) {
if (caster) {
if (caster->AIBot_spells[SpellIndex].time_cancast < Timer::GetCurrentTime()) { //checks spell recast
if (GetSpellRecastTimer(caster, spells[caster->AIBot_spells[SpellIndex].spellid].timer_id) < Timer::GetCurrentTime()) { //checks for spells on the same timer
return true; //can cast spell
}
}
}
return false;
}
void Bot::SetDisciplineRecastTimer(int timer_index, int32 recast_delay) {
if (timer_index > 0 && timer_index <= MaxDisciplineTimer) {
timers[DisciplineReuseStart + timer_index - 1] = Timer::GetCurrentTime() + recast_delay;
}
}
int32 Bot::GetDisciplineRecastTimer(Bot *caster, int timer_index) {
int32 result = 0;
if (caster) {
if (timer_index > 0 && timer_index <= MaxDisciplineTimer) {
result = caster->timers[DisciplineReuseStart + timer_index - 1];
}
}
return result;
}
uint32 Bot::GetDisciplineRemainingTime(Bot *caster, int timer_index) {
int32 result = 0;
if (caster) {
if (timer_index > 0 && timer_index <= MaxDisciplineTimer) {
if (GetDisciplineRecastTimer(caster, timer_index) > Timer::GetCurrentTime())
result = GetDisciplineRecastTimer(caster, timer_index) - Timer::GetCurrentTime();
}
}
return result;
}
bool Bot::CheckDisciplineRecastTimers(Bot *caster, int timer_index) {
if (caster) {
if (GetDisciplineRecastTimer(caster, timer_index) < Timer::GetCurrentTime()) { //checks for spells on the same timer
return true; //can cast spell
}
}
return false;
}
uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
{
uint8 spell_type_index = SPELL_TYPE_COUNT;
@@ -3000,7 +2935,7 @@ uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
return 0;
uint8 class_index = GetClass();
if (class_index > BERSERKER || class_index < WARRIOR)
if (class_index > Class::Berserker || class_index < Class::Warrior)
return 0;
--class_index;
+280 -123
View File
@@ -65,6 +65,7 @@ extern volatile bool RunLoops;
#include "../common/repositories/character_disciplines_repository.h"
#include "../common/repositories/character_data_repository.h"
#include "../common/repositories/discovered_items_repository.h"
#include "../common/repositories/keyring_repository.h"
#include "../common/events/player_events.h"
#include "../common/events/player_event_logs.h"
#include "dialogue_window.h"
@@ -1410,126 +1411,163 @@ void Client::SetMaxHP() {
Save();
}
bool Client::UpdateLDoNPoints(uint32 theme_id, int points) {
/* make sure total stays in sync with individual buckets
m_pp.ldon_points_available = m_pp.ldon_points_guk
+m_pp.ldon_points_mir
+m_pp.ldon_points_mmc
+m_pp.ldon_points_ruj
+m_pp.ldon_points_tak; */
if(points < 0) {
if(m_pp.ldon_points_available < (0 - points))
bool Client::UpdateLDoNPoints(uint32 theme_id, int points)
{
if (points < 0) {
if (m_pp.ldon_points_available < (0 - points)) {
return false;
}
}
bool is_loss = false;
switch (theme_id) {
case LDoNThemes::Unused: { // No theme, so distribute evenly across all
int split_points = (points / 5);
int guk_points = (split_points + (points % 5));
int mir_points = split_points;
int mmc_points = split_points;
int ruj_points = split_points;
int tak_points = split_points;
split_points = 0;
if(points < 0) {
if(m_pp.ldon_points_available < (0 - points)) {
if (points < 0) {
if (m_pp.ldon_points_available < (0 - points)) {
return false;
}
if(m_pp.ldon_points_guk < (0 - guk_points)) {
is_loss = true;
if (m_pp.ldon_points_guk < (0 - guk_points)) {
mir_points += (guk_points + m_pp.ldon_points_guk);
guk_points = (0 - m_pp.ldon_points_guk);
}
if(m_pp.ldon_points_mir < (0 - mir_points)) {
if (m_pp.ldon_points_mir < (0 - mir_points)) {
mmc_points += (mir_points + m_pp.ldon_points_mir);
mir_points = (0 - m_pp.ldon_points_mir);
}
if(m_pp.ldon_points_mmc < (0 - mmc_points)) {
if (m_pp.ldon_points_mmc < (0 - mmc_points)) {
ruj_points += (mmc_points + m_pp.ldon_points_mmc);
mmc_points = (0 - m_pp.ldon_points_mmc);
}
if(m_pp.ldon_points_ruj < (0 - ruj_points)) {
if (m_pp.ldon_points_ruj < (0 - ruj_points)) {
tak_points += (ruj_points + m_pp.ldon_points_ruj);
ruj_points = (0 - m_pp.ldon_points_ruj);
}
if(m_pp.ldon_points_tak < (0 - tak_points)) {
if (m_pp.ldon_points_tak < (0 - tak_points)) {
split_points = (tak_points + m_pp.ldon_points_tak);
tak_points = (0 - m_pp.ldon_points_tak);
tak_points = (0 - m_pp.ldon_points_tak);
}
}
m_pp.ldon_points_guk += guk_points;
m_pp.ldon_points_mir += mir_points;
m_pp.ldon_points_mmc += mmc_points;
m_pp.ldon_points_ruj += ruj_points;
m_pp.ldon_points_tak += tak_points;
points -= split_points;
if (split_points != 0) { // if anything left, recursively loop thru again
UpdateLDoNPoints(0, split_points);
UpdateLDoNPoints(LDoNThemes::Unused, split_points);
}
break;
}
case LDoNThemes::GUK: {
if(points < 0) {
if(m_pp.ldon_points_guk < (0 - points)) {
case LDoNThemes::GUK: {
if (points < 0) {
if (m_pp.ldon_points_guk < (0 - points)) {
return false;
}
is_loss = true;
}
m_pp.ldon_points_guk += points;
break;
}
case LDoNThemes::MIR: {
if(points < 0) {
if(m_pp.ldon_points_mir < (0 - points)) {
if (points < 0) {
if (m_pp.ldon_points_mir < (0 - points)) {
return false;
}
is_loss = true;
}
m_pp.ldon_points_mir += points;
break;
}
case LDoNThemes::MMC: {
if(points < 0) {
if(m_pp.ldon_points_mmc < (0 - points)) {
if (points < 0) {
if (m_pp.ldon_points_mmc < (0 - points)) {
return false;
}
is_loss = true;
}
m_pp.ldon_points_mmc += points;
break;
}
case LDoNThemes::RUJ: {
if(points < 0) {
if(m_pp.ldon_points_ruj < (0 - points)) {
if (points < 0) {
if (m_pp.ldon_points_ruj < (0 - points)) {
return false;
}
is_loss = true;
}
m_pp.ldon_points_ruj += points;
break;
}
case LDoNThemes::TAK: {
if(points < 0) {
if(m_pp.ldon_points_tak < (0 - points)) {
if (points < 0) {
if (m_pp.ldon_points_tak < (0 - points)) {
return false;
}
is_loss = true;
}
m_pp.ldon_points_tak += points;
break;
}
}
m_pp.ldon_points_available += points;
QuestEventID event_id = is_loss ? EVENT_LDON_POINTS_LOSS : EVENT_LDON_POINTS_GAIN;
if (parse->PlayerHasQuestSub(event_id)) {
const std::string &export_string = fmt::format(
"{} {}",
theme_id,
std::abs(points)
);
parse->EventPlayer(event_id, this, export_string, 0);
}
auto outapp = new EQApplicationPacket(OP_AdventurePointsUpdate, sizeof(AdventurePoints_Update_Struct));
AdventurePoints_Update_Struct* apus = (AdventurePoints_Update_Struct*)outapp->pBuffer;
auto *apus = (AdventurePoints_Update_Struct *) outapp->pBuffer;
apus->ldon_available_points = m_pp.ldon_points_available;
apus->ldon_guk_points = m_pp.ldon_points_guk;
apus->ldon_mirugal_points = m_pp.ldon_points_mir;
apus->ldon_guk_points = m_pp.ldon_points_guk;
apus->ldon_mirugal_points = m_pp.ldon_points_mir;
apus->ldon_mistmoore_points = m_pp.ldon_points_mmc;
apus->ldon_rujarkian_points = m_pp.ldon_points_ruj;
apus->ldon_takish_points = m_pp.ldon_points_tak;
apus->ldon_takish_points = m_pp.ldon_points_tak;
outapp->priority = 6;
QueuePacket(outapp);
safe_delete(outapp);
return true;
@@ -2675,7 +2713,7 @@ bool Client::HasSkill(EQ::skills::SkillType skill_id) const {
}
bool Client::CanHaveSkill(EQ::skills::SkillType skill_id) const {
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == BERSERKER && skill_id == EQ::skills::Skill1HPiercing)
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == Class::Berserker && skill_id == EQ::skills::Skill1HPiercing)
skill_id = EQ::skills::Skill2HPiercing;
return(content_db.GetSkillCap(GetClass(), skill_id, RuleI(Character, MaxLevel)) > 0);
@@ -2683,7 +2721,7 @@ bool Client::CanHaveSkill(EQ::skills::SkillType skill_id) const {
}
uint16 Client::MaxSkill(EQ::skills::SkillType skillid, uint16 class_, uint16 level) const {
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == BERSERKER && skillid == EQ::skills::Skill1HPiercing)
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == Class::Berserker && skillid == EQ::skills::Skill1HPiercing)
skillid = EQ::skills::Skill2HPiercing;
return(content_db.GetSkillCap(class_, skillid, level));
@@ -2691,7 +2729,7 @@ uint16 Client::MaxSkill(EQ::skills::SkillType skillid, uint16 class_, uint16 lev
uint8 Client::SkillTrainLevel(EQ::skills::SkillType skillid, uint16 class_)
{
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == BERSERKER && skillid == EQ::skills::Skill1HPiercing)
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == Class::Berserker && skillid == EQ::skills::Skill1HPiercing)
skillid = EQ::skills::Skill2HPiercing;
return(content_db.GetTrainLevel(class_, skillid, RuleI(Character, MaxLevel)));
@@ -3027,7 +3065,7 @@ bool Client::BindWound(Mob *bindmob, bool start, bool fail)
int max_percent = 50 + maxHPBonus;
if (GetClass() == MONK && GetSkill(EQ::skills::SkillBindWound) > 200) {
if (GetClass() == Class::Monk && GetSkill(EQ::skills::SkillBindWound) > 200) {
max_percent = 70 + maxHPBonus;
}
@@ -3076,9 +3114,9 @@ bool Client::BindWound(Mob *bindmob, bool start, bool fail)
else {
int percent_base = 50;
if (GetRawSkill(EQ::skills::SkillBindWound) > 200) {
if ((GetClass() == MONK) || (GetClass() == BEASTLORD))
if ((GetClass() == Class::Monk) || (GetClass() == Class::Beastlord))
percent_base = 70;
else if ((GetLevel() > 50) && ((GetClass() == WARRIOR) || (GetClass() == ROGUE) || (GetClass() == CLERIC)))
else if ((GetLevel() > 50) && ((GetClass() == Class::Warrior) || (GetClass() == Class::Rogue) || (GetClass() == Class::Cleric)))
percent_base = 70;
}
@@ -4087,54 +4125,87 @@ void Client::SendWindow(
void Client::KeyRingLoad()
{
std::string query = StringFormat("SELECT item_id FROM keyring "
"WHERE char_id = '%i' ORDER BY item_id", character_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
const auto &l = KeyringRepository::GetWhere(
database,
fmt::format(
"`char_id` = {} ORDER BY `item_id`",
character_id
)
);
if (l.empty()) {
return;
}
for (auto row = results.begin(); row != results.end(); ++row)
keyring.push_back(Strings::ToInt(row[0]));
for (const auto &e : l) {
keyring.emplace_back(e.item_id);
}
}
void Client::KeyRingAdd(uint32 item_id)
{
if(0==item_id)
return;
bool found = KeyRingCheck(item_id);
if (found)
return;
std::string query = StringFormat("INSERT INTO keyring(char_id, item_id) VALUES(%i, %i)", character_id, item_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
if (!item_id) {
return;
}
Message(Chat::LightBlue,"Added to keyring.");
const bool found = KeyRingCheck(item_id);
if (found) {
return;
}
keyring.push_back(item_id);
auto e = KeyringRepository::NewEntity();
e.char_id = CharacterID();
e.item_id = item_id;
e = KeyringRepository::InsertOne(database, e);
if (!e.id) {
return;
}
keyring.emplace_back(item_id);
if (!RuleB(World, UseItemLinksForKeyRing)) {
Message(Chat::LightBlue, "Added to keyring.");
return;
}
const std::string &item_link = database.CreateItemLink(item_id);
Message(
Chat::LightBlue,
fmt::format(
"Added {} to keyring.",
item_link
).c_str()
);
}
bool Client::KeyRingCheck(uint32 item_id)
{
for (auto iter = keyring.begin(); iter != keyring.end(); ++iter) {
if(*iter == item_id)
for (const auto &e : keyring) {
if (e == item_id) {
return true;
}
}
return false;
}
void Client::KeyRingList()
{
Message(Chat::LightBlue,"Keys on Keyring:");
Message(Chat::LightBlue, "Keys on Keyring:");
const EQ::ItemData *item = nullptr;
for (auto iter = keyring.begin(); iter != keyring.end(); ++iter) {
if ((item = database.GetItem(*iter))!=nullptr) {
Message(Chat::LightBlue,item->Name);
for (const auto &e : keyring) {
item = database.GetItem(e);
if (item) {
const std::string &item_string = RuleB(World, UseItemLinksForKeyRing) ? database.CreateItemLink(e) : item->Name;
Message(Chat::LightBlue, item_string.c_str());
}
}
}
@@ -4198,7 +4269,7 @@ void Client::UpdateLFP() {
for(unsigned int i=0; i<MAX_GROUP_MEMBERS; i++) {
LFPMembers[i].Name[0] = '\0';
LFPMembers[i].Class = NO_CLASS;
LFPMembers[i].Class = Class::None;
LFPMembers[i].Level = 0;
LFPMembers[i].Zone = 0;
}
@@ -4815,7 +4886,7 @@ void Client::HandleLDoNOpen(NPC *target)
{
if(target)
{
if(target->GetClass() != LDON_TREASURE)
if(target->GetClass() != Class::LDoNTreasure)
{
LogDebug("[{}] tried to open [{}] but [{}] was not a treasure chest",
GetName(), target->GetName(), target->GetName());
@@ -4884,7 +4955,7 @@ void Client::HandleLDoNOpen(NPC *target)
void Client::HandleLDoNSenseTraps(NPC *target, uint16 skill, uint8 type)
{
if(target && target->GetClass() == LDON_TREASURE)
if(target && target->GetClass() == Class::LDoNTreasure)
{
if(target->IsLDoNTrapped())
{
@@ -4927,7 +4998,7 @@ void Client::HandleLDoNDisarm(NPC *target, uint16 skill, uint8 type)
{
if(target)
{
if(target->GetClass() == LDON_TREASURE)
if(target->GetClass() == Class::LDoNTreasure)
{
if(!target->IsLDoNTrapped())
{
@@ -4977,7 +5048,7 @@ void Client::HandleLDoNPickLock(NPC *target, uint16 skill, uint8 type)
{
if(target)
{
if(target->GetClass() == LDON_TREASURE)
if(target->GetClass() == Class::LDoNTreasure)
{
if(target->IsLDoNTrapped())
{
@@ -5656,26 +5727,89 @@ void Client::AddPVPPoints(uint32 Points)
SendPVPStats();
}
void Client::AddCrystals(uint32 radiant, uint32 ebon)
{
m_pp.currentRadCrystals += radiant;
m_pp.careerRadCrystals += radiant;
m_pp.currentEbonCrystals += ebon;
m_pp.careerEbonCrystals += ebon;
void Client::AddEbonCrystals(uint32 amount, bool is_reclaim) {
m_pp.currentEbonCrystals += amount;
m_pp.careerEbonCrystals += amount;
SaveCurrency();
SendCrystalCounts();
// newer clients handle message client side (older clients likely used eqstr 5967 and 5968, this matches live)
if (radiant > 0)
{
MessageString(Chat::Yellow, YOU_RECEIVE, fmt::format("{} Radiant Crystals", radiant).c_str());
}
MessageString(
Chat::Yellow,
YOU_RECEIVE,
fmt::format(
"{} {}",
amount,
database.CreateItemLink(RuleI(Zone, EbonCrystalItemID))
).c_str()
);
if (ebon > 0)
{
MessageString(Chat::Yellow, YOU_RECEIVE, fmt::format("{} Ebon Crystals", ebon).c_str());
if (parse->PlayerHasQuestSub(EVENT_CRYSTAL_GAIN)) {
const std::string &export_string = fmt::format(
"{} 0 {}",
amount,
is_reclaim ? 1 : 0
);
parse->EventPlayer(EVENT_CRYSTAL_GAIN, this, export_string, 0);
}
}
void Client::AddRadiantCrystals(uint32 amount, bool is_reclaim) {
m_pp.currentRadCrystals += amount;
m_pp.careerRadCrystals += amount;
SaveCurrency();
SendCrystalCounts();
MessageString(
Chat::Yellow,
YOU_RECEIVE,
fmt::format(
"{} {}",
amount,
database.CreateItemLink(RuleI(Zone, RadiantCrystalItemID))
).c_str()
);
if (parse->PlayerHasQuestSub(EVENT_CRYSTAL_GAIN)) {
const std::string &export_string = fmt::format(
"0 {} {}",
amount,
is_reclaim ? 1 : 0
);
parse->EventPlayer(EVENT_CRYSTAL_GAIN, this, export_string, 0);
}
}
void Client::RemoveEbonCrystals(uint32 amount, bool is_reclaim) {
m_pp.currentEbonCrystals -= amount;
SaveCurrency();
SendCrystalCounts();
if (parse->PlayerHasQuestSub(EVENT_CRYSTAL_LOSS)) {
const std::string &export_string = fmt::format(
"{} 0 {}",
amount,
is_reclaim ? 1 : 0
);
parse->EventPlayer(EVENT_CRYSTAL_LOSS, this, export_string, 0);
}
}
void Client::RemoveRadiantCrystals(uint32 amount, bool is_reclaim) {
m_pp.currentRadCrystals -= amount;
SaveCurrency();
SendCrystalCounts();
if (parse->PlayerHasQuestSub(EVENT_CRYSTAL_LOSS)) {
const std::string &export_string = fmt::format(
"0 {} {}",
amount,
is_reclaim ? 1 : 0
);
parse->EventPlayer(EVENT_CRYSTAL_LOSS, this, export_string, 0);
}
}
@@ -6420,11 +6554,10 @@ void Client::SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount)
SendAlternateCurrencyValue(currency_id);
}
int Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method)
int Client::AddAlternateCurrencyValue(uint32 currency_id, int amount, bool is_scripted)
{
/* Added via Quest, rest of the logging methods may be done inline due to information available in that area of the code */
if (method == 1){
if (is_scripted) {
/* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */
if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){
std::string event_desc = StringFormat("Added via Quest :: Cursor to Item :: alt_currency_id:%i amount:%i in zoneid:%i instid:%i", currency_id, GetZoneID(), GetInstanceID());
@@ -6432,32 +6565,47 @@ int Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 met
}
}
if(amount == 0) {
if (!amount) {
return 0;
}
if(!alternate_currency_loaded) {
if (!alternate_currency_loaded) {
alternate_currency_queued_operations.push(std::make_pair(currency_id, amount));
return 0;
}
int new_value = 0;
auto iter = alternate_currency.find(currency_id);
if(iter == alternate_currency.end()) {
if (iter == alternate_currency.end()) {
new_value = amount;
} else {
new_value = (*iter).second + amount;
}
if(new_value < 0) {
if (new_value < 0) {
new_value = 0;
alternate_currency[currency_id] = 0;
database.UpdateAltCurrencyValue(CharacterID(), currency_id, 0);
} else {
alternate_currency[currency_id] = new_value;
database.UpdateAltCurrencyValue(CharacterID(), currency_id, new_value);
}
SendAlternateCurrencyValue(currency_id);
QuestEventID event_id = amount > 0 ? EVENT_ALT_CURRENCY_GAIN : EVENT_ALT_CURRENCY_LOSS;
if (parse->PlayerHasQuestSub(event_id)) {
const std::string &export_string = fmt::format(
"{} {} {}",
currency_id,
std::abs(amount),
new_value
);
parse->EventPlayer(event_id, this, export_string, 0);
}
return new_value;
}
@@ -8569,20 +8717,20 @@ void Client::InitInnates()
}
switch (class_) {
case DRUID:
case Class::Druid:
m_pp.InnateSkills[InnateHarmony] = InnateEnabled;
break;
case BARD:
case Class::Bard:
m_pp.InnateSkills[InnateReveal] = InnateEnabled;
break;
case ROGUE:
case Class::Rogue:
m_pp.InnateSkills[InnateSurprise] = InnateEnabled;
m_pp.InnateSkills[InnateReveal] = InnateEnabled;
break;
case RANGER:
case Class::Ranger:
m_pp.InnateSkills[InnateAwareness] = InnateEnabled;
break;
case MONK:
case Class::Monk:
m_pp.InnateSkills[InnateSurprise] = InnateEnabled;
m_pp.InnateSkills[InnateAwareness] = InnateEnabled;
default:
@@ -9720,7 +9868,7 @@ std::vector<int> Client::GetLearnableDisciplines(uint8 min_level, uint8 max_leve
continue;
}
if (spells[spell_id].classes[WARRIOR] == 0) {
if (spells[spell_id].classes[Class::Warrior] == 0) {
continue;
}
@@ -9791,7 +9939,7 @@ std::vector<int> Client::GetScribeableSpells(uint8 min_level, uint8 max_level) {
continue;
}
if (spells[spell_id].classes[WARRIOR] == 0) {
if (spells[spell_id].classes[Class::Warrior] == 0) {
continue;
}
@@ -10355,6 +10503,16 @@ void Client::SetDoorToolEntityId(uint16 door_tool_entity_id)
Client::m_door_tool_entity_id = door_tool_entity_id;
}
uint16 Client::GetObjectToolEntityId() const
{
return m_object_tool_entity_id;
}
void Client::SetObjectToolEntityId(uint16 object_tool_entity_id)
{
Client::m_object_tool_entity_id = object_tool_entity_id;
}
int Client::GetIPExemption()
{
return database.GetIPExemption(GetIPString());
@@ -10650,37 +10808,37 @@ uint16 Client::LearnDisciplines(uint8 min_level, uint8 max_level)
uint16 Client::GetClassTrackingDistanceMultiplier(uint16 class_) {
switch (class_) {
case WARRIOR:
case Class::Warrior:
return RuleI(Character, WarriorTrackingDistanceMultiplier);
case CLERIC:
case Class::Cleric:
return RuleI(Character, ClericTrackingDistanceMultiplier);
case PALADIN:
case Class::Paladin:
return RuleI(Character, PaladinTrackingDistanceMultiplier);
case RANGER:
case Class::Ranger:
return RuleI(Character, RangerTrackingDistanceMultiplier);
case SHADOWKNIGHT:
case Class::ShadowKnight:
return RuleI(Character, ShadowKnightTrackingDistanceMultiplier);
case DRUID:
case Class::Druid:
return RuleI(Character, DruidTrackingDistanceMultiplier);
case MONK:
case Class::Monk:
return RuleI(Character, MonkTrackingDistanceMultiplier);
case BARD:
case Class::Bard:
return RuleI(Character, BardTrackingDistanceMultiplier);
case ROGUE:
case Class::Rogue:
return RuleI(Character, RogueTrackingDistanceMultiplier);
case SHAMAN:
case Class::Shaman:
return RuleI(Character, ShamanTrackingDistanceMultiplier);
case NECROMANCER:
case Class::Necromancer:
return RuleI(Character, NecromancerTrackingDistanceMultiplier);
case WIZARD:
case Class::Wizard:
return RuleI(Character, WizardTrackingDistanceMultiplier);
case MAGICIAN:
case Class::Magician:
return RuleI(Character, MagicianTrackingDistanceMultiplier);
case ENCHANTER:
case Class::Enchanter:
return RuleI(Character, EnchanterTrackingDistanceMultiplier);
case BEASTLORD:
case Class::Beastlord:
return RuleI(Character, BeastlordTrackingDistanceMultiplier);
case BERSERKER:
case Class::Berserker:
return RuleI(Character, BerserkerTrackingDistanceMultiplier);
default:
return 0;
@@ -11127,16 +11285,15 @@ void Client::AddAAPoints(uint32 points)
{
m_pp.aapoints += points;
if (points == 1 && m_pp.aapoints == 1)
{
if (parse->PlayerHasQuestSub(EVENT_AA_GAIN)) {
parse->EventPlayer(EVENT_AA_GAIN, this, std::to_string(points), 0);
}
if (points == 1 && m_pp.aapoints == 1) {
MessageString(Chat::Yellow, GAIN_SINGLE_AA_SINGLE_AA, fmt::format_int(m_pp.aapoints).c_str());
}
else if (points == 1 && m_pp.aapoints > 1)
{
} else if (points == 1 && m_pp.aapoints > 1) {
MessageString(Chat::Yellow, GAIN_SINGLE_AA_MULTI_AA, fmt::format_int(m_pp.aapoints).c_str());
}
else
{
} else {
MessageString(Chat::Yellow, GAIN_MULTI_AA_MULTI_AA, fmt::format_int(points).c_str(), fmt::format_int(m_pp.aapoints).c_str());
}
+20 -9
View File
@@ -611,11 +611,14 @@ public:
void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; }
uint32 GetPVPPoints() { return m_pp.PVPCurrentPoints; }
void AddPVPPoints(uint32 Points);
void AddEbonCrystals(uint32 amount, bool is_reclaim = false);
void AddRadiantCrystals(uint32 amount, bool is_reclaim = false);
void RemoveEbonCrystals(uint32 amount, bool is_reclaim = false);
void RemoveRadiantCrystals(uint32 amount, bool is_reclaim = false);
uint32 GetRadiantCrystals() { return m_pp.currentRadCrystals; }
void SetRadiantCrystals(uint32 value);
uint32 GetEbonCrystals() { return m_pp.currentEbonCrystals; }
void SetEbonCrystals(uint32 value);
void AddCrystals(uint32 Radiant, uint32 Ebon);
void SendCrystalCounts();
uint64 GetExperienceForKill(Mob *against);
@@ -908,6 +911,7 @@ public:
void AutoGrantAAPoints();
void GrantAllAAPoints(uint8 unlock_level = 0);
bool HasAlreadyPurchasedRank(AA::Rank* rank);
void ListPurchasedAAs(Client *to, std::string search_criteria = std::string());
bool SendGMCommand(std::string message, bool ignore_status = false);
@@ -1274,6 +1278,10 @@ public:
}
else { return 0; }
}
inline bool CompleteTask(uint32 task_id)
{
return task_state ? task_state->CompleteTask(this, task_id) : false;
}
inline void FailTask(int task_id) { if (task_state) { task_state->FailTask(this, task_id); }}
inline int TaskTimeLeft(int task_id) { return (task_state ? task_state->TaskTimeLeft(task_id) : 0); }
inline int EnabledTaskCount(int task_set_id)
@@ -1488,7 +1496,7 @@ public:
void ConsentCorpses(std::string consent_name, bool deny = false);
void SendAltCurrencies();
void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount);
int AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0);
int AddAlternateCurrencyValue(uint32 currency_id, int amount, bool is_scripted = false);
void SendAlternateCurrencyValues();
void SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null = true);
uint32 GetAlternateCurrencyValue(uint32 currency_id) const;
@@ -1809,9 +1817,12 @@ private:
bool dev_tools_enabled;
uint16 m_door_tool_entity_id;
uint16 m_object_tool_entity_id;
public:
uint16 GetDoorToolEntityId() const;
void SetDoorToolEntityId(uint16 door_tool_entity_id);
uint16 GetObjectToolEntityId() const;
void SetObjectToolEntityId(uint16 object_tool_entity_id);
private:
int32 max_end;
@@ -2037,14 +2048,14 @@ public:
bool GetBotPrecombat() { return m_bot_precombat; }
void SetBotPrecombat(bool flag = true) { m_bot_precombat = flag; }
int GetBotRequiredLevel(uint8 class_id = NO_CLASS);
uint32 GetBotCreationLimit(uint8 class_id = NO_CLASS);
int GetBotSpawnLimit(uint8 class_id = NO_CLASS);
void SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id = NO_CLASS);
void SetBotRequiredLevel(int new_required_level, uint8 class_id = NO_CLASS);
void SetBotSpawnLimit(int new_spawn_limit, uint8 class_id = NO_CLASS);
int GetBotRequiredLevel(uint8 class_id = Class::None);
uint32 GetBotCreationLimit(uint8 class_id = Class::None);
int GetBotSpawnLimit(uint8 class_id = Class::None);
void SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id = Class::None);
void SetBotRequiredLevel(int new_required_level, uint8 class_id = Class::None);
void SetBotSpawnLimit(int new_spawn_limit, uint8 class_id = Class::None);
void CampAllBots(uint8 class_id = NO_CLASS);
void CampAllBots(uint8 class_id = Class::None);
void SpawnRaidBotsOnConnect(Raid* raid);
private:
+29 -29
View File
@@ -345,7 +345,7 @@ uint32 Mob::GetClassLevelFactor()
uint32 multiplier = 0;
uint8 mlevel = GetLevel();
switch (GetClass()) {
case WARRIOR: {
case Class::Warrior: {
if (mlevel < 20) {
multiplier = 220;
}
@@ -372,9 +372,9 @@ uint32 Mob::GetClassLevelFactor()
}
break;
}
case DRUID:
case CLERIC:
case SHAMAN: {
case Class::Druid:
case Class::Cleric:
case Class::Shaman: {
if (mlevel < 70) {
multiplier = 150;
}
@@ -383,9 +383,9 @@ uint32 Mob::GetClassLevelFactor()
}
break;
}
case BERSERKER:
case PALADIN:
case SHADOWKNIGHT: {
case Class::Berserker:
case Class::Paladin:
case Class::ShadowKnight: {
if (mlevel < 35) {
multiplier = 210;
}
@@ -409,10 +409,10 @@ uint32 Mob::GetClassLevelFactor()
}
break;
}
case MONK:
case BARD:
case ROGUE:
case BEASTLORD: {
case Class::Monk:
case Class::Bard:
case Class::Rogue:
case Class::Beastlord: {
if (mlevel < 51) {
multiplier = 180;
}
@@ -427,7 +427,7 @@ uint32 Mob::GetClassLevelFactor()
}
break;
}
case RANGER: {
case Class::Ranger: {
if (mlevel < 58) {
multiplier = 200;
}
@@ -439,10 +439,10 @@ uint32 Mob::GetClassLevelFactor()
}
break;
}
case MAGICIAN:
case WIZARD:
case NECROMANCER:
case ENCHANTER: {
case Class::Magician:
case Class::Wizard:
case Class::Necromancer:
case Class::Enchanter: {
if (mlevel < 70) {
multiplier = 120;
}
@@ -677,7 +677,7 @@ int64 Client::CalcManaRegen(bool bCombat)
if (IsSitting() || CanMedOnHorse()) {
// kind of weird to do it here w/e
// client does some base medding regen for shrouds here
if (GetClass() != BARD) {
if (GetClass() != Class::Bard) {
auto skill = GetSkill(EQ::skills::SkillMeditate);
if (skill > 0) {
regen++;
@@ -1077,7 +1077,7 @@ int32 Client::CalcMR()
MR = 20;
}
MR += itembonuses.MR + spellbonuses.MR + aabonuses.MR;
if (GetClass() == WARRIOR || GetClass() == BERSERKER) {
if (GetClass() == Class::Warrior || GetClass() == Class::Berserker) {
MR += GetLevel() / 2;
}
if (MR < 1) {
@@ -1151,14 +1151,14 @@ int32 Client::CalcFR()
FR = 20;
}
int c = GetClass();
if (c == RANGER) {
if (c == Class::Ranger) {
FR += 4;
int l = GetLevel();
if (l > 49) {
FR += l - 49;
}
}
if (c == MONK) {
if (c == Class::Monk) {
FR += 8;
int l = GetLevel();
if (l > 49) {
@@ -1238,19 +1238,19 @@ int32 Client::CalcDR()
}
int c = GetClass();
// the monk one is part of base resist
if (c == MONK) {
if (c == Class::Monk) {
int l = GetLevel();
if (l > 50)
DR += l - 50;
}
if (c == PALADIN) {
if (c == Class::Paladin) {
DR += 8;
int l = GetLevel();
if (l > 49) {
DR += l - 49;
}
}
else if (c == SHADOWKNIGHT || c == BEASTLORD) {
else if (c == Class::ShadowKnight || c == Class::Beastlord) {
DR += 4;
int l = GetLevel();
if (l > 49) {
@@ -1330,19 +1330,19 @@ int32 Client::CalcPR()
}
int c = GetClass();
// this monk bonus is part of the base
if (c == MONK) {
if (c == Class::Monk) {
int l = GetLevel();
if (l > 50)
PR += l - 50;
}
if (c == ROGUE) {
if (c == Class::Rogue) {
PR += 8;
int l = GetLevel();
if (l > 49) {
PR += l - 49;
}
}
else if (c == SHADOWKNIGHT) {
else if (c == Class::ShadowKnight) {
PR += 4;
int l = GetLevel();
if (l > 49) {
@@ -1421,7 +1421,7 @@ int32 Client::CalcCR()
CR = 25;
}
int c = GetClass();
if (c == RANGER || c == BEASTLORD) {
if (c == Class::Ranger || c == Class::Beastlord) {
CR += 4;
int l = GetLevel();
if (l > 49) {
@@ -1455,7 +1455,7 @@ int32 Client::CalcATK()
uint32 Mob::GetInstrumentMod(uint16 spell_id)
{
if (GetClass() != BARD) {
if (GetClass() != Class::Bard) {
//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
@@ -1689,7 +1689,7 @@ int64 Client::CalcEnduranceRegen(bool bCombat)
int weight_limit = GetSTR();
auto level = GetLevel();
if (GetClass() == MONK) {
if (GetClass() == Class::Monk) {
if (level > 99)
weight_limit = 58;
else if (level > 94)
+273 -256
View File
File diff suppressed because it is too large Load Diff
+9 -9
View File
@@ -434,7 +434,7 @@ bool Client::Process() {
}
}
if (GetClass() == WARRIOR || GetClass() == BERSERKER) {
if (GetClass() == Class::Warrior || GetClass() == Class::Berserker) {
if (!dead && !IsBerserk() && GetHPRatio() < RuleI(Combat, BerserkerFrenzyStart)) {
entity_list.MessageCloseString(this, false, 200, 0, BERSERK_START, GetName());
berserk = true;
@@ -1121,7 +1121,7 @@ void Client::OPMemorizeSpell(const EQApplicationPacket* app)
if (
m->scribing != memSpellForget &&
(
!EQ::ValueWithin(GetClass(), PLAYER_CLASS_WARRIOR, PLAYER_CLASS_BERSERKER) ||
!IsPlayerClass(GetClass()) ||
GetLevel() < spells[m->spell_id].classes[GetClass() - 1]
)
) {
@@ -1552,12 +1552,12 @@ void Client::OPGMTraining(const EQApplicationPacket *app)
Mob* pTrainer = entity_list.GetMob(gmtrain->npcid);
if(!pTrainer || !pTrainer->IsNPC() || pTrainer->GetClass() < WARRIORGM || pTrainer->GetClass() > 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() - (WARRIORGM - WARRIOR);
int trains_class = pTrainer->GetClass() - (Class::WarriorGM - Class::Warrior);
if (GetClass() != trains_class)
return;
}
@@ -1579,7 +1579,7 @@ void Client::OPGMTraining(const EQApplicationPacket *app)
}
}
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && GetClass() == BERSERKER) {
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && GetClass() == Class::Berserker) {
gmtrain->skills[EQ::skills::Skill1HPiercing] = gmtrain->skills[EQ::skills::Skill2HPiercing];
gmtrain->skills[EQ::skills::Skill2HPiercing] = 0;
}
@@ -1607,12 +1607,12 @@ void Client::OPGMEndTraining(const EQApplicationPacket *app)
FastQueuePacket(&outapp);
Mob* pTrainer = entity_list.GetMob(p->npcid);
if(!pTrainer || !pTrainer->IsNPC() || pTrainer->GetClass() < WARRIORGM || pTrainer->GetClass() > 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() - (WARRIORGM - WARRIOR);
int trains_class = pTrainer->GetClass() - (Class::WarriorGM - Class::Warrior);
if (GetClass() != trains_class)
return;
}
@@ -1638,12 +1638,12 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app)
GMSkillChange_Struct* gmskill = (GMSkillChange_Struct*) app->pBuffer;
Mob* pTrainer = entity_list.GetMob(gmskill->npcid);
if(!pTrainer || !pTrainer->IsNPC() || pTrainer->GetClass() < WARRIORGM || pTrainer->GetClass() > 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() - (WARRIORGM - WARRIOR);
int trains_class = pTrainer->GetClass() - (Class::WarriorGM - Class::Warrior);
if (GetClass() != trains_class)
return;
}
+1
View File
@@ -866,6 +866,7 @@ void command_bot(Client *c, const Seperator *sep)
#include "gm_commands/nukebuffs.cpp"
#include "gm_commands/nukeitem.cpp"
#include "gm_commands/object.cpp"
#include "gm_commands/object_manipulation.cpp"
#include "gm_commands/path.cpp"
#include "gm_commands/peqzone.cpp"
#include "gm_commands/petitems.cpp"
+1 -1
View File
@@ -277,7 +277,7 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
// enforce flags before they hit zoning process
auto z = GetZone(m_destination_zone_name, 0);
if (z && !z->flag_needed.empty() && Strings::IsNumber(z->flag_needed) && Strings::ToInt(z->flag_needed) == 1) {
if (sender->Admin() < minStatusToIgnoreZoneFlags && !sender->HasZoneFlag(z->zoneidnumber)) {
if (!sender->GetGM() && !sender->HasZoneFlag(z->zoneidnumber)) {
LogInfo(
"Character [{}] does not have the flag to be in this zone [{}]!",
sender->GetCleanName(),
+29 -20
View File
@@ -75,7 +75,7 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
//Crtical Hit Calculation pathway
if (chance > 0 || (IsOfClientBot() && GetClass() == WIZARD && GetLevel() >= RuleI(Spells, WizCritLevel))) {
if (chance > 0 || (IsOfClientBot() && GetClass() == Class::Wizard && GetLevel() >= RuleI(Spells, WizCritLevel))) {
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
@@ -92,7 +92,7 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
ratio += itembonuses.SpellCritDmgIncNoStack + spellbonuses.SpellCritDmgIncNoStack + aabonuses.SpellCritDmgIncNoStack;
}
else if ((IsOfClientBot() && GetClass() == WIZARD) || (IsMerc() && GetClass() == CASTERDPS)) {
else if ((IsOfClientBot() && GetClass() == Class::Wizard) || (IsMerc() && GetClass() == CASTERDPS)) {
if ((GetLevel() >= RuleI(Spells, WizCritLevel)) && zone->random.Roll(RuleI(Spells, WizCritChance))){
//Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. (20-70 is parse confirmed)
ratio += zone->random.Int(RuleI(Spells, WizardCritMinimumRandomRatio), RuleI(Spells, WizardCritMaximumRandomRatio));
@@ -100,7 +100,7 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
}
}
if (IsOfClientBot() && GetClass() == WIZARD) {
if (IsOfClientBot() && GetClass() == Class::Wizard) {
ratio += RuleI(Spells, WizCritRatio); //Default is zero
}
@@ -285,10 +285,13 @@ int64 Mob::GetActDoTDamage(uint16 spell_id, int64 value, Mob* target, bool from_
extra_dmg += GetSkillDmgAmt(spells[spell_id].skill) * ratio / 100;
}
if (extra_dmg) {
int duration = CalcBuffDuration(this, target, spell_id);
if (duration > 0)
extra_dmg /= duration;
if (RuleB(Spells, DOTBonusDamageSplitOverDuration)) {
if (extra_dmg) {
const int duration = CalcBuffDuration(this, target, spell_id);
if (duration > 0) {
extra_dmg /= duration;
}
}
}
value -= extra_dmg;
@@ -328,10 +331,13 @@ int64 Mob::GetActDoTDamage(uint16 spell_id, int64 value, Mob* target, bool from_
extra_dmg += GetSkillDmgAmt(spells[spell_id].skill);
}
if (extra_dmg) {
int duration = CalcBuffDuration(this, target, spell_id);
if (duration > 0)
extra_dmg /= duration;
if (RuleB(Spells, DOTBonusDamageSplitOverDuration)) {
if (extra_dmg) {
const int duration = CalcBuffDuration(this, target, spell_id);
if (duration > 0) {
extra_dmg /= duration;
}
}
}
value -= extra_dmg;
@@ -426,7 +432,7 @@ int64 Mob::GetActSpellHealing(uint16 spell_id, int64 value, Mob* target, bool fr
}
}
if (GetClass() == CLERIC) {
if (GetClass() == Class::Cleric) {
value += int64(base_value*RuleI(Spells, ClericInnateHealFocus) / 100); //confirmed on live parsing clerics get an innate 5 pct heal focus
}
value += int64(base_value*GetFocusEffect(focusImprovedHeal, spell_id, nullptr, from_buff_tic) / 100);
@@ -518,11 +524,14 @@ int64 Mob::GetActSpellHealing(uint16 spell_id, int64 value, Mob* target, bool fr
}
if (extra_heal) {
int duration = CalcBuffDuration(this, target, spell_id);
if (duration > 0) {
extra_heal /= duration;
value += extra_heal;
if (RuleB(Spells, HOTBonusHealingSplitOverDuration)) {
const int duration = CalcBuffDuration(this, target, spell_id);
if (duration > 0) {
extra_heal /= duration;
}
}
value += extra_heal;
}
value *= critical_modifier;
@@ -632,7 +641,7 @@ bool Client::TrainDiscipline(uint32 itemid) {
}
const auto player_class = GetClass();
if (player_class == WIZARD || player_class == ENCHANTER || player_class == MAGICIAN || player_class == NECROMANCER) {
if (player_class == Class::Wizard || player_class == Class::Enchanter || player_class == Class::Magician || player_class == Class::Necromancer) {
Message(Chat::Red, "Your class cannot learn from this tome.");
//summon them the item back...
SummonItem(itemid);
@@ -906,7 +915,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
if (reduced_recast > 0) {
instant_recast = false;
if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) {
if (GetClass() == Class::Bard && IsCasting() && spells[spell_id].cast_time == 0) {
if (DoCastingChecksOnCaster(spell_id, EQ::spells::CastingSlot::Discipline)) {
SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, (uint32)DiscTimer, reduced_recast, false);
}
@@ -921,7 +930,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
}
if (instant_recast) {
if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) {
if (GetClass() == Class::Bard && IsCasting() && spells[spell_id].cast_time == 0) {
if (DoCastingChecksOnCaster(spell_id, EQ::spells::CastingSlot::Discipline)) {
SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, 0xFFFFFFFF, 0, false);
}
@@ -1320,7 +1329,7 @@ void EntityList::AEAttack(
) {
for (int i = 0; i < attack_rounds; i++) {
if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER) {
if (!attacker->IsClient() || attacker->GetClass() == Class::Monk || attacker->GetClass() == Class::Ranger) {
attacker->Attack(current_mob, Hand, false, false, is_from_spell);
} else {
attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell);
+60 -10
View File
@@ -186,6 +186,14 @@ const char *QuestEventSubroutines[_LargestEventID] = {
"EVENT_UNMEMORIZE_SPELL",
"EVENT_SCRIBE_SPELL",
"EVENT_UNSCRIBE_SPELL",
"EVENT_LOOT_ADDED",
"EVENT_LDON_POINTS_GAIN",
"EVENT_LDON_POINTS_LOSS",
"EVENT_ALT_CURRENCY_GAIN",
"EVENT_ALT_CURRENCY_LOSS",
"EVENT_CRYSTAL_GAIN",
"EVENT_CRYSTAL_LOSS",
// Add new events before these or Lua crashes
"EVENT_SPELL_EFFECT_BOT",
"EVENT_SPELL_EFFECT_BUFF_TIC_BOT"
@@ -1883,19 +1891,17 @@ 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]);
if (extra_pointers && extra_pointers->size() >= 1)
{
Corpse* corpse = std::any_cast<Corpse*>(extra_pointers->at(0));
if (corpse)
{
if (extra_pointers && extra_pointers->size() >= 1) {
Corpse *corpse = std::any_cast<Corpse *>(extra_pointers->at(0));
if (corpse) {
ExportVar(package_name.c_str(), "killed_corpse_id", corpse->GetID());
}
}
if (extra_pointers && extra_pointers->size() >= 2)
{
NPC* killed = std::any_cast<NPC*>(extra_pointers->at(1));
if (killed)
{
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);
@@ -2241,6 +2247,50 @@ void PerlembParser::ExportEventVariables(
break;
}
case EVENT_LOOT_ADDED: {
if (extra_pointers && extra_pointers->size() == 1) {
auto *inst = std::any_cast<EQ::ItemInstance *>(extra_pointers->at(0));
if (inst) {
ExportVar(package_name.c_str(), "item", "QuestItem", inst);
ExportVar(package_name.c_str(), "item_id", inst->GetID());
ExportVar(package_name.c_str(), "item_name", inst->GetItem()->Name);
ExportVar(package_name.c_str(), "item_charges", inst->GetCharges());
ExportVar(package_name.c_str(), "augment_one", inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN));
ExportVar(package_name.c_str(), "augment_two", inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 1));
ExportVar(package_name.c_str(), "augment_three", inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 2));
ExportVar(package_name.c_str(), "augment_four", inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 3));
ExportVar(package_name.c_str(), "augment_five", inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 4));
ExportVar(package_name.c_str(), "augment_six", inst->GetAugmentItemID(EQ::invaug::SOCKET_END));
}
}
}
case EVENT_LDON_POINTS_GAIN:
case EVENT_LDON_POINTS_LOSS: {
Seperator sep(data);
ExportVar(package_name.c_str(), "theme_id", sep.arg[0]);
ExportVar(package_name.c_str(), "points", sep.arg[1]);
break;
}
case EVENT_ALT_CURRENCY_GAIN:
case EVENT_ALT_CURRENCY_LOSS: {
Seperator sep(data);
ExportVar(package_name.c_str(), "currency_id", sep.arg[0]);
ExportVar(package_name.c_str(), "amount", sep.arg[1]);
ExportVar(package_name.c_str(), "total", sep.arg[2]);
break;
}
case EVENT_CRYSTAL_GAIN:
case EVENT_CRYSTAL_LOSS: {
Seperator sep(data);
ExportVar(package_name.c_str(), "ebon_amount", sep.arg[0]);
ExportVar(package_name.c_str(), "radiant_amount", sep.arg[1]);
ExportVar(package_name.c_str(), "is_reclaim", sep.arg[2]);
break;
}
default: {
break;
}
+5 -5
View File
@@ -4918,7 +4918,7 @@ void EntityList::ZoneWho(Client *c, Who_All_Struct *Who)
FormatMSGID = 5024; // 5024 %T1[ANONYMOUS] %2 %3
else if (ClientEntry->GetAnon() == 2)
FormatMSGID = 5023; // 5023 %T1[ANONYMOUS] %2 %3 %4
uint32 PlayerClass = NO_CLASS;
uint32 PlayerClass = Class::None;
uint32 PlayerLevel = 0;
uint32 PlayerRace = RACE_DOUG_0;
uint32 ZoneMSGID = 0xFFFFFFFF;
@@ -4995,7 +4995,7 @@ uint32 EntityList::CheckNPCsClose(Mob *center)
auto it = npc_list.begin();
while (it != npc_list.end()) {
NPC *cur = it->second;
if (!cur || cur == center || cur->IsPet() || cur->GetClass() == LDON_TREASURE ||
if (!cur || cur == center || cur->IsPet() || cur->GetClass() == Class::LDoNTreasure ||
cur->GetBodyType() == BT_NoTarget || cur->GetBodyType() == BT_Special) {
++it;
continue;
@@ -5355,7 +5355,7 @@ void EntityList::AddLootToNPCS(uint32 item_id, uint32 count)
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (!it->second->IsPet()
&& it->second->GetClass() != LDON_TREASURE
&& it->second->GetClass() != Class::LDoNTreasure
&& it->second->GetBodyType() != BT_NoTarget
&& it->second->GetBodyType() != BT_NoTarget2
&& it->second->GetBodyType() != BT_Special)
@@ -5376,7 +5376,7 @@ void EntityList::AddLootToNPCS(uint32 item_id, uint32 count)
it = npc_list.begin();
while (it != npc_list.end()) {
if (!it->second->IsPet()
&& it->second->GetClass() != LDON_TREASURE
&& it->second->GetClass() != Class::LDoNTreasure
&& it->second->GetBodyType() != BT_NoTarget
&& it->second->GetBodyType() != BT_NoTarget2
&& it->second->GetBodyType() != BT_Special)
@@ -5429,7 +5429,7 @@ NPC *EntityList::GetClosestBanker(Mob *sender, uint32 &distance)
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (it->second->GetClass() == BANKER) {
if (it->second->GetClass() == Class::Banker) {
uint32 nd = ((it->second->GetY() - sender->GetY()) * (it->second->GetY() - sender->GetY())) +
((it->second->GetX() - sender->GetX()) * (it->second->GetX() - sender->GetX()));
if (nd < distance){
+2 -2
View File
@@ -529,8 +529,8 @@ public:
inline const std::unordered_map<uint16, Merc *> &GetMercList() { return merc_list; }
inline const std::unordered_map<uint16, Client *> &GetClientList() { return client_list; }
inline const std::list<Bot *> &GetBotList() { return bot_list; }
std::vector<Bot *> GetBotListByCharacterID(uint32 character_id, uint8 class_id = NO_CLASS);
std::vector<Bot *> GetBotListByClientName(std::string client_name, uint8 class_id = NO_CLASS);
std::vector<Bot *> GetBotListByCharacterID(uint32 character_id, uint8 class_id = Class::None);
std::vector<Bot *> GetBotListByClientName(std::string client_name, uint8 class_id = Class::None);
void SignalAllBotsByOwnerCharacterID(uint32 character_id, int signal_id);
void SignalAllBotsByOwnerName(std::string owner_name, int signal_id);
void SignalBotByBotID(uint32 bot_id, int signal_id);
+8
View File
@@ -128,6 +128,14 @@ typedef enum {
EVENT_UNMEMORIZE_SPELL,
EVENT_SCRIBE_SPELL,
EVENT_UNSCRIBE_SPELL,
EVENT_LOOT_ADDED,
EVENT_LDON_POINTS_GAIN,
EVENT_LDON_POINTS_LOSS,
EVENT_ALT_CURRENCY_GAIN,
EVENT_ALT_CURRENCY_LOSS,
EVENT_CRYSTAL_GAIN,
EVENT_CRYSTAL_LOSS,
// Add new events before these or Lua crashes
EVENT_SPELL_EFFECT_BOT,
EVENT_SPELL_EFFECT_BUFF_TIC_BOT,
+9 -9
View File
@@ -128,8 +128,8 @@ uint64 Client::CalcEXP(uint8 consider_level, bool ignore_modifiers) {
if (RuleB(Character, UseRaceClassExpBonuses)) {
if (
GetClass() == WARRIOR ||
GetClass() == ROGUE ||
GetClass() == Class::Warrior ||
GetClass() == Class::Rogue ||
GetBaseRace() == HALFLING
) {
total_modifier *= 1.05;
@@ -295,7 +295,7 @@ void Client::CalculateStandardAAExp(uint64 &add_aaxp, uint8 conlevel, bool resex
aatotalmod *= 1.05;
}
if (GetClass() == ROGUE || GetClass() == WARRIOR) {
if (GetClass() == Class::Rogue || GetClass() == Class::Warrior) {
aatotalmod *= 1.05;
}
}
@@ -443,7 +443,7 @@ void Client::CalculateExp(uint64 in_add_exp, uint64 &add_exp, uint64 &add_aaxp,
totalmod *= 1.05;
}
if (GetClass() == ROGUE || GetClass() == WARRIOR) {
if (GetClass() == Class::Rogue || GetClass() == Class::Warrior) {
totalmod *= 1.05;
}
}
@@ -1051,15 +1051,15 @@ uint32 Client::GetEXPForLevel(uint16 check_level)
if(RuleB(Character,UseOldClassExpPenalties))
{
float classmod = 1.0;
if(GetClass() == PALADIN || GetClass() == SHADOWKNIGHT || GetClass() == RANGER || GetClass() == BARD) {
if(GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight || GetClass() == Class::Ranger || GetClass() == Class::Bard) {
classmod = 1.4;
} else if(GetClass() == MONK) {
} else if(GetClass() == Class::Monk) {
classmod = 1.2;
} else if(GetClass() == WIZARD || GetClass() == ENCHANTER || GetClass() == MAGICIAN || GetClass() == NECROMANCER) {
} else if(GetClass() == Class::Wizard || GetClass() == Class::Enchanter || GetClass() == Class::Magician || GetClass() == Class::Necromancer) {
classmod = 1.1;
} else if(GetClass() == ROGUE) {
} else if(GetClass() == Class::Rogue) {
classmod = 0.91;
} else if(GetClass() == WARRIOR) {
} else if(GetClass() == Class::Warrior) {
classmod = 0.9;
}
+7 -5
View File
@@ -2,10 +2,12 @@
void command_acceptrules(Client *c, const Seperator *sep)
{
if (!database.GetAgreementFlag(c->AccountID())) {
database.SetAgreementFlag(c->AccountID());
c->SendAppearancePacket(AT_Anim, ANIM_STAND);
c->Message(Chat::White, "It is recorded you have agreed to the rules.");
if (database.GetAgreementFlag(c->AccountID())) {
c->Message(Chat::White, "You have already agreed to the rules.");
return;
}
}
database.SetAgreementFlag(c->AccountID());
c->SendAppearancePacket(AT_Anim, ANIM_STAND);
c->Message(Chat::White, "It is recorded you have agreed to the rules.");
}
+1 -4
View File
@@ -23,10 +23,7 @@ void command_castspell(Client *c, const Seperator *sep)
const uint16 spell_id = Strings::ToUnsignedInt(sep->arg[1]);
if (IsCastRestrictedSpell(spell_id) && c->Admin() < commandCastSpecials) {
c->Message(Chat::White, "Unable to cast spell.");
return;
} else if (spell_id >= SPDAT_RECORDS) {
if (spell_id >= SPDAT_RECORDS) {
c->Message(Chat::White, "Invalid Spell ID.");
return;
}
+113 -182
View File
@@ -9,18 +9,22 @@ void command_corpse(Client *c, const Seperator *sep)
c->Message(Chat::White, "Usage: #corpse deletenpccorpses - Deletes all NPC corpses");
c->Message(Chat::White, "Usage: #corpse inspectloot - Inspects the loot on a corpse");
c->Message(Chat::White, "Usage: #corpse listnpc - Lists all NPC corpses");
c->Message(Chat::White, "Usage: #corpse lock - Locks the corpse, only GMs can loot the corpse when it is locked");
c->Message(
Chat::White,
"Usage: #corpse lock - Locks the corpse, only GMs can loot the corpse when it is locked"
);
c->Message(Chat::White, "Usage: #corpse removecash - Removes the cash from a corpse");
c->Message(Chat::White, "Usage: #corpse unlock - Unlocks the corpses, allowing non-GMs to loot the corpse");
if (c->Admin() >= commandEditPlayerCorpses) {
c->Message(Chat::White, "Usage: #corpse charid [Character ID] - Change player corpse's owner");
c->Message(Chat::White, "Usage: #corpse deleteplayercorpses - Deletes all player corpses");
c->Message(Chat::White, "Usage: #corpse depop [Bury] - Depops single target corpse.");
c->Message(Chat::White, "Usage: #corpse depopall [Bury] - Depops all target player's corpses.");
c->Message(Chat::White, "Usage: #corpse listplayer - Lists all player corpses");
c->Message(Chat::White, "Usage: #corpse moveallgraveyard - Moves all player corpses to the current zone's graveyard or non-instance");
c->Message(Chat::White, "Note: Set bury to 0 to skip burying the corpses.");
}
c->Message(Chat::White, "Usage: #corpse charid [Character ID] - Change player corpse's owner");
c->Message(Chat::White, "Usage: #corpse deleteplayercorpses - Deletes all player corpses");
c->Message(Chat::White, "Usage: #corpse depop [Bury] - Depops single target corpse.");
c->Message(Chat::White, "Usage: #corpse depopall [Bury] - Depops all target player's corpses.");
c->Message(Chat::White, "Usage: #corpse listplayer - Lists all player corpses");
c->Message(
Chat::White,
"Usage: #corpse moveallgraveyard - Moves all player corpses to the current zone's graveyard or non-instance"
);
c->Message(Chat::White, "Note: Set bury to 0 to skip burying the corpses.");
return;
}
@@ -59,72 +63,57 @@ void command_corpse(Client *c, const Seperator *sep)
c->Message(Chat::White, "Usage: #corpse deletenpccorpses - Deletes all NPC corpses");
c->Message(Chat::White, "Usage: #corpse inspectloot - Inspects the loot on a corpse");
c->Message(Chat::White, "Usage: #corpse listnpc - Lists all NPC corpses");
c->Message(Chat::White, "Usage: #corpse lock - Locks the corpse, only GMs can loot the corpse when it is locked");
c->Message(
Chat::White,
"Usage: #corpse lock - Locks the corpse, only GMs can loot the corpse when it is locked"
);
c->Message(Chat::White, "Usage: #corpse removecash - Removes the cash from a corpse");
c->Message(Chat::White, "Usage: #corpse unlock - Unlocks the corpses, allowing non-GMs to loot the corpse");
if (c->Admin() >= commandEditPlayerCorpses) {
c->Message(Chat::White, "Usage: #corpse charid [Character ID] - Change player corpse's owner");
c->Message(Chat::White, "Usage: #corpse deleteplayercorpses - Deletes all player corpses");
c->Message(Chat::White, "Usage: #corpse depop [Bury] - Depops single target corpse.");
c->Message(Chat::White, "Usage: #corpse depopall [Bury] - Depops all target player's corpses.");
c->Message(Chat::White, "Usage: #corpse listplayer - Lists all player corpses");
c->Message(Chat::White, "Usage: #corpse moveallgraveyard - Moves all player corpses to the current zone's graveyard or non-instance");
c->Message(Chat::White, "Note: Set bury to 0 to skip burying the corpses.");
}
c->Message(Chat::White, "Usage: #corpse charid [Character ID] - Change player corpse's owner");
c->Message(Chat::White, "Usage: #corpse deleteplayercorpses - Deletes all player corpses");
c->Message(Chat::White, "Usage: #corpse depop [Bury] - Depops single target corpse.");
c->Message(Chat::White, "Usage: #corpse depopall [Bury] - Depops all target player's corpses.");
c->Message(Chat::White, "Usage: #corpse listplayer - Lists all player corpses");
c->Message(
Chat::White,
"Usage: #corpse moveallgraveyard - Moves all player corpses to the current zone's graveyard or non-instance"
);
c->Message(Chat::White, "Note: Set bury to 0 to skip burying the corpses.");
return;
}
if (is_delete_player_corpses) {
if (c->Admin() >= commandEditPlayerCorpses) {
auto corpses_deleted = entity_list.DeletePlayerCorpses();
auto deleted_string = (
corpses_deleted ?
fmt::format(
"{} Player corpse{} deleted.",
corpses_deleted,
corpses_deleted != 1 ? "s" : ""
) :
"There are no player corpses to delete."
);
c->Message(Chat::White, deleted_string.c_str());
} else {
c->Message(Chat::White, "Your status is not high enough to delete player corpses.");
return;
}
auto corpses_deleted = entity_list.DeletePlayerCorpses();
auto deleted_string = (
corpses_deleted ?
fmt::format(
"{} Player corpse{} deleted.",
corpses_deleted,
corpses_deleted != 1 ? "s" : ""
) :
"There are no player corpses to delete."
);
c->Message(Chat::White, deleted_string.c_str());
} else if (is_delete) {
if (!target || !target->IsCorpse()) {
c->Message(Chat::White, "You must target a corpse to use this command.");
return;
}
if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to delete a player corpse.");
return;
}
c->Message(
Chat::White,
fmt::format(
"Deleting {} corpse {}.",
target->IsNPCCorpse() ? "NPC" : "player",
c->GetTargetDescription(target)
).c_str()
);
if (
target->IsNPCCorpse() ||
c->Admin() >= commandEditPlayerCorpses
) {
c->Message(
Chat::White,
fmt::format(
"Deleting {} corpse {}.",
target->IsNPCCorpse() ? "NPC" : "player",
c->GetTargetDescription(target)
).c_str()
);
target->CastToCorpse()->Delete();
}
target->CastToCorpse()->Delete();
} else if (is_list_npc) {
entity_list.ListNPCCorpses(c);
} else if (is_list_player) {
if (c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to list player corpses.");
return;
}
entity_list.ListPlayerCorpses(c);
} else if (is_delete_npc_corpses) {
auto corpses_deleted = entity_list.DeleteNPCCorpses();
@@ -139,42 +128,32 @@ void command_corpse(Client *c, const Seperator *sep)
);
c->Message(Chat::White, deleted_string.c_str());
} else if (is_character_id) {
if (c->Admin() >= commandEditPlayerCorpses) {
if (!target || !target->IsPlayerCorpse()) {
c->Message(Chat::White, "You must target a player corpse to use this command.");
return;
}
if (!sep->IsNumber(2)) {
c->Message(Chat::White, "Usage: #corpse charid [Character ID] - Change player corpse's owner");
return;
}
auto character_id = Strings::ToInt(sep->arg[2]);
c->Message(
Chat::White,
fmt::format(
"Setting the owner to {} ({}) for the player corpse {}.",
database.GetCharNameByID(character_id),
target->CastToCorpse()->SetCharID(character_id),
c->GetTargetDescription(target)
).c_str()
);
} else {
c->Message(Chat::White, "Your status is not high enough to modify a player corpse's owner.");
if (!target || !target->IsPlayerCorpse()) {
c->Message(Chat::White, "You must target a player corpse to use this command.");
return;
}
if (!sep->IsNumber(2)) {
c->Message(Chat::White, "Usage: #corpse charid [Character ID] - Change player corpse's owner");
return;
}
const uint32 character_id = Strings::ToUnsignedInt(sep->arg[2]);
c->Message(
Chat::White,
fmt::format(
"Setting the owner to {} ({}) for the player corpse {}.",
database.GetCharNameByID(character_id),
target->CastToCorpse()->SetCharID(character_id),
c->GetTargetDescription(target)
).c_str()
);
} else if (is_reset_looter) {
if (!target || !target->IsCorpse()) {
c->Message(Chat::White, "You must target a corpse to use this command.");
return;
}
if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to reset looter on a player corpse.");
return;
}
target->CastToCorpse()->ResetLooter();
c->Message(
Chat::White,
@@ -190,36 +169,22 @@ void command_corpse(Client *c, const Seperator *sep)
return;
}
if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to remove cash from a player corpse.");
return;
}
target->CastToCorpse()->RemoveCash();
if (
target->IsNPCCorpse() ||
c->Admin() >= commandEditPlayerCorpses
) {
target->CastToCorpse()->RemoveCash();
c->Message(
Chat::White,
fmt::format(
"Removed cash from {} corpse {}.",
target->IsNPCCorpse() ? "NPC" : "player",
c->GetTargetDescription(target)
).c_str()
);
}
c->Message(
Chat::White,
fmt::format(
"Removed cash from {} corpse {}.",
target->IsNPCCorpse() ? "NPC" : "player",
c->GetTargetDescription(target)
).c_str()
);
} else if (is_inspect_loot) {
if (!target || !target->IsCorpse()) {
c->Message(Chat::White, "You must target a corpse to use this command.");
return;
}
if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to inspect the loot of a player corpse.");
return;
}
target->CastToCorpse()->QueryLoot(c);
} else if (is_lock) {
if (!target || !target->IsCorpse()) {
@@ -227,11 +192,6 @@ void command_corpse(Client *c, const Seperator *sep)
return;
}
if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to lock player corpses.");
return;
}
target->CastToCorpse()->Lock();
c->Message(
Chat::White,
@@ -247,11 +207,6 @@ void command_corpse(Client *c, const Seperator *sep)
return;
}
if (target->IsPlayerCorpse() && c->Admin() < commandEditPlayerCorpses) {
c->Message(Chat::White, "Your status is not high enough to unlock player corpses.");
return;
}
target->CastToCorpse()->UnLock();
c->Message(
Chat::White,
@@ -267,30 +222,20 @@ void command_corpse(Client *c, const Seperator *sep)
return;
}
if (c->Admin() >= commandEditPlayerCorpses) {
bool bury_corpse = (
sep->IsNumber(2) ?
(
Strings::ToInt(sep->arg[2]) != 0 ?
true :
false
) :
false
);
c->Message(
Chat::White,
fmt::format(
"Depopping player corpse {}.",
c->GetTargetDescription(target)
).c_str()
);
target->CastToCorpse()->DepopPlayerCorpse();
if (bury_corpse) {
target->CastToCorpse()->Bury();
}
} else {
c->Message(Chat::White, "Your status is not high enough to depop a player corpse.");
return;
const bool bury_corpse = sep->IsNumber(2) ? Strings::ToBool(sep->arg[2]) : false;
c->Message(
Chat::White,
fmt::format(
"Depopping player corpse {}.",
c->GetTargetDescription(target)
).c_str()
);
target->CastToCorpse()->DepopPlayerCorpse();
if (bury_corpse) {
target->CastToCorpse()->Bury();
}
} else if (is_depop_all) {
if (!target || !target->IsClient()) {
@@ -298,50 +243,36 @@ void command_corpse(Client *c, const Seperator *sep)
return;
}
if (c->Admin() >= commandEditPlayerCorpses) {
bool bury_corpse = (
sep->IsNumber(2) ?
(
Strings::ToInt(sep->arg[2]) != 0 ?
true :
false
) :
false
);
const bool bury_corpse = sep->IsNumber(2) ? Strings::ToBool(sep->arg[2]) : false;
c->Message(
Chat::White,
fmt::format(
"Depopping all player corpses for {}.",
c->GetTargetDescription(target)
).c_str()
);
target->CastToClient()->DepopAllCorpses();
if (bury_corpse) {
target->CastToClient()->BuryPlayerCorpses();
}
} else if (is_move_all_to_graveyard) {
const int moved_count = entity_list.MovePlayerCorpsesToGraveyard(true);
if (moved_count) {
c->Message(
Chat::White,
fmt::format(
"Depopping all player corpses for {}.",
c->GetTargetDescription(target)
"Moved {} player corpse{} to graveyard in {} ({}).",
moved_count,
moved_count != 1 ? "s" : "",
ZoneLongName(zone->GetZoneID()),
ZoneName(zone->GetZoneID())
).c_str()
);
target->CastToClient()->DepopAllCorpses();
if (bury_corpse) {
target->CastToClient()->BuryPlayerCorpses();
}
} else {
c->Message(Chat::White, "Your status is not high enough to depop all of a player's corpses.");
return;
}
} else if (is_move_all_to_graveyard) {
int moved_count = entity_list.MovePlayerCorpsesToGraveyard(true);
if (c->Admin() >= commandEditPlayerCorpses) {
if (moved_count) {
c->Message(
Chat::White,
fmt::format(
"Moved {} player corpse{} to graveyard in {} ({}).",
moved_count,
moved_count != 1 ? "s" : "",
ZoneLongName(zone->GetZoneID()),
ZoneName(zone->GetZoneID())
).c_str()
);
} else {
c->Message(Chat::White, "There are no player corpses to move to the graveyard.");
}
} else {
c->Message(Chat::White, "Your status is not high enough to move all player corpses to the graveyard.");
c->Message(Chat::White, "There are no player corpses to move to the graveyard.");
}
}
}
+19 -16
View File
@@ -2,24 +2,27 @@
void command_disarmtrap(Client *c, const Seperator *sep)
{
Mob *target = c->GetTarget();
if (!target) {
c->Message(Chat::Red, "You must have a target.");
Mob *t = c->GetTarget();
if (!t || !t->IsNPC()) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
if (target->IsNPC()) {
if (c->HasSkill(EQ::skills::SkillDisarmTraps)) {
if (DistanceSquaredNoZ(c->GetPosition(), target->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) {
c->Message(Chat::Red, "%s is too far away.", target->GetCleanName());
return;
}
c->HandleLDoNDisarm(target->CastToNPC(), c->GetSkill(EQ::skills::SkillDisarmTraps), LDoNTypeMechanical);
}
else {
c->Message(Chat::Red, "You do not have the disarm trap skill.");
}
if (!c->HasSkill(EQ::skills::SkillDisarmTraps)) {
c->Message(Chat::White, "You do not have the Disarm Trap skill.");
return;
}
}
if (DistanceSquaredNoZ(c->GetPosition(), t->GetPosition()) > RuleI(Adventure, LDoNTrapDistanceUse)) {
c->Message(
Chat::White,
fmt::format(
"{} is too far away.",
t->GetCleanName()
).c_str()
);
return;
}
c->HandleLDoNDisarm(t->CastToNPC(), c->GetSkill(EQ::skills::SkillDisarmTraps), LDoNTypeMechanical);
}
+1 -1
View File
@@ -10,7 +10,7 @@ void command_doanim(Client *c, const Seperator *sep)
}
Mob* t = c;
if (c->GetTarget() && c->Admin() >= commandDoAnimOthers) {
if (c->GetTarget()) {
t = c->GetTarget();
}
-1
View File
@@ -1,6 +1,5 @@
#include "../client.h"
#include "door_manipulation.h"
#include "../doors.h"
void command_door(Client *c, const Seperator *sep)
{
+2 -2
View File
@@ -4,7 +4,7 @@ void FindClass(Client *c, const Seperator *sep)
{
if (sep->IsNumber(2)) {
const auto class_id = Strings::ToInt(sep->arg[2]);
if (EQ::ValueWithin(class_id, WARRIOR, BERSERKER)) {
if (EQ::ValueWithin(class_id, Class::Warrior, Class::Berserker)) {
const std::string& class_name = GetClassIDName(class_id);
c->Message(
Chat::White,
@@ -41,7 +41,7 @@ void FindClass(Client *c, const Seperator *sep)
auto found_count = 0;
for (uint16 class_id = WARRIOR; class_id <= MERCENARY_MASTER; class_id++) {
for (uint16 class_id = Class::Warrior; class_id <= Class::MercenaryLiaison; class_id++) {
const std::string& class_name = GetClassIDName(class_id);
const auto& class_name_lower = Strings::ToLower(class_name);
if (!Strings::Contains(class_name_lower, search_criteria)) {
+3 -60
View File
@@ -81,11 +81,6 @@ void command_guild(Client *c, const Seperator *sep)
).c_str()
);
} else {
if (c->Admin() < minStatusToEditOtherGuilds) {
c->Message(Chat::White, "You cannot edit other peoples' guilds.");
return;
}
auto guild_name = sep->argplus[3];
auto guild_id = guild_mgr.CreateGuild(sep->argplus[3], leader_id);
@@ -144,16 +139,6 @@ void command_guild(Client *c, const Seperator *sep)
return;
}
if (c->Admin() < minStatusToEditOtherGuilds) {
if (c->GuildID() != guild_id) {
c->Message(Chat::White, "You cannot edit other peoples' guilds.");
return;
} else if (!guild_mgr.CheckGMStatus(guild_id, c->Admin())) {
c->Message(Chat::White, "You cannot edit your current guild, your status is not high enough.");
return;
}
}
LogGuilds(
"[{}]: Deleting guild [{}] ([{}]) with GM command",
c->GetName(),
@@ -175,16 +160,10 @@ void command_guild(Client *c, const Seperator *sep)
SendGuildSubCommands(c);
} else if (is_info) {
if (arguments != 2 && c->IsInAGuild()) {
if (c->Admin() >= minStatusToEditOtherGuilds) {
c->Message(Chat::White, "#guild info [Guild ID]");
} else {
c->Message(Chat::White, "You cannot edit other peoples' guilds.");
}
c->Message(Chat::White, "#guild info [Guild ID]");
} else {
auto guild_id = GUILD_NONE;
if (arguments != 2 || !sep->IsNumber(2)) {
guild_id = c->GuildID();
} else if (c->Admin() >= minStatusToEditOtherGuilds) {
if (sep->IsNumber(2)) {
guild_id = Strings::ToUnsignedInt(sep->arg[2]);
}
@@ -193,11 +172,6 @@ void command_guild(Client *c, const Seperator *sep)
}
}
} else if (is_list) {
if (c->Admin() < minStatusToEditOtherGuilds) {
c->Message(Chat::White, "You cannot edit other peoples' guilds.");
return;
}
guild_mgr.ListGuilds(c, std::string());
} else if (is_rename) {
if (!sep->IsNumber(2)) {
@@ -215,16 +189,6 @@ void command_guild(Client *c, const Seperator *sep)
return;
}
if (c->Admin() < minStatusToEditOtherGuilds) {
if (c->GuildID() != guild_id) {
c->Message(Chat::White, "You cannot edit other peoples' guilds.");
return;
} else if (!guild_mgr.CheckGMStatus(guild_id, c->Admin())) {
c->Message(Chat::White, "You cannot edit your current guild, your status is not high enough.");
return;
}
}
auto new_guild_name = sep->argplus[3];
LogGuilds(
"[{}]: Renaming guild [{}] ([{}]) to [{}] with GM command",
@@ -293,11 +257,6 @@ void command_guild(Client *c, const Seperator *sep)
return;
}
if (c->Admin() < minStatusToEditOtherGuilds && guild_id != c->GuildID()) {
c->Message(Chat::White, "You cannot edit other peoples' guilds.");
return;
}
if (!guild_id || guild_id == GUILD_NONE) {
LogGuilds(
"[{}]: Removing [{}] ([{}]) from guild with GM command",
@@ -329,6 +288,7 @@ void command_guild(Client *c, const Seperator *sep)
).c_str()
);
} else {
guild_mgr.SetGuild(character_id, GUILD_NONE, 0);
c->Message(
Chat::White,
fmt::format(
@@ -389,16 +349,6 @@ void command_guild(Client *c, const Seperator *sep)
return;
}
if (c->Admin() < minStatusToEditOtherGuilds) {
if (c->GuildID() != guild_id) {
c->Message(Chat::White, "You cannot edit other peoples' guilds.");
return;
} else if (!guild_mgr.CheckGMStatus(guild_id, c->Admin())) {
c->Message(Chat::White, "You cannot edit your current guild, your status is not high enough.");
return;
}
}
LogGuilds(
"[{}]: Setting leader of guild [{}] ([{}]) to [{}] with GM command",
c->GetName(),
@@ -460,11 +410,6 @@ void command_guild(Client *c, const Seperator *sep)
return;
}
if (c->Admin() < minStatusToEditOtherGuilds && character_id != c->CharacterID()) {
c->Message(Chat::White, "You cannot edit other peoples' guilds.");
return;
}
LogGuilds(
"[{}]: Setting [{}] ([{}])'s guild rank to [{}] with GM command",
c->GetName(),
@@ -497,8 +442,6 @@ void command_guild(Client *c, const Seperator *sep)
);
if (!client) {
c->Message(Chat::White, "You must target someone or specify a character name.");
} else if (c->Admin() < minStatusToEditOtherGuilds && client->GuildID() != c->GuildID()) {
c->Message(Chat::White, "You cannot edit other peoples' guilds.");
} else {
if (!client->IsInAGuild()) {
c->Message(
+19 -39
View File
@@ -15,20 +15,13 @@ void command_interrogateinv(Client *c, const Seperator *sep)
// same or not.
if (strcasecmp(sep->arg[1], "help") == 0) {
if (c->Admin() < commandInterrogateInv) {
c->Message(Chat::White, "Usage: #interrogateinv");
c->Message(Chat::White, " Displays your inventory's current in-memory nested storage references");
}
else {
c->Message(Chat::White, "Usage: #interrogateinv [log] [silent]");
c->Message(
Chat::White,
" Displays your or your Player target inventory's current in-memory nested storage references"
);
c->Message(Chat::White, " [log] - Logs interrogation to file");
c->Message(Chat::White, " [silent] - Omits the in-game message portion of the interrogation");
}
return;
c->Message(Chat::White, "Usage: #interrogateinv [log] [silent]");
c->Message(
Chat::White,
" Displays your or your Player target inventory's current in-memory nested storage references"
);
c->Message(Chat::White, " [log] - Logs interrogation to file");
c->Message(Chat::White, " [silent] - Omits the in-game message portion of the interrogation");
}
Client *target = nullptr;
@@ -38,33 +31,20 @@ void command_interrogateinv(Client *c, const Seperator *sep)
bool error = false;
bool allowtrip = false;
if (c->Admin() < commandInterrogateInv) {
if (c->GetInterrogateInvState()) {
c->Message(Chat::Red, "The last use of #interrogateinv on this inventory instance discovered an error...");
c->Message(Chat::Red, "Logging out, zoning or re-arranging items at this point will result in item loss!");
return;
}
target = c;
allowtrip = true;
if (c->GetTarget() == nullptr) {
target = c;
} else if (c->GetTarget()->IsClient()) {
target = c->GetTarget()->CastToClient();
} else {
c->Message(Chat::Default, "Use of this command is limited to Client entities");
return;
}
else {
if (c->GetTarget() == nullptr) {
target = c;
}
else if (c->GetTarget()->IsClient()) {
target = c->GetTarget()->CastToClient();
}
else {
c->Message(Chat::Default, "Use of this command is limited to Client entities");
return;
}
if (strcasecmp(sep->arg[1], "log") == 0) {
log = true;
}
if (strcasecmp(sep->arg[2], "silent") == 0) {
silent = true;
}
if (strcasecmp(sep->arg[1], "log") == 0) {
log = true;
}
if (strcasecmp(sep->arg[2], "silent") == 0) {
silent = true;
}
bool success = target->InterrogateInventory(c, log, silent, allowtrip, error);
+278 -297
View File
@@ -27,50 +27,48 @@ void command_invsnapshot(Client *c, const Seperator *sep)
"<td><c \"#AAAAAA\">takes snapshot of character inventory</td>"
"</tr>";
if (c->Admin() >= commandInvSnapshot) {
window_text.append(
"<tr>"
"<td><c \"#00FF00\">gcount</td>"
"<td></td>"
"<td><c \"#AAAAAA\">returns global snapshot count</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">gclear</td>"
"<td><c \"#FFFF00\"><br>now</td>"
"<td><c \"#AAAAAA\">delete all snapshots - rule<br>delete all snapshots - now</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">count</td>"
"<td></td>"
"<td><c \"#AAAAAA\">returns character snapshot count</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">clear</td>"
"<td><c \"#FFFF00\"><br>now</td>"
"<td><c \"#AAAAAA\">delete character snapshots - rule<br>delete character snapshots - now</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">list</td>"
"<td><br><c \"#FFFF00\">count</td>"
"<td><c \"#AAAAAA\">lists entry ids for current character<br>limits to count</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">parse</td>"
"<td><c \"#00FF00\">tstmp</td>"
"<td><c \"#AAAAAA\">displays slots and items in snapshot</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">compare</td>"
"<td><c \"#00FF00\">tstmp</td>"
"<td><c \"#AAAAAA\">compares inventory against snapshot</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">restore</td>"
"<td><c \"#00FF00\">tstmp</td>"
"<td><c \"#AAAAAA\">restores slots and items in snapshot</td>"
"</tr>"
);
}
window_text.append(
"<tr>"
"<td><c \"#00FF00\">gcount</td>"
"<td></td>"
"<td><c \"#AAAAAA\">returns global snapshot count</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">gclear</td>"
"<td><c \"#FFFF00\"><br>now</td>"
"<td><c \"#AAAAAA\">delete all snapshots - rule<br>delete all snapshots - now</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">count</td>"
"<td></td>"
"<td><c \"#AAAAAA\">returns character snapshot count</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">clear</td>"
"<td><c \"#FFFF00\"><br>now</td>"
"<td><c \"#AAAAAA\">delete character snapshots - rule<br>delete character snapshots - now</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">list</td>"
"<td><br><c \"#FFFF00\">count</td>"
"<td><c \"#AAAAAA\">lists entry ids for current character<br>limits to count</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">parse</td>"
"<td><c \"#00FF00\">tstmp</td>"
"<td><c \"#AAAAAA\">displays slots and items in snapshot</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">compare</td>"
"<td><c \"#00FF00\">tstmp</td>"
"<td><c \"#AAAAAA\">compares inventory against snapshot</td>"
"</tr>"
"<tr>"
"<td><c \"#00FF00\">restore</td>"
"<td><c \"#00FF00\">tstmp</td>"
"<td><c \"#AAAAAA\">restores slots and items in snapshot</td>"
"</tr>"
);
window_text.append(
"</table>"
@@ -81,34 +79,30 @@ void command_invsnapshot(Client *c, const Seperator *sep)
return;
}
if (c->Admin() >= commandInvSnapshot) { // global arguments
if (strcmp(sep->arg[1], "gcount") == 0) {
auto is_count = database.CountInvSnapshots();
c->Message(
Chat::White,
"There %s %i inventory snapshot%s.",
(is_count == 1 ? "is" : "are"),
is_count,
(is_count == 1 ? "" : "s"));
if (strcmp(sep->arg[1], "gcount") == 0) {
auto is_count = database.CountInvSnapshots();
return;
}
if (strcmp(sep->arg[1], "gclear") == 0) {
if (strcmp(sep->arg[2], "now") == 0) {
database.ClearInvSnapshots(true);
c->Message(Chat::White, "Inventory snapshots cleared using current time.");
} else {
database.ClearInvSnapshots();
c->Message(
Chat::White,
"There %s %i inventory snapshot%s.",
(is_count == 1 ? "is" : "are"),
is_count,
(is_count == 1 ? "" : "s"));
return;
Chat::White, "Inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).",
RuleI(Character, InvSnapshotHistoryD), (RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s"));
}
if (strcmp(sep->arg[1], "gclear") == 0) {
if (strcmp(sep->arg[2], "now") == 0) {
database.ClearInvSnapshots(true);
c->Message(Chat::White, "Inventory snapshots cleared using current time.");
}
else {
database.ClearInvSnapshots();
c->Message(
Chat::White, "Inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).",
RuleI(Character, InvSnapshotHistoryD), (RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s"));
}
return;
}
return;
}
if (!c->GetTarget() || !c->GetTarget()->IsClient()) {
@@ -127,8 +121,7 @@ void command_invsnapshot(Client *c, const Seperator *sep)
tc->GetName(),
RuleI(Character, InvSnapshotMinIntervalM),
(RuleI(Character, InvSnapshotMinIntervalM) == 1 ? "" : "s"));
}
else {
} else {
tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinRetryM));
c->Message(
Chat::White,
@@ -141,278 +134,266 @@ void command_invsnapshot(Client *c, const Seperator *sep)
return;
}
if (c->Admin() >= commandInvSnapshot) {
if (strcmp(sep->arg[1], "count") == 0) {
auto is_count = database.CountCharacterInvSnapshots(tc->CharacterID());
if (strcmp(sep->arg[1], "count") == 0) {
auto is_count = database.CountCharacterInvSnapshots(tc->CharacterID());
c->Message(
Chat::White,
"%s (id: %u) has %i inventory snapshot%s.",
tc->GetName(),
tc->CharacterID(),
is_count,
(is_count == 1 ? "" : "s"));
return;
}
if (strcmp(sep->arg[1], "clear") == 0) {
if (strcmp(sep->arg[2], "now") == 0) {
database.ClearCharacterInvSnapshots(tc->CharacterID(), true);
c->Message(
Chat::White,
"%s (id: %u) has %i inventory snapshot%s.",
"%s\'s (id: %u) inventory snapshots cleared using current time.",
tc->GetName(),
tc->CharacterID());
} else {
database.ClearCharacterInvSnapshots(tc->CharacterID());
c->Message(
Chat::White,
"%s\'s (id: %u) inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).",
tc->GetName(),
tc->CharacterID(),
is_count,
(is_count == 1 ? "" : "s"));
RuleI(Character, InvSnapshotHistoryD),
(RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s"));
}
return;
}
if (strcmp(sep->arg[1], "list") == 0) {
std::list<std::pair<uint32, int>> is_list;
database.ListCharacterInvSnapshots(tc->CharacterID(), is_list);
if (is_list.empty()) {
c->Message(Chat::White, "No inventory snapshots for %s (id: %u)", tc->GetName(), tc->CharacterID());
return;
}
if (strcmp(sep->arg[1], "clear") == 0) {
if (strcmp(sep->arg[2], "now") == 0) {
database.ClearCharacterInvSnapshots(tc->CharacterID(), true);
c->Message(
Chat::White,
"%s\'s (id: %u) inventory snapshots cleared using current time.",
tc->GetName(),
tc->CharacterID());
}
else {
database.ClearCharacterInvSnapshots(tc->CharacterID());
c->Message(
Chat::White,
"%s\'s (id: %u) inventory snapshots cleared using RuleI(Character, InvSnapshotHistoryD) (%i day%s).",
tc->GetName(),
tc->CharacterID(),
RuleI(Character, InvSnapshotHistoryD),
(RuleI(Character, InvSnapshotHistoryD) == 1 ? "" : "s"));
}
return;
auto list_count = 0;
if (sep->IsNumber(2)) {
list_count = Strings::ToInt(sep->arg[2]);
}
if (list_count < 1 || list_count > is_list.size()) {
list_count = is_list.size();
}
if (strcmp(sep->arg[1], "list") == 0) {
std::list<std::pair<uint32, int>> is_list;
database.ListCharacterInvSnapshots(tc->CharacterID(), is_list);
std::string window_title = StringFormat("Snapshots for %s", tc->GetName());
if (is_list.empty()) {
c->Message(Chat::White, "No inventory snapshots for %s (id: %u)", tc->GetName(), tc->CharacterID());
return;
}
auto list_count = 0;
if (sep->IsNumber(2)) {
list_count = Strings::ToInt(sep->arg[2]);
}
if (list_count < 1 || list_count > is_list.size()) {
list_count = is_list.size();
}
std::string window_title = StringFormat("Snapshots for %s", tc->GetName());
std::string window_text =
"<table>"
"<tr>"
"<td>Timestamp</td>"
"<td>Entry Count</td>"
"</tr>";
for (auto iter : is_list) {
if (!list_count) {
break;
}
window_text.append(
StringFormat(
std::string window_text =
"<table>"
"<tr>"
"<td>%u</td>"
"<td>%i</td>"
"</tr>",
iter.first,
iter.second
));
"<td>Timestamp</td>"
"<td>Entry Count</td>"
"</tr>";
--list_count;
for (auto iter: is_list) {
if (!list_count) {
break;
}
window_text.append(
"</table>"
);
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
return;
}
if (strcmp(sep->arg[1], "parse") == 0) {
if (!sep->IsNumber(2)) {
c->Message(Chat::White, "A timestamp is required to use this option.");
return;
}
uint32 timestamp = Strings::ToUnsignedInt(sep->arg[2]);
if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) {
c->Message(
Chat::White,
"No inventory snapshots for %s (id: %u) exist at %u.",
tc->GetName(),
tc->CharacterID(),
timestamp
);
return;
}
std::list<std::pair<int16, uint32>> parse_list;
database.ParseCharacterInvSnapshot(tc->CharacterID(), timestamp, parse_list);
std::string window_title = StringFormat("Snapshot Parse for %s @ %u", tc->GetName(), timestamp);
std::string window_text = "Slot: ItemID - Description<br>";
for (auto iter : parse_list) {
auto item_data = database.GetItem(iter.second);
std::string window_line = StringFormat(
"%i: %u - %s<br>",
StringFormat(
"<tr>"
"<td>%u</td>"
"<td>%i</td>"
"</tr>",
iter.first,
iter.second,
(item_data ? item_data->Name : "[error]"));
iter.second
));
if (window_text.length() + window_line.length() < 4095) {
window_text.append(window_line);
}
else {
c->Message(Chat::White, "Too many snapshot entries to list...");
break;
}
}
--list_count;
}
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
window_text.append(
"</table>"
);
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
return;
}
if (strcmp(sep->arg[1], "parse") == 0) {
if (!sep->IsNumber(2)) {
c->Message(Chat::White, "A timestamp is required to use this option.");
return;
}
if (strcmp(sep->arg[1], "compare") == 0) {
if (!sep->IsNumber(2)) {
c->Message(Chat::White, "A timestamp is required to use this option.");
return;
uint32 timestamp = Strings::ToUnsignedInt(sep->arg[2]);
if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) {
c->Message(
Chat::White,
"No inventory snapshots for %s (id: %u) exist at %u.",
tc->GetName(),
tc->CharacterID(),
timestamp
);
return;
}
std::list<std::pair<int16, uint32>> parse_list;
database.ParseCharacterInvSnapshot(tc->CharacterID(), timestamp, parse_list);
std::string window_title = StringFormat("Snapshot Parse for %s @ %u", tc->GetName(), timestamp);
std::string window_text = "Slot: ItemID - Description<br>";
for (auto iter: parse_list) {
auto item_data = database.GetItem(iter.second);
std::string window_line = StringFormat(
"%i: %u - %s<br>",
iter.first,
iter.second,
(item_data ? item_data->Name : "[error]"));
if (window_text.length() + window_line.length() < 4095) {
window_text.append(window_line);
} else {
c->Message(Chat::White, "Too many snapshot entries to list...");
break;
}
}
uint32 timestamp = Strings::ToUnsignedInt(sep->arg[2]);
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) {
c->Message(
Chat::White,
"No inventory snapshots for %s (id: %u) exist at %u.",
tc->GetName(),
tc->CharacterID(),
timestamp
);
return;
}
return;
}
std::list<std::pair<int16, uint32>> inv_compare_list;
database.DivergeCharacterInventoryFromInvSnapshot(tc->CharacterID(), timestamp, inv_compare_list);
if (strcmp(sep->arg[1], "compare") == 0) {
if (!sep->IsNumber(2)) {
c->Message(Chat::White, "A timestamp is required to use this option.");
return;
}
std::list<std::pair<int16, uint32>> iss_compare_list;
database.DivergeCharacterInvSnapshotFromInventory(tc->CharacterID(), timestamp, iss_compare_list);
uint32 timestamp = Strings::ToUnsignedInt(sep->arg[2]);
std::string window_title = StringFormat("Snapshot Comparison for %s @ %u", tc->GetName(), timestamp);
if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) {
c->Message(
Chat::White,
"No inventory snapshots for %s (id: %u) exist at %u.",
tc->GetName(),
tc->CharacterID(),
timestamp
);
return;
}
std::string window_text = "Slot: (action) Snapshot -&gt; Inventory<br>";
std::list<std::pair<int16, uint32>> inv_compare_list;
database.DivergeCharacterInventoryFromInvSnapshot(tc->CharacterID(), timestamp, inv_compare_list);
auto inv_iter = inv_compare_list.begin();
auto iss_iter = iss_compare_list.begin();
std::list<std::pair<int16, uint32>> iss_compare_list;
database.DivergeCharacterInvSnapshotFromInventory(tc->CharacterID(), timestamp, iss_compare_list);
while (true) {
std::string window_line;
std::string window_title = StringFormat("Snapshot Comparison for %s @ %u", tc->GetName(), timestamp);
if (inv_iter == inv_compare_list.end() && iss_iter == iss_compare_list.end()) {
break;
}
else if (inv_iter != inv_compare_list.end() && iss_iter == iss_compare_list.end()) {
window_line = StringFormat("%i: (delete) [empty] -&gt; %u<br>", inv_iter->first, inv_iter->second);
std::string window_text = "Slot: (action) Snapshot -&gt; Inventory<br>";
auto inv_iter = inv_compare_list.begin();
auto iss_iter = iss_compare_list.begin();
while (true) {
std::string window_line;
if (inv_iter == inv_compare_list.end() && iss_iter == iss_compare_list.end()) {
break;
} else if (inv_iter != inv_compare_list.end() && iss_iter == iss_compare_list.end()) {
window_line = StringFormat("%i: (delete) [empty] -&gt; %u<br>", inv_iter->first, inv_iter->second);
++inv_iter;
} else if (inv_iter == inv_compare_list.end() && iss_iter != iss_compare_list.end()) {
window_line = StringFormat("%i: (insert) %u -&gt; [empty]<br>", iss_iter->first, iss_iter->second);
++iss_iter;
} else {
if (inv_iter->first < iss_iter->first) {
window_line = StringFormat(
"%i: (delete) [empty] -&gt; %u<br>",
inv_iter->first,
inv_iter->second
);
++inv_iter;
} else if (inv_iter->first > iss_iter->first) {
window_line = StringFormat(
"%i: (insert) %u -&gt; [empty]<br>",
iss_iter->first,
iss_iter->second
);
++iss_iter;
} else {
window_line = StringFormat(
"%i: (replace) %u -&gt; %u<br>",
iss_iter->first,
iss_iter->second,
inv_iter->second
);
++inv_iter;
}
else if (inv_iter == inv_compare_list.end() && iss_iter != iss_compare_list.end()) {
window_line = StringFormat("%i: (insert) %u -&gt; [empty]<br>", iss_iter->first, iss_iter->second);
++iss_iter;
}
else {
if (inv_iter->first < iss_iter->first) {
window_line = StringFormat(
"%i: (delete) [empty] -&gt; %u<br>",
inv_iter->first,
inv_iter->second
);
++inv_iter;
}
else if (inv_iter->first > iss_iter->first) {
window_line = StringFormat(
"%i: (insert) %u -&gt; [empty]<br>",
iss_iter->first,
iss_iter->second
);
++iss_iter;
}
else {
window_line = StringFormat(
"%i: (replace) %u -&gt; %u<br>",
iss_iter->first,
iss_iter->second,
inv_iter->second
);
++inv_iter;
++iss_iter;
}
}
if (window_text.length() + window_line.length() < 4095) {
window_text.append(window_line);
}
else {
c->Message(Chat::White, "Too many comparison entries to list...");
break;
}
}
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
if (window_text.length() + window_line.length() < 4095) {
window_text.append(window_line);
} else {
c->Message(Chat::White, "Too many comparison entries to list...");
break;
}
}
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
return;
}
if (strcmp(sep->arg[1], "restore") == 0) {
if (!sep->IsNumber(2)) {
c->Message(Chat::White, "A timestamp is required to use this option.");
return;
}
if (strcmp(sep->arg[1], "restore") == 0) {
if (!sep->IsNumber(2)) {
c->Message(Chat::White, "A timestamp is required to use this option.");
return;
}
uint32 timestamp = Strings::ToUnsignedInt(sep->arg[2]);
if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) {
c->Message(
Chat::White,
"No inventory snapshots for %s (id: %u) exist at %u.",
tc->GetName(),
tc->CharacterID(),
timestamp
);
return;
}
if (database.SaveCharacterInvSnapshot(tc->CharacterID())) {
tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM));
}
else {
c->Message(
Chat::Red, "Failed to take pre-restore inventory snapshot of %s (id: %u).",
tc->GetName(), tc->CharacterID());
return;
}
if (database.RestoreCharacterInvSnapshot(tc->CharacterID(), timestamp)) {
// cannot delete all valid item slots from client..so, we worldkick
tc->WorldKick(); // self restores update before the 'kick' is processed
c->Message(
Chat::White, "Successfully applied snapshot %u to %s's (id: %u) inventory.",
timestamp, tc->GetName(), tc->CharacterID());
}
else {
c->Message(
Chat::Red, "Failed to apply snapshot %u to %s's (id: %u) inventory.",
timestamp, tc->GetName(), tc->CharacterID());
}
uint32 timestamp = Strings::ToUnsignedInt(sep->arg[2]);
if (!database.ValidateCharacterInvSnapshotTimestamp(tc->CharacterID(), timestamp)) {
c->Message(
Chat::White,
"No inventory snapshots for %s (id: %u) exist at %u.",
tc->GetName(),
tc->CharacterID(),
timestamp
);
return;
}
if (database.SaveCharacterInvSnapshot(tc->CharacterID())) {
tc->SetNextInvSnapshot(RuleI(Character, InvSnapshotMinIntervalM));
} else {
c->Message(
Chat::Red, "Failed to take pre-restore inventory snapshot of %s (id: %u).",
tc->GetName(), tc->CharacterID());
return;
}
if (database.RestoreCharacterInvSnapshot(tc->CharacterID(), timestamp)) {
// cannot delete all valid item slots from client..so, we worldkick
tc->WorldKick(); // self restores update before the 'kick' is processed
c->Message(
Chat::White, "Successfully applied snapshot %u to %s's (id: %u) inventory.",
timestamp, tc->GetName(), tc->CharacterID());
} else {
c->Message(
Chat::Red, "Failed to apply snapshot %u to %s's (id: %u) inventory.",
timestamp, tc->GetName(), tc->CharacterID());
}
return;
}
}
+252 -216
View File
@@ -3,237 +3,273 @@
#include "../object.h"
#include "../doors.h"
struct UniqueEntity {
uint16 entity_id;
std::string entity_name;
uint32 unique_id;
glm::vec4 position;
};
void command_list(Client *c, const Seperator *sep)
{
const int arguments = sep->argnum;
if (!arguments) {
c->Message(Chat::White, "Usage: #list [corpses|doors|npcs|objects|players] [search]");
c->Message(Chat::White, "Example: #list npcs (Blank for all)");
return;
}
const bool is_corpses = !strcasecmp(sep->arg[1], "corpses");
const bool is_doors = !strcasecmp(sep->arg[1], "doors");
const bool is_npcs = !strcasecmp(sep->arg[1], "npcs");
const bool is_objects = !strcasecmp(sep->arg[1], "objects");
const bool is_players = !strcasecmp(sep->arg[1], "players");
if (
!is_corpses &&
!is_doors &&
!is_npcs &&
!is_objects &&
!is_players
) {
c->Message(Chat::White, "Usage: #list [npcs|players|corpses|doors|objects] [search]");
c->Message(Chat::White, "Example: #list npcs (Blank for all)");
return;
}
std::string search_type;
if (strcasecmp(sep->arg[1], "npcs") == 0) {
search_type = "npcs";
std::string unique_type;
if (is_corpses) {
search_type = "corpse";
unique_type = "Corpse ID";
} else if (is_doors) {
search_type = "door";
unique_type = "Door ID";
} else if (is_npcs) {
search_type = "NPC";
unique_type = "NPC ID";
} else if (is_objects) {
search_type = "object";
unique_type = "Object ID";
} else if (is_players) {
search_type = "player";
unique_type = "Character ID";
}
if (strcasecmp(sep->arg[1], "players") == 0) {
search_type = "players";
}
uint32 entity_count = 0;
uint32 found_count = 0;
if (strcasecmp(sep->arg[1], "corpses") == 0) {
search_type = "corpses";
}
const std::string &search_string = sep->arg[2] ? Strings::ToLower(sep->arg[2]) : std::string();
if (strcasecmp(sep->arg[1], "doors") == 0) {
search_type = "doors";
}
std::vector<UniqueEntity> unique_entities;
if (strcasecmp(sep->arg[1], "objects") == 0) {
search_type = "objects";
}
if (is_corpses) {
const auto &l = entity_list.GetCorpseList();
if (search_type.length() > 0) {
for (const auto &e : l) {
Corpse *entity = e.second;
int entity_count = 0;
int found_count = 0;
entity_count++;
std::string search_string;
if (sep->arg[2]) {
search_string = Strings::ToLower(sep->arg[2]);
}
// NPC
if (search_type.find("npcs") != std::string::npos) {
auto &entity_list_search = entity_list.GetMobList();
for (auto &itr : entity_list_search) {
Mob *entity = itr.second;
if (!entity->IsNPC()) {
continue;
}
entity_count++;
std::string entity_name = Strings::ToLower(entity->GetName());
if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) {
continue;
}
std::string saylink = StringFormat(
"#goto %.0f %0.f %.0f",
entity->GetX(),
entity->GetY(),
entity->GetZ() + (entity->IsBoat() ? 50 : 0));
c->Message(
0,
"| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f",
Saylink::Silent(saylink, "Goto").c_str(),
entity->GetID(),
entity->GetName(),
entity->GetX(),
entity->GetY(),
entity->GetZ()
);
found_count++;
const std::string &entity_name = Strings::ToLower(entity->GetName());
if (!search_string.empty() && entity_name.find(search_string) == std::string::npos) {
continue;
}
}
// Client
if (search_type.find("players") != std::string::npos) {
auto &entity_list_search = entity_list.GetClientList();
for (auto &itr : entity_list_search) {
Client *entity = itr.second;
entity_count++;
std::string entity_name = Strings::ToLower(entity->GetName());
/**
* Filter by name
*/
if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) {
continue;
unique_entities.emplace_back(
UniqueEntity{
.entity_id = entity->GetID(),
.entity_name = entity->GetName(),
.unique_id = entity->GetCorpseDBID(),
.position = entity->GetPosition()
}
std::string saylink = StringFormat(
"#goto %.0f %0.f %.0f",
entity->GetX(),
entity->GetY(),
entity->GetZ());
c->Message(
0,
"| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f",
Saylink::Silent(saylink, "Goto").c_str(),
entity->GetID(),
entity->GetName(),
entity->GetX(),
entity->GetY(),
entity->GetZ()
);
found_count++;
}
}
// Corpse
if (search_type.find("corpses") != std::string::npos) {
auto &entity_list_search = entity_list.GetCorpseList();
for (auto &itr : entity_list_search) {
Corpse *entity = itr.second;
entity_count++;
std::string entity_name = Strings::ToLower(entity->GetName());
if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) {
continue;
}
std::string saylink = StringFormat(
"#goto %.0f %0.f %.0f",
entity->GetX(),
entity->GetY(),
entity->GetZ());
c->Message(
0,
"| %s | ID %5d | %s | x %.0f | y %0.f | z %.0f",
Saylink::Silent(saylink, "Goto").c_str(),
entity->GetID(),
entity->GetName(),
entity->GetX(),
entity->GetY(),
entity->GetZ()
);
found_count++;
}
}
// Doors
if (search_type.find("doors") != std::string::npos) {
auto &entity_list_search = entity_list.GetDoorsList();
for (auto &itr : entity_list_search) {
Doors *entity = itr.second;
entity_count++;
std::string entity_name = Strings::ToLower(entity->GetDoorName());
if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) {
continue;
}
std::string saylink = StringFormat(
"#goto %.0f %0.f %.0f",
entity->GetX(),
entity->GetY(),
entity->GetZ());
c->Message(
0,
"| %s | Entity ID %5d | Door ID %i | %s | x %.0f | y %0.f | z %.0f",
Saylink::Silent(saylink, "Goto").c_str(),
entity->GetID(),
entity->GetDoorID(),
entity->GetDoorName(),
entity->GetX(),
entity->GetY(),
entity->GetZ()
);
found_count++;
}
}
// Objects
if (search_type.find("objects") != std::string::npos) {
auto &entity_list_search = entity_list.GetObjectList();
for (auto &itr : entity_list_search) {
Object *entity = itr.second;
entity_count++;
std::string entity_name = Strings::ToLower(entity->GetModelName());
if (search_string.length() > 0 && entity_name.find(search_string) == std::string::npos) {
continue;
}
std::string saylink = StringFormat(
"#goto %.0f %0.f %.0f",
entity->GetX(),
entity->GetY(),
entity->GetZ());
c->Message(
0,
"| %s | Entity ID %5d | Object DBID %i | %s | x %.0f | y %0.f | z %.0f",
Saylink::Silent(saylink, "Goto").c_str(),
entity->GetID(),
entity->GetDBID(),
entity->GetModelName(),
entity->GetX(),
entity->GetY(),
entity->GetZ()
);
found_count++;
}
}
if (found_count) {
c->Message(
0, "Found (%i) of type (%s) in zone (%i) total",
found_count,
search_type.c_str(),
entity_count
);
found_count++;
}
} else if (is_doors) {
const auto &l = entity_list.GetDoorsList();
for (const auto &e : l) {
Doors *entity = e.second;
entity_count++;
const std::string &entity_name = Strings::ToLower(entity->GetDoorName());
if (!search_string.empty() && entity_name.find(search_string) == std::string::npos) {
continue;
}
unique_entities.emplace_back(
UniqueEntity{
.entity_id = entity->GetID(),
.entity_name = entity->GetDoorName(),
.unique_id = entity->GetDoorID(),
.position = entity->GetPosition()
}
);
found_count++;
}
} else if (is_npcs) {
const auto &l = entity_list.GetMobList();
for (const auto &e : l) {
Mob *entity = e.second;
if (!entity->IsNPC()) {
continue;
}
entity_count++;
const std::string &entity_name = Strings::ToLower(entity->GetName());
if (!search_string.empty() && entity_name.find(search_string) == std::string::npos) {
continue;
}
unique_entities.emplace_back(
UniqueEntity{
.entity_id = entity->GetID(),
.entity_name = entity->GetName(),
.unique_id = entity->GetNPCTypeID(),
.position = entity->GetPosition()
}
);
found_count++;
}
} else if (is_objects) {
const auto &l = entity_list.GetObjectList();
for (const auto &e : l) {
Object *entity = e.second;
entity_count++;
const std::string &entity_name = Strings::ToLower(entity->GetModelName());
if (!search_string.empty() && entity_name.find(search_string) == std::string::npos) {
continue;
}
unique_entities.emplace_back(
UniqueEntity{
.entity_id = entity->GetID(),
.entity_name = entity->GetModelName(),
.unique_id = entity->GetDBID(),
.position = glm::vec4(entity->GetX(), entity->GetY(), entity->GetZ(), 0.0f)
}
);
found_count++;
}
} else if (is_players) {
const auto &l = entity_list.GetClientList();
for (const auto &e : l) {
Client *entity = e.second;
entity_count++;
const std::string &entity_name = Strings::ToLower(entity->GetName());
if (!search_string.empty() && entity_name.find(search_string) == std::string::npos) {
continue;
}
unique_entities.emplace_back(
UniqueEntity{
.entity_id = entity->GetID(),
.entity_name = entity->GetName(),
.unique_id = entity->CharacterID(),
.position = entity->GetPosition()
}
);
found_count++;
}
}
else {
c->Message(Chat::White, "Usage of #list");
c->Message(Chat::White, "- #list [npcs|players|corpses|doors|objects] [search]");
c->Message(Chat::White, "- Example: #list npcs (Blank for all)");
}
}
if (!found_count) {
c->Message(
Chat::White,
fmt::format(
"Could not find any {}{}.",
search_type,
(
!search_string.empty() ?
fmt::format(
" matching '{}'",
search_string
) :
""
)
).c_str()
);
return;
}
std::sort(unique_entities.begin(), unique_entities.end(), [](UniqueEntity a, UniqueEntity b) {
if (a.entity_id && b.entity_id) {
return a.entity_id < b.entity_id;
} else {
return a.unique_id < b.unique_id;
}
});
for (const auto& e : unique_entities) {
const std::string &saylink = Saylink::Silent(
fmt::format(
"#goto {:.2f} {:.2f} {:.2f}",
e.position.x,
e.position.y,
e.position.z
),
"Goto"
);
c->Message(
Chat::White,
fmt::format(
"| {}{}{} | {} |",
saylink,
(
e.entity_id ?
fmt::format(
" | ID {}",
e.entity_id
) :
""
),
(
e.unique_id ?
fmt::format(
" | {} {}",
unique_type,
e.unique_id
) :
""
),
e.entity_name
).c_str()
);
}
c->Message(
Chat::White,
fmt::format(
"Found {} {}{}{}, {} total.",
found_count,
search_type,
found_count != 1 ? "s" : "",
(
!search_string.empty() ?
fmt::format(
" matching '{}'",
search_string
) :
""
),
entity_count
).c_str()
);
}
+6 -6
View File
@@ -7,12 +7,12 @@ void command_merchantshop(Client *c, const Seperator *sep)
!m ||
!m->IsNPC() ||
(
m->GetClass() != MERCHANT &&
m->GetClass() != DISCORD_MERCHANT &&
m->GetClass() != ADVENTURE_MERCHANT &&
m->GetClass() != NORRATHS_KEEPERS_MERCHANT &&
m->GetClass() != DARK_REIGN_MERCHANT &&
m->GetClass() != ALT_CURRENCY_MERCHANT
m->GetClass() != Class::Merchant &&
m->GetClass() != Class::DiscordMerchant &&
m->GetClass() != Class::AdventureMerchant &&
m->GetClass() != Class::NorrathsKeepersMerchant &&
m->GetClass() != Class::DarkReignMerchant &&
m->GetClass() != Class::AlternateCurrencyMerchant
)
) {
c->Message(Chat::White, "You must target a merchant.");
+28 -57
View File
@@ -2,18 +2,18 @@
void command_movechar(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
const int arguments = sep->argnum;
if (arguments < 2) {
c->Message(Chat::White, "Usage: #movechar [Character ID|Character Name] [Zone ID|Zone Short Name]");
return;
}
std::string character_name = (
const std::string &character_name = (
sep->IsNumber(1) ?
database.GetCharNameByID(Strings::ToUnsignedInt(sep->arg[1])) :
sep->arg[1]
);
auto character_id = database.GetCharacterID(character_name.c_str());
const uint32 character_id = database.GetCharacterID(character_name.c_str());
if (!character_id) {
c->Message(
Chat::White,
@@ -25,15 +25,15 @@ void command_movechar(Client *c, const Seperator *sep)
return;
}
auto account_id = database.GetAccountIDByChar(character_name.c_str());
const uint32 account_id = database.GetAccountIDByChar(character_name.c_str());
std::string zone_short_name = Strings::ToLower(
const std::string &zone_short_name = Strings::ToLower(
sep->IsNumber(2) ?
ZoneName(Strings::ToUnsignedInt(sep->arg[2]), true) :
sep->arg[2]
);
bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos;
const bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos;
if (is_unknown_zone) {
c->Message(
Chat::White,
@@ -45,63 +45,34 @@ void command_movechar(Client *c, const Seperator *sep)
return;
}
auto zone_id = ZoneID(zone_short_name);
auto z = GetZone(zone_id);
const uint32 zone_id = ZoneID(zone_short_name);
auto z = GetZone(zone_id);
if (!z) {
c->Message(Chat::Red, "Invalid zone.");
return;
}
bool is_special_zone = (
zone_short_name.find("cshome") != std::string::npos ||
zone_short_name.find("load") != std::string::npos ||
zone_short_name.find("load2") != std::string::npos
const bool moved = database.MoveCharacterToZone(character_name.c_str(), zone_id);
std::string moved_string = moved ? "Succeeded" : "Failed";
c->Message(
Chat::White,
fmt::format(
"Character Move {} | Character: {} ({})",
moved_string,
character_name,
character_id
).c_str()
);
if (c->Admin() < commandMovecharToSpecials && is_special_zone) {
c->Message(
Chat::White,
fmt::format(
"{} ({}) is a special zone and you cannot move someone there.",
z->long_name,
zone_short_name
).c_str()
);
return;
}
if (
c->Admin() >= commandMovecharSelfOnly ||
account_id == c->AccountID()
) {
bool moved = database.MoveCharacterToZone(character_name.c_str(), zone_id);
std::string moved_string = (
moved ?
"Succeeded" :
"Failed"
);
c->Message(
Chat::White,
fmt::format(
"Character Move {} | Character: {} ({})",
moved_string,
character_name,
character_id
).c_str()
);
c->Message(
Chat::White,
fmt::format(
"Character Move {} | Zone: {} ({}) ID: {}",
moved_string,
z->long_name,
zone_short_name,
zone_id
).c_str()
);
} else {
c->Message(Chat::White, "You cannot move characters that are not on your account.");
}
c->Message(
Chat::White,
fmt::format(
"Character Move {} | Zone: {} ({}) ID: {}",
moved_string,
z->long_name,
zone_short_name,
zone_id
).c_str()
);
}
+60 -50
View File
@@ -3,74 +3,84 @@
void command_movement(Client *c, const Seperator *sep)
{
auto &mgr = MobMovementManager::Get();
if (sep->arg[1][0] == 0) {
c->Message(Chat::White, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop/packet");
const int arguments = sep->argnum;
if (!arguments) {
c->Message(Chat::White, "Usage: #movement [clear|packet|rotate|run|stats|stop|walk]");
return;
}
if (strcasecmp(sep->arg[1], "stats") == 0) {
mgr.DumpStats(c);
const bool is_clear = !strcasecmp(sep->arg[1], "clear");
const bool is_packet = !strcasecmp(sep->arg[1], "packet");
const bool is_rotate = !strcasecmp(sep->arg[1], "rotate");
const bool is_run = !strcasecmp(sep->arg[1], "run");
const bool is_stats = !strcasecmp(sep->arg[1], "stats");
const bool is_stop = !strcasecmp(sep->arg[1], "stop");
const bool is_walk = !strcasecmp(sep->arg[1], "walk");
if (
!is_clear &&
!is_packet &&
!is_rotate &&
!is_run &&
!is_stats &&
!is_stop &&
!is_walk
) {
c->Message(Chat::White, "Usage: #movement [clear|packet|rotate|run|stats|stop|walk]");
return;
}
else if (strcasecmp(sep->arg[1], "clearstats") == 0) {
mgr.ClearStats();
}
else if (strcasecmp(sep->arg[1], "walkto") == 0) {
auto target = c->GetTarget();
if (target == nullptr) {
c->Message(Chat::White, "No target found.");
auto &m = MobMovementManager::Get();
if (is_clear) {
m.ClearStats();
} else if (is_packet) {
Mob *t = c->GetTarget();
if (!t) {
c->Message(Chat::White, "You must have a target to use this command.");
return;
}
target->WalkTo(c->GetX(), c->GetY(), c->GetZ());
}
else if (strcasecmp(sep->arg[1], "runto") == 0) {
auto target = c->GetTarget();
if (target == nullptr) {
c->Message(Chat::White, "No target found.");
const float x = sep->IsNumber(2) ? Strings::ToFloat(sep->arg[2]) : 0.0f;
const float y = sep->IsNumber(3) ? Strings::ToFloat(sep->arg[3]) : 0.0f;
const float z = sep->IsNumber(4) ? Strings::ToFloat(sep->arg[4]) : 0.0f;
const float heading = sep->IsNumber(5) ? Strings::ToFloat(sep->arg[5]) : 0.0f;
const int animation = sep->IsNumber(6) ? Strings::ToInt(sep->arg[6]) : 0.0f;
m.SendCommandToClients(t, x, y, z, heading, animation, ClientRangeAny);
} else if (is_rotate) {
Mob *t = c->GetTarget();
if (!t) {
c->Message(Chat::White, "You must have a target to use this command.");
return;
}
target->RunTo(c->GetX(), c->GetY(), c->GetZ());
}
else if (strcasecmp(sep->arg[1], "rotateto") == 0) {
auto target = c->GetTarget();
if (target == nullptr) {
c->Message(Chat::White, "No target found.");
t->RotateToWalking(t->CalculateHeadingToTarget(c->GetX(), c->GetY()));
} else if (is_run) {
Mob *t = c->GetTarget();
if (!t) {
c->Message(Chat::White, "You must have a target to use this command.");
return;
}
target->RotateToWalking(target->CalculateHeadingToTarget(c->GetX(), c->GetY()));
}
else if (strcasecmp(sep->arg[1], "stop") == 0) {
auto target = c->GetTarget();
if (target == nullptr) {
c->Message(Chat::White, "No target found.");
t->RunTo(c->GetX(), c->GetY(), c->GetZ());
} else if (is_stats) {
m.DumpStats(c);
} else if (is_stop) {
Mob *t = c->GetTarget();
if (!t) {
c->Message(Chat::White, "You must have a target to use this command.");
return;
}
target->StopNavigation();
}
else if (strcasecmp(sep->arg[1], "packet") == 0) {
auto target = c->GetTarget();
if (target == nullptr) {
c->Message(Chat::White, "No target found.");
t->StopNavigation();
} else if (is_walk) {
Mob *t = c->GetTarget();
if (!t) {
c->Message(Chat::White, "You must have a target to use this command.");
return;
}
mgr.SendCommandToClients(
target,
Strings::ToFloat(sep->arg[2]),
Strings::ToFloat(sep->arg[3]),
Strings::ToFloat(sep->arg[4]),
Strings::ToFloat(sep->arg[5]),
Strings::ToInt(sep->arg[6]),
ClientRangeAny
);
}
else {
c->Message(Chat::White, "Usage: #movement stats/clearstats/walkto/runto/rotateto/stop/packet");
t->WalkTo(c->GetX(), c->GetY(), c->GetZ());
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+15
View File
@@ -0,0 +1,15 @@
#ifndef EQEMU_OBJECT_MANIPULATION_H
#define EQEMU_OBJECT_MANIPULATION_H
#include "../client.h"
class ObjectManipulation {
public:
static void CommandHandler(Client *c, const Seperator *sep);
static void CommandHeader(Client *c);
static void SendSubcommands(Client *c);
};
#endif //EQEMU_OBJECT_MANIPULATION_H
+3 -3
View File
@@ -5,10 +5,10 @@ void command_petname(Client *c, const Seperator *sep)
Mob *target;
target = c->GetTarget();
if (!target) {
c->Message(Chat::White, "Usage: #petname newname (requires a target)");
if (!target || !target->IsPet()) {
c->Message(Chat::White, "Usage: #petname newname (requires a pet target)");
}
else if (target->IsPet() && (target->GetOwnerID() == c->GetID()) && strlen(sep->arg[1]) > 0) {
else if (target->GetOwnerID() == c->GetID() && strlen(sep->arg[1]) > 0) {
char *oldname = strdup(target->GetName());
target->TempName(sep->arg[1]);
c->Message(Chat::White, "Renamed %s to %s", oldname, sep->arg[1]);
+1 -1
View File
@@ -46,7 +46,7 @@ void command_scribespell(Client *c, const Seperator *sep)
);
if (
spells[spell_id].classes[WARRIOR] != 0 &&
spells[spell_id].classes[Class::Warrior] != 0 &&
spells[spell_id].skill != EQ::skills::SkillTigerClaw &&
spells[spell_id].classes[t->GetPP().class_ - 1] > 0 &&
!IsDiscipline(spell_id)
+9 -16
View File
@@ -9,17 +9,11 @@ void SetTime(Client *c, const Seperator *sep)
TimeOfDay_Struct world_time{};
zone->zone_time.GetCurrentEQTimeOfDay(time(0), &world_time);
auto time_string = fmt::format(
"{} (Timezone: {})",
Strings::ZoneTime(world_time.hour, world_time.minute),
Strings::ZoneTime(zone->zone_time.getEQTimeZoneHr(), zone->zone_time.getEQTimeZoneHr())
);
c->Message(
Chat::White,
fmt::format(
"It is currently {}.",
time_string
Strings::ZoneTime(world_time.hour - 1, world_time.minute)
).c_str()
);
@@ -27,15 +21,15 @@ void SetTime(Client *c, const Seperator *sep)
}
uint8 minutes = 0;
uint8 hours = Strings::ToUnsignedInt(sep->arg[2]) + 1;
uint8 hours = Strings::ToUnsignedInt(sep->arg[2]);
if (hours > 24) {
hours = 24;
}
uint8 real_hours = (
(hours - 1) > 0 ?
(hours - 1) :
hours > 0 ?
hours :
0
);
@@ -47,21 +41,20 @@ void SetTime(Client *c, const Seperator *sep)
}
}
c->Message(
Chat::White,
fmt::format(
"Setting world time to {} (Timezone: {}).",
Strings::ZoneTime(hours, minutes),
Strings::ZoneTime(zone->zone_time.getEQTimeZoneHr(), zone->zone_time.getEQTimeZoneHr())
"Setting world time to {}.",
Strings::ZoneTime(hours, minutes)
).c_str()
);
zone->SetTime(real_hours, minutes);
LogInfo(
"{} :: Setting world time to {} (Timezone: {})",
"{} :: Setting world time to {}.",
c->GetCleanName(),
Strings::ZoneTime(hours, minutes),
Strings::ZoneTime(zone->zone_time.getEQTimeZoneHr(), zone->zone_time.getEQTimeZoneHr())
Strings::ZoneTime(hours, minutes)
);
}
+2
View File
@@ -1,4 +1,5 @@
#include "../client.h"
#include "show/aas.cpp"
#include "show/aa_points.cpp"
#include "show/aggro.cpp"
#include "show/buffs.cpp"
@@ -56,6 +57,7 @@ void command_show(Client *c, const Seperator *sep)
};
std::vector<Cmd> commands = {
Cmd{.cmd = "aas", .u = "aas", .fn = ShowAAs, .a = {"#showaas"}},
Cmd{.cmd = "aa_points", .u = "aa_points", .fn = ShowAAPoints, .a = {"#showaapoints", "#showaapts"}},
Cmd{.cmd = "aggro", .u = "aggro [Distance] [-v] (-v is verbose Faction Information)", .fn = ShowAggro, .a = {"#aggro"}},
Cmd{.cmd = "buffs", .u = "buffs", .fn = ShowBuffs, .a = {"#showbuffs"}},
+13
View File
@@ -0,0 +1,13 @@
#include "../../client.h"
void ShowAAs(Client *c, const Seperator *sep)
{
Client *t = c;
if (c->GetTarget() && c->GetTarget()->IsClient()) {
t = c->GetTarget()->CastToClient();
}
const std::string& search_criteria = sep->argnum >= 2 ? sep->argplus[2] : std::string();
t->ListPurchasedAAs(c, search_criteria);
}
+2 -7
View File
@@ -2,13 +2,8 @@
void ShowFlags(Client *c, const Seperator *sep)
{
auto t = c;
if (
c->GetTarget() &&
c->GetTarget()->IsClient() &&
c->Admin() >= minStatusToSeeOthersZoneFlags
) {
Client *t = c;
if (c->GetTarget() && c->GetTarget()->IsClient()) {
t = c->GetTarget()->CastToClient();
}
+2 -6
View File
@@ -2,12 +2,8 @@
void ShowPEQZoneFlags(Client *c, const Seperator *sep)
{
auto t = c;
if (
c->GetTarget() &&
c->GetTarget()->IsClient() &&
c->Admin() >= minStatusToSeeOthersZoneFlags
) {
Client *t = c;
if (c->GetTarget() && c->GetTarget()->IsClient()) {
t = c->GetTarget()->CastToClient();
}
+14 -19
View File
@@ -3,19 +3,16 @@
extern WorldServer worldserver;
#include "../corpse.h"
void command_summon(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
const int arguments = sep->argnum;
if (!arguments && !c->GetTarget()) {
c->Message(Chat::White, "Usage: #summon - Summon your target, if you have one, to your position");
c->Message(Chat::White, "Usage: #summon [Character Name] - Summon a character by name to your position");
c->Message(Chat::White, "Note: You may also summon your target if you have one.");
return;
}
Mob* t = c;
Mob *t = c;
if (arguments == 1) {
std::string character_name = sep->arg[1];
@@ -31,9 +28,9 @@ void command_summon(Client *c, const Seperator *sep)
return;
}
auto search_client = entity_list.GetClientByName(character_name.c_str());
if (search_client) {
t = search_client->CastToMob();
Client *s = entity_list.GetClientByName(character_name.c_str());
if (s) {
t = s->CastToMob();
} else {
if (!worldserver.Connected()) {
c->Message(Chat::White, "World server is currently disconnected.");
@@ -42,15 +39,18 @@ void command_summon(Client *c, const Seperator *sep)
auto pack = new ServerPacket(ServerOP_ZonePlayer, sizeof(ServerZonePlayer_Struct));
auto szp = (ServerZonePlayer_Struct *) pack->pBuffer;
strn0cpy(szp->adminname, c->GetName(), sizeof(szp->adminname));
szp->adminrank = c->Admin();
szp->ignorerestrictions = 2;
strn0cpy(szp->name, character_name.c_str(), sizeof(szp->name));
strn0cpy(szp->zone, zone->GetShortName(), sizeof(szp->zone));
szp->x_pos = c->GetX();
szp->y_pos = c->GetY();
szp->z_pos = c->GetZ();
szp->instance_id = zone->GetInstanceID();
szp->adminrank = c->Admin();
szp->ignorerestrictions = 2;
szp->instance_id = zone->GetInstanceID();
szp->x_pos = c->GetX();
szp->y_pos = c->GetY();
szp->z_pos = c->GetZ();
worldserver.SendPacket(pack);
safe_delete(pack);
return;
@@ -97,9 +97,4 @@ void command_summon(Client *c, const Seperator *sep)
}
t->GMMove(c->GetPosition());
if (t->IsNPC()) {
t->CastToNPC()->SaveGuardSpot(glm::vec4(0.0f));
}
}
+113 -63
View File
@@ -8,7 +8,7 @@ extern WorldServer worldserver;
void command_task(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
const int arguments = sep->argnum;
if (!arguments) {
c->Message(Chat::White, "Syntax: #task [subcommand]");
c->Message(Chat::White, "------------------------------------------------");
@@ -23,6 +23,7 @@ void command_task(Client *c, const Seperator *sep)
);
c->Message(Chat::White, "--- update <task_id> <activity_id> [count] | Updates task");
c->Message(Chat::White, "--- assign <task_id> | Assigns task to client");
c->Message(Chat::White, "--- complete <task_id> | Completes a task if a client has it assigned to them");
c->Message(Chat::White, "--- uncomplete <task_id> | Uncompletes a task if a client has completed it");
c->Message(
Chat::White,
@@ -73,22 +74,24 @@ void command_task(Client *c, const Seperator *sep)
return;
}
Client *target = c;
Client *t = c;
if (c->GetTarget() && c->GetTarget()->IsClient()) {
target = c->GetTarget()->CastToClient();
t = c->GetTarget()->CastToClient();
}
bool is_assign = !strcasecmp(sep->arg[1], "assign");
bool is_purgetimers = !strcasecmp(sep->arg[1], "purgetimers");
bool is_reload = !strcasecmp(sep->arg[1], "reload");
bool is_reloadall = !strcasecmp(sep->arg[1], "reloadall");
bool is_sharedpurge = !strcasecmp(sep->arg[1], "sharedpurge");
bool is_show = !strcasecmp(sep->arg[1], "show");
bool is_uncomplete = !strcasecmp(sep->arg[1], "uncomplete");
bool is_update = !strcasecmp(sep->arg[1], "update");
const bool is_assign = !strcasecmp(sep->arg[1], "assign");
const bool is_complete = !strcasecmp(sep->arg[1], "complete");
const bool is_purgetimers = !strcasecmp(sep->arg[1], "purgetimers");
const bool is_reload = !strcasecmp(sep->arg[1], "reload");
const bool is_reloadall = !strcasecmp(sep->arg[1], "reloadall");
const bool is_sharedpurge = !strcasecmp(sep->arg[1], "sharedpurge");
const bool is_show = !strcasecmp(sep->arg[1], "show");
const bool is_uncomplete = !strcasecmp(sep->arg[1], "uncomplete");
const bool is_update = !strcasecmp(sep->arg[1], "update");
if (
!is_assign &&
!is_complete &&
!is_purgetimers &&
!is_reload &&
!is_reloadall &&
@@ -161,70 +164,117 @@ void command_task(Client *c, const Seperator *sep)
}
if (is_assign) {
auto task_id = std::strtoul(sep->arg[2], nullptr, 10);
const uint32 task_id = Strings::ToUnsignedInt(sep->arg[2]);
if (task_id) {
target->AssignTask(task_id, 0, false);
t->AssignTask(task_id, 0, false);
c->Message(
Chat::Yellow,
Chat::White,
fmt::format(
"Assigned task ID {} to {}.",
"Assigned {} (ID {}) to {}.",
task_manager->GetTaskName(task_id),
task_id,
c->GetTargetDescription(target)
c->GetTargetDescription(t)
).c_str()
);
}
return;
} else if (is_complete) {
if (sep->IsNumber(2)) {
const uint32 task_id = Strings::ToUnsignedInt(sep->arg[2]);
if (!task_id) {
c->Message(Chat::White, "Invalid task ID specified.");
return;
}
if (t->IsTaskActive(task_id)) {
const bool did_complete = t->CompleteTask(task_id);
if (did_complete) {
c->Message(
Chat::White,
fmt::format(
"Successfully completed {} (ID {}) for {}.",
task_manager->GetTaskName(task_id),
task_id,
c->GetTargetDescription(t)
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"Failed to complete {} (ID {}) for {}.",
task_manager->GetTaskName(task_id),
task_id,
c->GetTargetDescription(t)
).c_str()
);
}
} else {
c->Message(
Chat::White,
fmt::format(
"{} {} not have not {} (ID {}) assigned to them.",
c->GetTargetDescription(t, TargetDescriptionType::UCYou),
c == t ? "do" : "does",
task_manager->GetTaskName(task_id),
task_id
).c_str()
);
}
}
} else if (is_purgetimers) {
c->Message(
Chat::Yellow,
Chat::White,
fmt::format(
"Task timers have been purged for {}.",
c->GetTargetDescription(target)
c->GetTargetDescription(t)
).c_str()
);
if (c != target) {
target->Message(Chat::Yellow, "Your task timers have been purged by a GM.");
if (c != t) {
t->Message(Chat::White, "Your task timers have been purged by a GM.");
}
target->PurgeTaskTimers();
return;
t->PurgeTaskTimers();
} else if (is_reload) {
if (arguments >= 2) {
if (!strcasecmp(sep->arg[2], "sets")) {
c->Message(Chat::Yellow, "Attempting to reload task sets.");
const bool is_sets = !strcasecmp(sep->arg[2], "sets");
const bool is_task = !strcasecmp(sep->arg[2], "task");
if (is_sets) {
c->Message(Chat::White, "Attempting to reload task sets.");
worldserver.SendReloadTasks(RELOADTASKSETS);
c->Message(Chat::Yellow, "Successfully reloaded task sets.");
return;
} else if (!strcasecmp(sep->arg[2], "task") && arguments == 3) {
int task_id = std::strtoul(sep->arg[3], nullptr, 10);
c->Message(Chat::White, "Successfully reloaded task sets.");
} else if (is_task && arguments == 3 && sep->IsNumber(3)) {
const uint32 task_id = Strings::ToUnsignedInt(sep->arg[3]);
if (task_id) {
c->Message(
Chat::Yellow,
Chat::White,
fmt::format(
"Attempted to reload task ID {}.",
"Attempting to reload {} (ID {}).",
task_manager->GetTaskName(task_id),
task_id
).c_str()
);
worldserver.SendReloadTasks(RELOADTASKS, task_id);
c->Message(
Chat::Yellow,
Chat::White,
fmt::format(
"Successfully reloaded task ID {}.",
"Successfully reloaded {} (ID {}).",
task_manager->GetTaskName(task_id),
task_id
).c_str()
);
return;
}
}
}
} else if (is_reloadall) {
c->Message(Chat::Yellow, "Attempting to reload tasks.");
c->Message(Chat::White, "Attempting to reload tasks.");
worldserver.SendReloadTasks(RELOADTASKS);
c->Message(Chat::Yellow, "Successfully reloaded tasks.");
return;
c->Message(Chat::White, "Successfully reloaded tasks.");
} else if (is_sharedpurge) {
if (!strcasecmp(sep->arg[2], "confirm")) {
const bool is_confirm = !strcasecmp(sep->arg[2], "confirm");
if (is_confirm) {
LogTasksDetail("Sending purge request");
auto pack = new ServerPacket(ServerOP_SharedTaskPurgeAllCommand, 0);
worldserver.SendPacket(pack);
@@ -239,14 +289,11 @@ void command_task(Client *c, const Seperator *sep)
Saylink::Silent("#task sharedpurge confirm", "confirm")
).c_str()
);
return;
} else if (is_show) {
target->ShowClientTasks(c);
return;
t->ShowClientTasks(c);
} else if (is_uncomplete) {
if (sep->IsNumber(2)) {
auto task_id = Strings::ToUnsignedInt(sep->arg[2]);
const uint32 task_id = Strings::ToUnsignedInt(sep->arg[2]);
if (!task_id) {
c->Message(Chat::White, "Invalid task ID specified.");
return;
@@ -257,57 +304,60 @@ void command_task(Client *c, const Seperator *sep)
database,
fmt::format(
"charid = {} AND taskid = {}",
target->CharacterID(),
task_id
t->CharacterID(),
task_id
)
)
) {
c->Message(
Chat::Yellow,
Chat::White,
fmt::format(
"Successfully uncompleted task ID {} for {}.",
"Successfully uncompleted {} (ID {}) for {}.",
task_manager->GetTaskName(task_id),
task_id,
c->GetTargetDescription(target)
c->GetTargetDescription(t)
).c_str()
);
} else {
c->Message(
Chat::Yellow,
Chat::White,
fmt::format(
"{} {} not completed task ID {}.",
c->GetTargetDescription(target, TargetDescriptionType::UCYou),
c == target ? "have" : "has",
"{} {} not completed {} (ID {}).",
c->GetTargetDescription(t, TargetDescriptionType::UCYou),
c == t ? "have" : "has",
task_manager->GetTaskName(task_id),
task_id
).c_str()
);
}
}
} else if (is_update) {
if (arguments >= 3) {
auto task_id = std::strtoul(sep->arg[2], nullptr, 10);
auto activity_id = std::strtoul(sep->arg[3], nullptr, 10);
int count = 1;
if (arguments >= 3 && sep->IsNumber(2) && sep->IsNumber(3)) {
const uint32 task_id = Strings::ToUnsignedInt(sep->arg[2]);
const uint32 activity_id = Strings::ToUnsignedInt(sep->arg[3]);
int count = 1;
if (arguments >= 4) {
count = std::strtol(sep->arg[4], nullptr, 10);
if (arguments == 4 && sep->IsNumber(4)) {
count = Strings::ToInt(sep->arg[4]);
if (count <= 0) {
count = 1;
}
}
c->Message(
Chat::Yellow,
Chat::White,
fmt::format(
"Updating task ID {}, activity {} with a count of {} for {}.",
"Updating {} (ID {}), activity {} with a count of {} for {}.",
task_manager->GetTaskName(task_id),
task_id,
activity_id,
count,
c->GetTargetDescription(target)
c->GetTargetDescription(t)
).c_str()
);
target->UpdateTaskActivity(task_id, activity_id, count);
target->ShowClientTasks(c);
t->UpdateTaskActivity(task_id, activity_id, count);
t->ShowClientTasks(c);
}
}
}
+38 -16
View File
@@ -5,21 +5,43 @@ extern WorldServer worldserver;
void command_zonebootup(Client *c, const Seperator *sep)
{
if (!worldserver.Connected()) {
c->Message(Chat::White, "Error: World server disconnected");
const int arguments = sep->argnum;
if (arguments < 2 || !sep->IsNumber(1)) {
c->Message(Chat::White, "Usage: #zonebootup [Zone Server ID] [Zone ID|Zone Short Name] [Instance ID]");
return;
}
else if (sep->arg[2][0] == 0) {
c->Message(Chat::White, "Usage: #zonebootup ZoneServerID# zoneshortname");
}
else {
auto pack = new ServerPacket(ServerOP_ZoneBootup, sizeof(ServerZoneStateChange_struct));
ServerZoneStateChange_struct *s = (ServerZoneStateChange_struct *) pack->pBuffer;
s->ZoneServerID = Strings::ToInt(sep->arg[1]);
strcpy(s->adminname, c->GetName());
s->zoneid = ZoneID(sep->arg[2]);
s->makestatic = (bool) (strcasecmp(sep->arg[3], "static") == 0);
worldserver.SendPacket(pack);
safe_delete(pack);
}
}
if (!worldserver.Connected()) {
c->Message(Chat::White, "World server disconnected.");
return;
}
const uint32 zone_server_id = Strings::ToUnsignedInt(sep->arg[1]);
const uint32 zone_id = Strings::IsNumber(sep->arg[2]) ? Strings::ToUnsignedInt(sep->arg[2]) : ZoneID(sep->arg[2]);
const bool is_static = arguments > 2 ? !strcasecmp(sep->arg[3], "static") : false;
const uint16 instance_id = arguments > 3 ? static_cast<uint16>(Strings::ToUnsignedInt(sep->arg[4])) : 0;
if (!zone_id) {
c->Message(
Chat::White,
fmt::format(
"Zone '{}' does not exist.",
sep->arg[1]
).c_str()
);
return;
}
auto pack = new ServerPacket(ServerOP_ZoneBootup, sizeof(ServerZoneStateChange_Struct));
auto *s = (ServerZoneStateChange_Struct *) pack->pBuffer;
s->zone_server_id = zone_server_id;
s->zone_id = zone_id;
s->instance_id = instance_id;
s->is_static = is_static;
strn0cpy(s->admin_name, c->GetName(), sizeof(s->admin_name));
worldserver.SendPacket(pack);
safe_delete(pack);
}
+32 -21
View File
@@ -5,26 +5,37 @@ extern WorldServer worldserver;
void command_zoneshutdown(Client *c, const Seperator *sep)
{
if (!worldserver.Connected()) {
c->Message(Chat::White, "Error: World server disconnected");
const int arguments = sep->argnum;
if (!arguments) {
c->Message(Chat::White, "Usage: #zoneshutdown [Zone ID|Zone Short Name]");
return;
}
else if (sep->arg[1][0] == 0) {
c->Message(Chat::White, "Usage: #zoneshutdown zoneshortname");
}
else {
auto pack = new ServerPacket(
ServerOP_ZoneShutdown,
sizeof(ServerZoneStateChange_struct));
ServerZoneStateChange_struct *s = (ServerZoneStateChange_struct *) pack->pBuffer;
strcpy(s->adminname, c->GetName());
if (sep->arg[1][0] >= '0' && sep->arg[1][0] <= '9') {
s->ZoneServerID = Strings::ToInt(sep->arg[1]);
}
else {
s->zoneid = ZoneID(sep->arg[1]);
}
worldserver.SendPacket(pack);
safe_delete(pack);
}
}
if (!worldserver.Connected()) {
c->Message(Chat::White, "World server disconnected.");
return;
}
const uint32 zone_id = sep->IsNumber(1) ? Strings::ToUnsignedInt(sep->arg[1]) : ZoneID(sep->arg[1]);
if (!zone_id) {
c->Message(
Chat::White,
fmt::format(
"Zone '{}' does not exist.",
sep->arg[1]
).c_str()
);
return;
}
auto pack = new ServerPacket(ServerOP_ZoneShutdown, sizeof(ServerZoneStateChange_Struct));
auto *s = (ServerZoneStateChange_Struct *) pack->pBuffer;
s->zone_id = zone_id;
strn0cpy(s->admin_name, c->GetName(), sizeof(s->admin_name));
worldserver.SendPacket(pack);
safe_delete(pack);
}
+6
View File
@@ -2510,3 +2510,9 @@ bool Group::IsLeader(const char* name) {
return false;
}
std::string Group::GetGroupLeaderName(uint32 group_id) {
char leader_name_buffer[64] = { 0 };
database.GetGroupLeadershipInfo(group_id, leader_name_buffer);
return std::string(leader_name_buffer);
}
+3 -1
View File
@@ -76,7 +76,7 @@ public:
void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = nullptr);
inline void SetLeader(Mob* c){ leader = c; };
inline Mob* GetLeader() { return leader; };
const char* GetLeaderName() { return membername[0]; };
const char* GetLeaderName() { return GetGroupLeaderName(GetID()).c_str(); };
void SendHPManaEndPacketsTo(Mob* newmember);
void SendHPPacketsFrom(Mob* member);
void SendManaPacketFrom(Mob* member);
@@ -177,6 +177,8 @@ private:
int mentor_percent;
XTargetAutoHaters m_autohatermgr;
std::string GetGroupLeaderName(uint32 group_id);
};
#endif
+5 -5
View File
@@ -600,7 +600,7 @@ bool HealRotation::healable_target(bool use_class_at, bool critical_only)
return false;
if (healable_target->GetHPRatio() > m_safe_hp_ratio[ClassArmorType(healable_target->GetClass())])
return false;
if (healable_target->IsBerserk() && (healable_target->GetClass() == WARRIOR || healable_target->GetClass() == BERSERKER)) {
if (healable_target->IsBerserk() && (healable_target->GetClass() == Class::Warrior || healable_target->GetClass() == Class::Berserker)) {
if (healable_target->GetHPRatio() <= RuleI(Combat, BerserkerFrenzyEnd) && healable_target->GetHPRatio() > m_critical_hp_ratio[ClassArmorType(healable_target->GetClass())])
return false;
}
@@ -610,7 +610,7 @@ bool HealRotation::healable_target(bool use_class_at, bool critical_only)
return false;
if (healable_target->GetHPRatio() > SAFE_HP_RATIO_BASE)
return false;
if (healable_target->IsBerserk() && (healable_target->GetClass() == WARRIOR || healable_target->GetClass() == BERSERKER)) {
if (healable_target->IsBerserk() && (healable_target->GetClass() == Class::Warrior || healable_target->GetClass() == Class::Berserker)) {
if (healable_target->GetHPRatio() <= RuleI(Combat, BerserkerFrenzyEnd) && healable_target->GetHPRatio() > CRITICAL_HP_RATIO_BASE)
return false;
}
@@ -938,9 +938,9 @@ void HealRotation::validate_hot()
bool IsHealRotationMemberClass(uint8 class_id)
{
switch (class_id) {
case CLERIC:
case DRUID:
case SHAMAN:
case Class::Cleric:
case Class::Druid:
case Class::Shaman:
return true;
default:
return false;

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