Compare commits

...

68 Commits

Author SHA1 Message Date
regneq caae34ac5e fixed client lua summonitem.
export npc:CanTalk to lua.
added QuestSlot.
Added a condition for questslot in summonitem function.
2024-04-05 18:01:48 -07:00
regneq 56c7db4cbf [MultiQuest] Add functions to Loot. Import to Lua_NPC. 2024-04-05 12:08:01 -07:00
KayenEQ 043eeced6f [Bug Fix] Client not updating HP bar when an HP Buff with a Heal is applied. (#4237)
* [Bug Fix] HP Bar not updating when applying HP Buff with a heal.

Bug: When an HP buff with a heal effect is applied for first time, the heal portion of the effect heals the client and updates HPs currently server side, but client side the HP bar does not register it as a heal thus you display as less than full HP. However due to server thinking your healed, you are unable to correct it by healing.

Solution: You need to resend the HP update after buff completed and action packet resent.

* add SE_MaxHPChange to fix

would result in same bug
2024-04-02 01:13:29 -05:00
KayenEQ e9a0c79301 [Bug Fix] SPA214 SE_MaxHPChange calculation errors corrected. (#4238)
* [Bug Fix] HP Bar not updating when applying HP Buff with a heal.

Bug: When an HP buff with a heal effect is applied for first time, the heal portion of the effect heals the client and updates HPs currently server side, but client side the HP bar does not register it as a heal thus you display as less than full HP. However due to server thinking your healed, you are unable to correct it by healing.

Solution: You need to resend the HP update after buff completed and action packet resent.

* add SE_MaxHPChange to fix

would result in same bug

* [Bug Fix] SPA214 Percent HP change calculation fix

Fix how spell and item bonuses using SPA 214 are calculated. Will now be calculated consistent with client.

* [Bug Fix] SPA214 SE_MaxHPChange calculation errors corrected.

removed code from other PR
2024-04-02 01:12:55 -05:00
Alex King dc48c45421 [Bug Fix] Fix Bot Creation Issue (#4235)
# Notes
- Creating bots was failing because were checking for `false` on `Database::CheckUsedName()` in `BotDatabase::QueryNameAvailability`.
- `Database::CheckUsedName()` is now `Database::IsNameUsed()` and checks for both bots and character name usages.
- We were checking for `false` which was always happening when there were no entries for the supplied name, meaning we were never allowed to create a bot.
2024-04-02 01:12:08 -05:00
Chris Miles d7a8fb8691 [Fix] Fix manifest for skill caps schema type (#4231) 2024-04-02 01:08:19 -05:00
Chris Miles 3a5381d38a [Crash] Check mob pointer before trying to remove it (#4230) 2024-04-01 18:09:57 -04:00
Alex King e64f03fcc0 [Bug Fix] Fix Lua Crash with Spell Blocked Event (#4236) 2024-04-01 18:06:39 -04:00
Alex King d77966797e [Quest API] Add Spell Blocked Event to Perl/Lua (#4217)
* [Spells] Add Unblockable Spell Table and Spell Blocked Event

- Add `EVENT_SPELL_BLOCKED`, exports `$blocking_spell_id`, `$cast_spell_id`, `$blocking_spell`, and `$cast_spell`.

- Add `event_spell_blocked`, exports `e.blocking_spell_id`, `e.cast_spell_id`, `e.blocking_spell`, and `e.cast_spell`.

- Adds `spells_unblockable` table with a `spell_id` and `is_unblockable` column.
- This table will need to be populated based on known spells that should be unblockable.

- This event will allow operators to perform events when spells are blocked.

* Cleanup

* Cleanup

* Update spells.cpp

* Remove unused repositories.

* Finalize

* Update lua_parser_events.cpp
2024-03-31 22:58:30 -05:00
Akkadius 048aad437b Update pull_request_template.md 2024-03-31 22:55:27 -05:00
hg b2d9de8d96 [Quests] Avoid Player and Bot quests in unloaded zone (#4232)
If a mob has a target when a zone is shutdown it will crash while trying
to dispatch EVENT_TARGET_CHANGE when the Mob destructor cleans up hatelists
if a quest interface isn't loaded for the type (in this case no bot scripts):

 	zone.exe!fmt::v10::format<std::string const &,char const *>(fmt::v10::basic_format_string<char,std::string const &,char const *> fmt={...}, const std::string & <args_0>={...}, const char * && <args_1>=0x0000000000000000) Line 2835	C++
>	zone.exe!QuestParserCollection::GetQIByBotQuest(std::string & filename={...}) Line 1138	C++
 	zone.exe!QuestParserCollection::BotHasQuestSubLocal(QuestEventID event_id=EVENT_TARGET_CHANGE) Line 353	C++
 	zone.exe!QuestParserCollection::BotHasQuestSub(QuestEventID event_id=EVENT_TARGET_CHANGE) Line 389	C++
 	zone.exe!Mob::SetTarget(Mob * mob=0x0000000000000000) Line 5431	C++
 	zone.exe!NPC::SetTarget(Mob * mob=0x0000000000000000) Line 575	C++
 	zone.exe!Mob::RemoveFromHateList(Mob * mob=0x000001bfbdc66040) Line 4894	C++
 	zone.exe!EntityList::RemoveFromTargets(Mob * mob=0x000001bfbdc66040, bool RemoveFromXTargets=true) Line 1530	C++
 	zone.exe!Mob::~Mob() Line 547	C++
 	zone.exe!NPC::~NPC() Line 537	C++
 	zone.exe!NPC::`scalar deleting destructor'(unsigned int)	C++
 	zone.exe!EntityList::RemoveAllMobs() Line 2678	C++
 	zone.exe!EntityList::Clear() Line 3090	C++
 	zone.exe!Zone::~Zone() Line 1103	C++
 	zone.exe!Zone::`scalar deleting destructor'(unsigned int)	C++
 	zone.exe!Zone::Shutdown(bool quiet=false) Line 928	C++

This is caused by the Zone destructor deleting short_name before calling
entity_list.Clear(). With an unloaded quest interface BotHasQuestSubLocal
calls GetQIByBotQuest which gets a null zone->GetShortName() and crashes
while formatting strings.

The immediate regressing commit for this crash is because a check for
zone->IsLoaded() was removed in 74f1eac4 with others that were removed
to fix a regression by #4025. GetQIByBotQuest and GetQIByPlayerQuest
always had this check and should have remained for them.

This restores the zone->IsLoaded() checks for GetQIByBotQuest/PlayerQuest.
The other functions cannot have that check added until the other issues
mentioned in #4149 are addressed.
2024-03-31 22:49:13 -05:00
Fryguy 42bfa4bb2e [Bug Fix] Shared Tasks - charid is now character_id (#4233)
## Type of change

Please delete options that are not relevant.

- [x] Bug fix (non-breaking change which fixes an issue)

# Checklist:

- [x] I have performed a self-review of my code. Ensuring variables, functions and methods are named in a human-readable way, comments are added only where naming of variables, functions and methods can't give enough context.
- [x] I have tested my changes
- [x] I own the changes of my code take responsibilities for the potential issues that occur
2024-03-31 22:41:15 -05:00
Chris Miles 72ce7c8e91 Update pull_request_template.md 2024-03-31 15:52:54 -05:00
Chris Miles e306c86875 Create pull_request_template.md 2024-03-31 15:48:33 -05:00
Alex King 3ae7979a67 [Bug Fix] Fix Issue With Bot Raid Aggro (#4222)
# Notes
- Bots were reportedly being bypassed by NPCs due to not taking into consideration their raid group's damage.
- Must have been missed when bot raids were implemented.
2024-03-31 11:56:12 -05:00
Xackery b638795f9b [Feature] Add LuaMod functions for CommonDamage and HealDamage (#4227)
* Add LuaMod functions for CommonDamage and HealDamage

* Remove extra bracket
2024-03-31 11:30:16 -04:00
KayenEQ 9e3bf91374 [Spells] Implemented SPA 463 SE_SHIELD_TARGET (#4224)
SPA 463 SE_SHIELD_TARGET

Live description: "Shields your target, taking a percentage of their damage".

Only example spell on live is an NPC who uses it during a raid event "Laurion's Song" expansion. SPA 54492 'Guardian Stance' Described as 100% Melee Shielding

Example of mechanic. Base value = 70. Caster puts buff on target. Each melee hit Buff Target takes 70% less damage, Buff Caster receives 30% of the melee damage.
Added mechanic to cause buff to fade if target or caster are separated by a distance greater than the casting range of the spell. This allows similar mechanics to the /shield ability, without a range removal mechanic it would be too easy to abuse if put on a player spell. *can not confirm live does this currently

Can not be cast on self.
2024-03-30 13:34:03 -04:00
KayenEQ cd89926435 [Feature] Additive Spell Focus from Worn slot with Limit Checks (#4208)
* [Feature] Additive Spell Focus from Worn slot with limits

New rule (UseAdditiveFocusFromWornSlotWithLimits) allows you place to focus effects in worn slots which will apply the focus additively and perform normal limit checks on those focus. This differs from regular focus behavior that only takes highest value.

This is a new version of an old rule "UseAdditiveFocusFromWornSlot"
which allowed similar behavior but ignored focus limits. Thus hindering its full potential for itemization.

* Update spell_effects.cpp

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2024-03-30 13:23:02 -04:00
Xackery e19f72f021 [Feature] Add RegisterBug LuaMod (#4209)
* Add RegisterBug LuaMod

* Add missing header

* Add missing header to lua_mod

* Fix RegisterBug ignore_default

* Fix ignore_default

* Fix formatting
2024-03-30 11:45:37 -04:00
JJ df1dc5d1e4 [Cleanup] Avoid unnecessary copies in database functions (#4220)
Since `auto` doesn't deduce references, these will most likely create copies having unintended results.
2024-03-29 07:45:41 -04:00
Alex King e11286164f [Bug Fix] Fix Luabind Double Class Register (#4219)
* [Bug Fix] Fix Luabind Double Class Register

# Notes
- These two methods were registering to the same class as another method, causing an error seen by @neckkola and others.

# Error
```
zone: /home/eqemu/source_jas/libs/luabind/src/class_registry.cpp:151: void luabind::detail::class_registry::add_class(const luabind::type_id&, luabind::detail::class_rep*): Assertion `(m_classes.find(info) == m_classes.end()) && "you are trying to register a class twice"' failed.
```

* Update lua_general.cpp
2024-03-28 20:05:55 -04:00
JJ b946b800fb [Cleanup] Reference type in GetRaidLeaderName (#4218)
Minor cleanup from #4054
2024-03-28 19:15:16 -04:00
Alex King ab8ac81df6 [Bug Fix] Fix Group Leadership (#4214)
* [Bug Fix] Fix Group Leadership

# Notes
- We were not sending anything to `group_leaders` table if we did not already have an existing row.

# Video

* Update database.cpp
2024-03-28 15:33:49 -05:00
Alex King 109940fc0c [Quest API] Add Class/Deity/Race Methods to Perl/Lua (#4215)
# Perl
- Add `$client->GetDeityBitmask()`.
- Add `quest::get_class_bitmask(class_id)`.
- Add `quest::get_deity_bitmask(deity_id)`.
- Add `quest::get_race_bitmask(race_id)`.

# Lua
- Add `client:GetDeityBitmask()`.
- Add `eq.get_class_bitmask(class_id)`.
- Add `eq.get_deity_bitmask(deity_id)`.
- Add `eq.get_race_bitmask(race_id)`.

# Notes
- Allows operators to get the class/deity/race bitmask of a class/deity/race by ID.
- Allows operators to get a client's deity bitmask.
2024-03-28 15:32:02 -05:00
Fryguy a87496b0cf [Lua] Add Zone and Language Constants (#4211)
* [LUA] Add Zone and Language Constants

This will add Zone:

```lua
if eq.get_zone_id() == Zone.Qeynos then
   foo()
end
```

It will also add Language:

```lua
if e.language == Language.ElderElvish and e.other:GetLanguageSkill(Language.ElderElvish) >= 100 then
   e.self:Say("You know my language!", Language.ElderElvish);
end
```

These changes should help avoid magic numbers in quests and aide in readability without the need for -- comments to clarify.

* Adjust to lower case
2024-03-28 13:16:41 -04:00
Alex King f905ee70e4 [Bug Fix] Fix Auto Login Issue (#4213)
* [Bug Fix] Fix Auto Login Issue

# Notes
- We were setting `live_name` value regardless of if we were zoning, causing us to automatically log in to the last character we'd logged in to before.

* Remove &
2024-03-28 09:48:17 -04:00
JJ 52417023f8 [Cleanup] Remove unnecessary reference types (#4212)
Minor clean up from #4054 and #4181
2024-03-27 22:17:21 -04:00
KayenEQ 20d9417628 [Spells] SPA148 Stacking Fix (#4206)
Update to SPA148 which acts to block spells buffs that are of lesser value than the current buff for specific effect slots. This effected was preventing detrimental debuffs from being applied that were using same effect slot. This bug affected a very small amount of spell interactions and was fixed on live in 2018.  Example being Vishmitars Corruption (6642) being blocked by SteelOak Skin (5352)

 I confirmed the behavior on live myself. The detrimental buff  if in conflict should now be applied instead of blocked.
2024-03-27 14:39:40 -04:00
Xackery 4692799677 [Bug Fix] Fix event_consider any_cast error (#4210) 2024-03-27 14:31:38 -04:00
Chris Miles cf7f0f4321 [Skill Caps] Further improvements (#4205) 2024-03-24 13:04:26 -04:00
Chris Miles 823a5956de [Hotfix] Fix crash in SendEnterWorld (#4204) 2024-03-23 23:28:29 -05:00
JJ 6a8bd3c5d6 [Bug Fix] Fix fishing chances (#4203)
Fix regression from #4008 where the item count wasn't being incremented
Move the zone table chance to a rule
General cleanup of some initializers and variable types
2024-03-23 23:32:07 -04:00
Alex King 523ba30d81 [Repositories] Convert database.cpp to Repositories (#4054)
* [Repositories] Convert database.cpp to Repositories

- Convert all database.cpp methods to repositories where possible.

* Final push.

* Cleanup

* Cleanup

* Update database.h

* Fix crash

* Update database.cpp
2024-03-23 19:30:56 -05:00
Alex King d7ea290b6b [Skill Caps] Remove from shared memory and simplify (#4069)
* [Skill Caps] Remove from shared memory and simplify

- Removes Skill Caps loading from shared memory and puts it into zone.
- Adds `id` column to `skill_caps`.
- Remove primary keys and use `id` as primary key.
- Add unique index using `skill_id`, `class_id`, `level`, and `cap`.
- Renames `class` to `class_id` in `skill_caps` table.
- Renames `skillID` to `skill_id` in `skill_caps` table.
- Regenerates Skill Caps repository.
- Adds `#reload skill_caps` to reload skill caps in real time.

* Update groups.cpp

* Update groups.cpp
2024-03-23 18:52:40 -05:00
Alex King 036309ebec [Hot Fix] Hot Fix for Group::AddToGroup Hot Fix (#4202)
# Notes
- Typo in previous.
2024-03-23 18:51:43 -05:00
Alex King b400700d81 [Hot Fix] Fix Group::AddToGroup (#4201)
# Notes
- We were not using `r` values at any point.
2024-03-23 18:49:15 -05:00
Alex King 21cec87ac4 [Bug Fix] Fix Bot/Character ID Overlap in Groups (#4093)
* [Bug Fix] Fix Bot/Character ID Overlap in Groups

- Attempt to fix bot/character ID overlap in groups keeping bots with the same unique identifier as players from not spawning on zone.
- Adds `bot_id` to `group_id` to differentiate bots from characters and hopefully alleviate this issue.

* Update base_group_id_repository.h

* Final push
2024-03-23 17:55:03 -05:00
Alex King abdec39cdd [Quest API] Add Archetype Methods to Perl/Lua (#4181)
* [Quest API] Add Archetype Methods to Perl/Lua

- Add `$mob->GetArchetypeName()`.
- Add `$mob->IsIntelligenceCasterClass()`.
- Add `$mob->IsPureMeleeClass()`.
- Add `$mob->IsWisdomCasterClass()`.

- Add `mob:GetArchetypeName()`.
- Add `mob:IsIntelligenceCasterClass()`.
- Add `mob:IsPureMeleeClass()`.
- Add `mob:IsWisdomCasterClass()`.

- Allows operators to use mob archetypes to perform different operations.
- Add a namespace for archetypes instead of constants.
- Utilize `IsIntelligenceCasterClass()`, `IsPureMeleeClass()`, and `IsWisdomCasterClass()` where necessary.
-

* Update mob.cpp
2024-03-23 14:37:35 -05:00
Fryguy d2372de982 [Bug Fix] Radiant/Ebon Crystals should only extract to 1000 (#4195)
* [Bug] Radiant/Ebon Crystals should only extract to 1000

Creating a stack larger than 1000 can cause potential dupe issues further down the stream if other tasks are performed on the stack.

* Use stacksize instead

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2024-03-23 12:56:59 -05:00
Chris Miles ea9b7841d4 [Release] 22.48.0 (#4200) 2024-03-23 00:24:01 -05:00
Alex King 8826d7b927 [Rules] Add World:Rules Rule (#4194)
* [Rules] Add World:Rules Rule

# Notes
- Adds `World:Rules` rule to take the place of the variables table value.

* Update client.cpp

* Update client.cpp
2024-03-23 00:01:27 -05:00
JJ e4157f0221 [Bug Fix] Fix reusing timers (#4199)
Fixes regression from #4099 where timers that were reused were not getting changed with their updated times
2024-03-23 00:01:15 -05:00
Alex King 66cc947b2a [Quest API] Add Restore Methods for Health, Mana, and Endurance to Perl/Lua (#4179)
* [Quest API] Add Restore Methods for Health, Mana, and Endurance to Perl/Lua

- Add `$mob->RestoreEndurance()`.
- Add `$mob->RestoreHealth()`.
- Add `$mob->RestoreMana()`.

- Add `mob:RestoreEndurance()`.
- Add `mob:RestoreHealth()`.
- Add `mob:RestoreMana()`.

- Allows operators to easily restore a mob to full health, mana, or endurance.
- `RestoreHealth` is just a more verbosely named `Heal`.
- Convert spots in source to use these short hands instead of directly using `SetEndurance(GetMaxEndurance())`, `SetHP(GetMaxHP())`, or `SetMana(GetMaxMana())`.

* Update mob.h

* Update mob.h
2024-03-22 23:53:35 -05:00
hg 5bfd8f5da2 [Tradeskills] Implement learning recipes from books (#4170)
This uses the book scribe button to learn recipes in SoF+ clients. For
Titanium clients the button is not available so a workaround will need
to be added later.

Note live gives no feedback when scribing books (at least those tested).
2024-03-22 23:50:06 -05:00
Chris Miles 96830b4a19 [Loot] Fix issue with nested data being loaded multiple times (#4192)
* [Loot] Fix issue with nested data being loaded multiple times

* Update zone_loot.cpp

* Fix #lootsim printout

* Update loot.cpp
2024-03-22 23:49:23 -05:00
Alex King 4bf60a6522 [Bug Fix] Fix ScaleNPC() in Perl (#4196)
# Notes
- We were not passing `override_special_abilities` in the right parameter slot.
- Default `always_scale` to `true` when using the Perl/Lua method.
2024-03-22 23:36:30 -05:00
JJ 16cb7364e8 [Misc] Windows preprocessor define in crash.cpp (#4191) 2024-03-22 23:27:31 -05:00
Alex King f5050ab5dc [Bug Fix] Fix range_percent (#4197)
# Notes
- This was uninitialized and was getting bogus values.
2024-03-22 16:17:38 -04:00
Alex King e32cbf19ee [Bug Fix] Fix #serverrules Command (#4193)
* [Bug Fix] Fix #serverrules Command

# Notes
- This command was effectively disabled as it was not included nor was the command itself defined within a `command_add`

* Update command.cpp
2024-03-20 03:31:49 -04:00
Alex King ee12a7ad2e [Bug Fix] Fix EVENT_KILLED_MERIT firing before NPC removal (#4185)
* [Bug Fix] Fix EVENT_KILLED_MERIT firing before NPC removal

# Notes
- NPCs were parsing this event too early and anything that checked if they were still alive in `EVENT_KILLED_MERIT` would show them still alive because of this.

# Image

* Code cleanup

* Update client.h

* Add GetRaidOrGroupOrSelf() to Perl/Lua

* Update to luabind::object, fix logic per comments.

* Fix

* Fix per comments.
2024-03-17 17:32:44 -04:00
Alex King e5bdbc4f1e [Bug Fix] Fix Bot Cloning (#4186)
# Notes
- We were not setting `inventories_index` to `0` so it was trying to use the pre-existing unique identifier, causing the query to fail.
2024-03-16 23:09:57 -04:00
Alex King f829a99e6d [Quest API] Add RemoveAlternateCurrencyValue() to Perl/Lua (#4190)
# Perl
- Add `$client->RemoveAlternateCurrencyValue(currency_id, amount)`.

# Lua
- Add `client:RemoveAlternateCurrencyValue(currency_id, amount)`.

# Notes
- Allows operators to more easily remove alternate currencies, returns a `bool`, `false` if failed`, `true` if succeeded.
- Added `Zone::DoesAlternateCurrencyExist` that will reject setting, removing, or adding  to a currency that does not exist.
2024-03-16 23:09:51 -04:00
Alex King 82aa6a1587 [Bug Fix] Fix Proximity Say (#4189)
# Notes
- Without setting `HaveProximitySays` to `true` along with if `n->proximity->say` NPCs will not respond to proximity say stuff.
2024-03-14 20:26:30 -04:00
Alex King 161c13f457 [Quest API] Add Buff Support to Perl/Lua (#4182)
* [Quest API] Add Buff Support to Perl/Lua

- Add `$mob->GetCasterID()`.
- Add `$mob->GetCasterLevel()`.
- Add `$mob->GetCasterName()`.
- Add `$mob->GetCastOnX()`.
- Add `$mob->GetCastOnY()`.
- Add `$mob->GetCastOnZ()`.
- Add `$mob->GetCounters()`.
- Add `$mob->GetDOTRune()`.
- Add `$mob->GetExtraDIChance()`.
- Add `$mob->GetInstrumentModi()`.
- Add `$mob->GetMagicRune()`.
- Add `$mob->GetMeleeRune()`.
- Add `$mob->GetNumberOfHits()`.
- Add `$mob->GetRootBreakChanc()`.
- Add `$mob->GetSpellID()`.
- Add `$mob->GetTicsRemaining()`.
- Add `$mob->GetVirusSpreadTim()`.
- Add `$mob->IsCasterClient()`.
- Add `$mob->IsPersistentBuff()`.
- Add `$mob->SendsClientUpdate()`.

- Add `mob:GetCasterID()`.
- Add `mob:GetCasterLevel()`.
- Add `mob:GetCasterName()`.
- Add `mob:GetCastOnX()`.
- Add `mob:GetCastOnY()`.
- Add `mob:GetCastOnZ()`.
- Add `mob:GetCounters()`.
- Add `mob:GetDOTRune()`.
- Add `mob:GetExtraDIChance()`.
- Add `mob:GetInstrumentModi()`.
- Add `mob:GetMagicRune()`.
- Add `mob:GetMeleeRune()`.
- Add `mob:GetNumberOfHits()`.
- Add `mob:GetRootBreakChanc()`.
- Add `mob:GetSpellID()`.
- Add `mob:GetTicsRemaining()`.
- Add `mob:GetVirusSpreadTim()`.
- Add `mob:IsCasterClient()`.
- Add `mob:IsPersistentBuff()`.
- Add `mob:SendsClientUpdate()`.

- Adds support for `Buffs_Struct` to Perl/Lua.
- Allows operators to read a mob's buff data directly to determine caster, melee rune, etc.

* Fix GetCasterID() to proper data type.

* Remove Lua_Buffs, return table instead.

* Cleanup
2024-03-13 23:38:15 -04:00
Alex King b29c26becb [Bug Fix] Fix GetLeaderName() for Groups (#4184)
* [Bug Fix] Fix GetLeaderName() for Groups

# Notes
- We were getting bogus data in this.
- Made it its own method.

* Remove ExpeditionRequest::GetGroupLeaderName()
2024-03-13 21:52:02 -04:00
Alex King e48dae2392 [Quest API] Add Silent Saylink Methods to Perl/Lua (#4177)
# Perl
- Add `quest::silent_saylink(text)`.
- Add `quest::silent_saylink(text, link_name)`.

# Lua
- Add `eq.silent_say_link(text)`.
- Add `eq.silent_say_link(text, link_name)`.

# Notes
- Allows operators to more easily use silent saylinks without an optional silent parameter in the traditional saylink methods.
- Sets `silent` parameter default to `false` so we do not need to pass `false` when we are not using a a silent saylink.
- Changes all places that used `EQ::SayLinkEngine::GenerateQuestSaylink` to `Saylink::Create` where possible.
- Removed `questmgr` method that is no longer necessary.
- Cleaned up Lua methods to use the strings directly instead of building one out.
2024-03-13 20:27:44 -04:00
Alex King 4b83a96f64 [Bug Fix] Fix Empty Groups When Removing Bots (#4178)
# Notes
- We were checking for the incorrect value, meaning you could end up in an empty group.
2024-03-13 20:27:38 -04:00
Alex King 95cc22ffbb [Quest API] Add GetDeityName() to Perl/Lua (#4180)
# Perl
- Add `$mob->GetDeityName()`.

# Lua
- Add `mob:GetDeityName()`.

# Notes
- Allows operators to get a mob's deity name.
2024-03-13 20:27:31 -04:00
nytmyr 6ca11256c6 [Bots] IsValidTarget Crash Fix (#4187)
* [Bots] IsValidTarget crash fix

This addresses crashes related to IsValidTarget on multiple servers.

Unsure of the exact reason if anyone can explain why changing from const bool to bool in this situation fixes the problem.

Is it because the const is somehow crashing on a bad pointer or is it attempting to be force changed?

* Update bot.cpp

---------

Co-authored-by: Alex King <89047260+Kinglykrab@users.noreply.github.com>
2024-03-12 12:34:27 -04:00
Alex King d94493468c [Bug Fix] Fix Mob::CalculateDistance(mob) Typo (#4183)
# Notes
- Was using `GetX()` for all parts of the calculation, meaning it would produce incorrect values.
- Part of [this](https://github.com/EQEmu/Server/pull/3455/files#diff-94a8e3b04f001e4f74ce2da8404cbce7653146d963a3b93be6536505035ce96dR602-R607).
2024-03-09 22:20:02 -05:00
Alex King 957b4f8821 [Bug Fix] Fix Crash in ClientList::GetCLEIP (#4173)
* [Bug Fix] Fix Crash in ClientList::GetCLEIP

# Notes
- We were not validating pointer when moving to next iterator.

# Crashes
https://spire.akkadius.com/dev/release/22.46.1?id=19955
https://spire.akkadius.com/dev/release/22.46.1?id=19948
https://spire.akkadius.com/dev/release/22.46.0?id=19945

* Update clientlist.cpp

* Update clientlist.cpp
2024-03-09 19:47:58 -05:00
Alex King 5013459824 [Hot Fix] Add bool return to fix Client::RemoveAAPoints (#4176)
# Notes
- Was missing the `return true;` at the bottom.
2024-03-09 10:07:13 -05:00
Alex King 94af2843e3 [Cleanup] Cleanup Zone Get Methods (#4169)
# Notes
- Utilize `GetZoneVersionWithFallback` to shorten methods and reduce duplicate code.
2024-03-08 21:22:11 -05:00
Alex King 3bfb148bdc [Quest API] Add RemoveAAPoints() and AA Loss Event to Perl/Lua (#4174)
* [Quest API] Add RemoveAAPoints() and AA Loss Event to Perl/Lua

# Perl
- Add `$client->RemoveAAPoints(points)`.
- Add `EVENT_AA_LOSS`, exports `$aa_lost`.

# Lua
- Add `client:RemoveAAPoints(points)`.
- Add `event_aa_loss`, exports `e.aa_lost`.

# Notes
- Allows operators to more easily remove AA Points.
- Has a bool return type that will return false if the player does not have enough AA Points to complete the removal.

* Update client.cpp
2024-03-08 21:20:33 -05:00
MortimerGreenwald 96370e0298 [Bug Fix] An Update to Xtarget to exclude Bot owned Temp/Swarm Pets (#4172)
* An Update to Xtarget to exclude Bot owned Temp/Swarm Pets

* Missing a parentheses

* Cleaned up logic.
2024-03-08 15:29:53 -05:00
nytmyr fef5108b0d [Bots] Move BotGroupSay to Pet Response (#4171)
* [Bots] Move BotGroupSay messages to PetResponse filter to reduce spam

* Cleanup raid checks

* Group cleanup
2024-03-08 11:46:50 -05:00
Alex King 6f883566f6 [Bug Fix] Fix Default Value in rule_values table (#4166)
# Notes
- Some versions of SQL do not allow a default value for text fields.
2024-03-07 16:41:41 -05:00
nytmyr 45b1501c8a [Quest API] Add DeleteBot() to Perl/Lua (#4167)
### Perl
- Add $bot->DeleteBot().
```
sub EVENT_SAY {
	if ($text =~/#deletebot/i && $client->Admin() >= 100) {
		my @bot_list = $entity_list->GetBotList();

		foreach $ent (@bot_list) {
			if ($ent) {
				quest::shout("Deleting " . $ent->GetCleanName());
				$ent->DeleteBot();
				$ent->Camp(0);
			}
		}
	}
}
```
### Lua
- Add bot:DeleteBot().
```
function event_say(e)
	if(e.message:findi("#deletebot")) then
		local bot_list = eq.get_entity_list():GetBotList();

		for ent in bot_list.entries do
			if (ent) then
				e.self:Message(7,"Deleting " .. ent:GetCleanName() .. "");
				ent:DeleteBot();
				ent:Camp(false);
			end
		end
	end
end
```
### Notes
- Allows operators to delete bots.
2024-03-07 06:08:17 -05:00
144 changed files with 5951 additions and 4072 deletions
+57
View File
@@ -1,3 +1,60 @@
## [22.48.0] 3/23/2024
### Bots
* IsValidTarget Crash Fix ([#4187](https://github.com/EQEmu/Server/pull/4187)) @nytmyr 2024-03-12
* Move BotGroupSay to Pet Response ([#4171](https://github.com/EQEmu/Server/pull/4171)) @nytmyr 2024-03-08
### Code
* Cleanup Zone Get Methods ([#4169](https://github.com/EQEmu/Server/pull/4169)) @Kinglykrab 2024-03-09
### Fixes
* An Update to Xtarget to exclude Bot owned Temp/Swarm Pets ([#4172](https://github.com/EQEmu/Server/pull/4172)) @MortimerGreenwald 2024-03-08
* Fix #serverrules Command ([#4193](https://github.com/EQEmu/Server/pull/4193)) @Kinglykrab 2024-03-20
* Fix Bot Cloning ([#4186](https://github.com/EQEmu/Server/pull/4186)) @Kinglykrab 2024-03-17
* Fix Crash in ClientList::GetCLEIP ([#4173](https://github.com/EQEmu/Server/pull/4173)) @Kinglykrab 2024-03-10
* Fix Default Value in `rule_values` table ([#4166](https://github.com/EQEmu/Server/pull/4166)) @Kinglykrab 2024-03-07
* Fix EVENT_KILLED_MERIT firing before NPC removal ([#4185](https://github.com/EQEmu/Server/pull/4185)) @Kinglykrab 2024-03-17
* Fix Empty Groups When Removing Bots ([#4178](https://github.com/EQEmu/Server/pull/4178)) @Kinglykrab 2024-03-14
* Fix GetLeaderName() for Groups ([#4184](https://github.com/EQEmu/Server/pull/4184)) @Kinglykrab 2024-03-14
* Fix Mob::CalculateDistance(mob) Typo ([#4183](https://github.com/EQEmu/Server/pull/4183)) @Kinglykrab 2024-03-10
* Fix Proximity Say ([#4189](https://github.com/EQEmu/Server/pull/4189)) @Kinglykrab 2024-03-15
* Fix ScaleNPC() in Perl ([#4196](https://github.com/EQEmu/Server/pull/4196)) @Kinglykrab 2024-03-23
* Fix range_percent ([#4197](https://github.com/EQEmu/Server/pull/4197)) @Kinglykrab 2024-03-22
* Fix reusing timers ([#4199](https://github.com/EQEmu/Server/pull/4199)) @joligario 2024-03-23
### Hot Fix
* Add bool return to fix Client::RemoveAAPoints ([#4176](https://github.com/EQEmu/Server/pull/4176)) @Kinglykrab 2024-03-09
### Loot
* Fix issue with nested data being loaded multiple times ([#4192](https://github.com/EQEmu/Server/pull/4192)) @Akkadius 2024-03-23
### Misc
* Windows preprocessor define in crash.cpp ([#4191](https://github.com/EQEmu/Server/pull/4191)) @joligario 2024-03-23
### Quest API
* Add Buff Support to Perl/Lua ([#4182](https://github.com/EQEmu/Server/pull/4182)) @Kinglykrab 2024-03-14
* Add DeleteBot() to Perl/Lua ([#4167](https://github.com/EQEmu/Server/pull/4167)) @nytmyr 2024-03-07
* Add GetDeityName() to Perl/Lua ([#4180](https://github.com/EQEmu/Server/pull/4180)) @Kinglykrab 2024-03-14
* Add RemoveAAPoints() and AA Loss Event to Perl/Lua ([#4174](https://github.com/EQEmu/Server/pull/4174)) @Kinglykrab 2024-03-09
* Add RemoveAlternateCurrencyValue() to Perl/Lua ([#4190](https://github.com/EQEmu/Server/pull/4190)) @Kinglykrab 2024-03-17
* Add Restore Methods for Health, Mana, and Endurance to Perl/Lua ([#4179](https://github.com/EQEmu/Server/pull/4179)) @Kinglykrab 2024-03-23
* Add Silent Saylink Methods to Perl/Lua ([#4177](https://github.com/EQEmu/Server/pull/4177)) @Kinglykrab 2024-03-14
### Rules
* Add World:Rules Rule ([#4194](https://github.com/EQEmu/Server/pull/4194)) @Kinglykrab 2024-03-23
### Tradeskills
* Implement learning recipes from books ([#4170](https://github.com/EQEmu/Server/pull/4170)) @hgtw 2024-03-23
## [22.47.0] 3/5/2024
### Crash Fix
+42 -45
View File
@@ -29,6 +29,8 @@
#include "../../common/content/world_content_service.h"
#include "../../common/zone_store.h"
#include "../../common/path_manager.h"
#include "../../common/repositories/skill_caps_repository.h"
#include "../../common/file.h"
EQEmuLogSys LogSys;
WorldContentService content_service;
@@ -164,81 +166,76 @@ void ExportSpells(SharedDatabase *db)
fclose(f);
}
bool SkillUsable(SharedDatabase *db, int skill_id, int class_id)
bool SkillUsable(SharedDatabase* db, int skill_id, int class_id)
{
bool res = false;
std::string query = StringFormat(
"SELECT max(cap) FROM skill_caps WHERE class=%d AND skillID=%d",
class_id, skill_id
const auto& l = SkillCapsRepository::GetWhere(
*db,
fmt::format(
"`class_id` = {} AND `skill_id` = {} ORDER BY `cap` DESC LIMIT 1",
class_id,
skill_id
)
);
auto results = db->QueryDatabase(query);
if (!results.Success()) {
return false;
}
if (results.RowCount() == 0) {
return false;
}
auto row = results.begin();
if (row[0] && Strings::ToInt(row[0]) > 0) {
return true;
}
return false;
return !l.empty();
}
int GetSkill(SharedDatabase *db, int skill_id, int class_id, int level)
uint32 GetSkill(SharedDatabase* db, int skill_id, int class_id, int level)
{
std::string query = StringFormat(
"SELECT cap FROM skill_caps WHERE class=%d AND skillID=%d AND level=%d",
class_id, skill_id, level
const auto& l = SkillCapsRepository::GetWhere(
*db,
fmt::format(
"`class_id` = {} AND `skill_id` = {} AND `level` = {}",
class_id,
skill_id,
level
)
);
auto results = db->QueryDatabase(query);
if (!results.Success()) {
if (l.empty()) {
return 0;
}
if (results.RowCount() == 0) {
return 0;
}
auto e = l.front();
auto row = results.begin();
return Strings::ToInt(row[0]);
return e.cap;
}
void ExportSkillCaps(SharedDatabase *db)
void ExportSkillCaps(SharedDatabase* db)
{
LogInfo("Exporting Skill Caps");
std::string file = fmt::format("{}/export/SkillCaps.txt", path.GetServerPath());
FILE *f = fopen(file.c_str(), "w");
if (!f) {
std::ofstream file(fmt::format("{}/export/SkillCaps.txt", path.GetServerPath()));
if (!file || !file.is_open()) {
LogError("Unable to open export/SkillCaps.txt to write, skipping.");
return;
}
for (int cl = 1; cl <= 16; ++cl) {
for (int skill = 0; skill <= 77; ++skill) {
if (SkillUsable(db, skill, cl)) {
int previous_cap = 0;
for (int level = 1; level <= 100; ++level) {
int cap = GetSkill(db, skill, cl, level);
const uint8 skill_cap_max_level = (
RuleI(Character, SkillCapMaxLevel) > 0 ?
RuleI(Character, SkillCapMaxLevel) :
RuleI(Character, MaxLevel)
);
for (uint8 class_id = Class::Warrior; class_id <= Class::Berserker; class_id++) {
for (uint8 skill_id = EQ::skills::Skill1HBlunt; skill_id <= EQ::skills::Skill2HPiercing; skill_id++) {
if (SkillUsable(db, skill_id, class_id)) {
uint32 previous_cap = 0;
for (uint8 level = 1; level <= skill_cap_max_level; level++) {
uint32 cap = GetSkill(db, skill_id, class_id, level);
if (cap < previous_cap) {
cap = previous_cap;
}
fprintf(f, "%d^%d^%d^%d^0\n", cl, skill, level, cap);
file << fmt::format("{}^{}^{}^{}^0", class_id, skill_id, level, cap) << std::endl;
previous_cap = cap;
}
}
}
}
fclose(f);
file.close();
}
void ExportBaseData(SharedDatabase *db)
+11 -9
View File
@@ -83,6 +83,7 @@ SET(common_sources
shared_tasks.cpp
shareddb.cpp
skills.cpp
skill_caps.cpp
spdat.cpp
strings.cpp
struct_strategy.cpp
@@ -492,7 +493,7 @@ SET(repositories
repositories/zone_repository.h
repositories/zone_points_repository.h
)
)
SET(common_headers
additive_lagged_fibonacci_engine.h
@@ -592,7 +593,7 @@ SET(common_headers
ptimer.h
queue.h
races.h
raid.h
raid.h
random.h
rdtsc.h
rulesys.h
@@ -606,6 +607,7 @@ SET(common_headers
shared_tasks.h
shareddb.h
skills.h
skill_caps.h
spdat.h
strings.h
struct_strategy.h
@@ -681,13 +683,13 @@ SOURCE_GROUP(Event FILES
event/event_loop.h
event/timer.h
event/task.h
)
)
SOURCE_GROUP(Json FILES
json/json.h
json/jsoncpp.cpp
json/json-forwards.h
)
)
SOURCE_GROUP(Net FILES
net/console_server.cpp
@@ -724,7 +726,7 @@ SOURCE_GROUP(Net FILES
net/websocket_server.h
net/websocket_server_connection.cpp
net/websocket_server_connection.h
)
)
SOURCE_GROUP(Patches FILES
patches/patches.h
@@ -768,12 +770,12 @@ SOURCE_GROUP(Patches FILES
patches/titanium_limits.cpp
patches/uf.cpp
patches/uf_limits.cpp
)
)
SOURCE_GROUP(StackWalker FILES
StackWalker/StackWalker.h
StackWalker/StackWalker.cpp
)
)
SOURCE_GROUP(Util FILES
util/memory_stream.h
@@ -781,7 +783,7 @@ SOURCE_GROUP(Util FILES
util/directory.h
util/uuid.cpp
util/uuid.h
)
)
INCLUDE_DIRECTORIES(Patches SocketLib StackWalker)
@@ -794,6 +796,6 @@ ENDIF (UNIX)
IF (WIN32 AND EQEMU_BUILD_PCH)
TARGET_PRECOMPILE_HEADERS(common PRIVATE pch/pch.h)
ENDIF()
ENDIF ()
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
+1 -1
View File
@@ -15,7 +15,7 @@
#include <cstdio>
#include <vector>
#if WINDOWS
#ifdef _WINDOWS
#define popen _popen
#endif
+1011 -1285
View File
File diff suppressed because it is too large Load Diff
+145 -139
View File
@@ -18,8 +18,8 @@
#ifndef EQEMU_DATABASE_H
#define EQEMU_DATABASE_H
#define AUTHENTICATION_TIMEOUT 60
#define INVALID_ID 0xFFFFFFFF
#define AUTHENTICATION_TIMEOUT 60
#define INVALID_ID 0xFFFFFFFF
#include "global_define.h"
#include "eqemu_logsys.h"
@@ -38,8 +38,7 @@
class MySQLRequestResult;
class Client;
namespace EQ
{
namespace EQ {
class InventoryProfile;
}
@@ -52,10 +51,11 @@ struct npcDecayTimes_Struct {
struct VarCache_Struct {
std::map<std::string, std::string> m_cache;
uint32 last_update;
uint32 last_update;
VarCache_Struct() : last_update(0) { }
void Add(const std::string &key, const std::string &value) { m_cache[key] = value; }
const std::string *Get(const std::string &key) {
void Add(const std::string& key, const std::string& value) { m_cache[key] = value; }
const std::string* Get(const std::string& key)
{
auto it = m_cache.find(key);
return (it != m_cache.end() ? &it->second : nullptr);
}
@@ -76,37 +76,33 @@ class PTimerList;
#define SQL(...) #__VA_ARGS__
class LogSettings;
class Database : public DBcore {
public:
Database();
Database(const char* host, const char* user, const char* passwd, const char* database,uint32 port);
bool Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port, std::string connection_label = "default");
Database(
const std::string& host,
const std::string& user,
const std::string& password,
const std::string& database,
uint32 port
);
bool Connect(
const std::string& host,
const std::string& user,
const std::string& password,
const std::string& database,
uint32 port,
std::string connection_label = "default"
);
~Database();
/* Character Creation */
bool CreateCharacter(
uint32 account_id,
char *name,
uint16 gender,
uint16 race,
uint16 class_,
uint8 str,
uint8 sta,
uint8 cha,
uint8 dex,
uint8 int_,
uint8 agi,
uint8 wis,
uint8 face
);
bool DeleteCharacter(char *character_name);
bool MoveCharacterToZone(const char *charname, uint32 zone_id);
bool DeleteCharacter(const std::string& name);
bool MoveCharacterToZone(const std::string& name, uint32 zone_id);
bool MoveCharacterToZone(uint32 character_id, uint32 zone_id);
bool ReserveName(uint32 account_id, char *name);
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct *pp);
bool UpdateName(const char *oldname, const char *newname);
bool ReserveName(uint32 account_id, const std::string& name);
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp);
bool UpdateName(const std::string& old_name, const std::string& new_name);
bool CopyCharacter(
const std::string& source_character_name,
const std::string& destination_character_name,
@@ -114,161 +110,171 @@ public:
);
/* General Information Queries */
bool AddBannedIP(const std::string& banned_ip, const std::string& notes); //Add IP address to the banned_ips table.
bool AddToNameFilter(const std::string& name);
bool CheckBannedIPs(const std::string& login_ip); //Check incoming connection against banned IP table.
bool CheckGMIPs(const std::string& login_ip, uint32 account_id);
bool CheckNameFilter(const std::string& name, bool surname = false);
bool IsNameUsed(const std::string& name);
bool AddBannedIP(std::string banned_ip, std::string notes); //Add IP address to the banned_ips table.
bool AddToNameFilter(std::string name);
bool CheckBannedIPs(std::string login_ip); //Check incoming connection against banned IP table.
bool CheckGMIPs(std::string login_ip, uint32 account_id);
bool CheckNameFilter(std::string name, bool surname = false);
bool CheckUsedName(std::string name);
uint32 GetAccountIDByChar(const std::string& name, uint32* character_id = 0);
uint32 GetAccountIDByChar(uint32 character_id);
uint32 GetAccountIDByName(const std::string& account_name, const std::string& loginserver, int16* status = 0, uint32* lsid = 0);
uint32 GetCharacterID(const std::string& name);
uint32 GetGuildIDByCharID(uint32 character_id);
uint32 GetGroupIDByCharID(uint32 character_id);
uint32 GetRaidIDByCharID(uint32 character_id);
uint32 GetAccountIDByChar(const char* charname, uint32* oCharID = 0);
uint32 GetAccountIDByChar(uint32 char_id);
uint32 GetAccountIDByName(std::string account_name, std::string loginserver, int16* status = 0, uint32* lsid = 0);
uint32 GetCharacterID(const std::string& name);
uint32 GetCharacterInfo(std::string character_name, uint32 *account_id, uint32 *zone_id, uint32 *instance_id);
uint32 GetGuildIDByCharID(uint32 char_id);
uint32 GetGroupIDByCharID(uint32 char_id);
uint32 GetRaidIDByCharID(uint32 char_id);
void GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID = 0);
void GetCharName(uint32 char_id, char* name);
std::string GetCharNameByID(uint32 char_id);
std::string GetNPCNameByID(uint32 npc_id);
std::string GetCleanNPCNameByID(uint32 npc_id);
void LoginIP(uint32 account_id, std::string login_ip);
const std::string GetAccountName(uint32 account_id, uint32* lsaccount_id = 0);
const std::string GetCharName(uint32 character_id);
const std::string GetCharNameByID(uint32 character_id);
const std::string GetNPCNameByID(uint32 npc_id);
const std::string GetCleanNPCNameByID(uint32 npc_id);
void LoginIP(uint32 account_id, const std::string& login_ip);
/* Instancing */
bool AddClientToInstance(uint16 instance_id, uint32 character_id);
bool CheckInstanceByCharID(uint16 instance_id, uint32 character_id);
bool CheckInstanceExists(uint16 instance_id);
bool CheckInstanceExpired(uint16 instance_id);
bool CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration);
bool GetUnusedInstanceID(uint16 &instance_id);
bool GetUnusedInstanceID(uint16& instance_id);
bool IsGlobalInstance(uint16 instance_id);
bool RemoveClientFromInstance(uint16 instance_id, uint32 char_id);
bool RemoveClientsFromInstance(uint16 instance_id);
bool VerifyInstanceAlive(uint16 instance_id, uint32 character_id);
bool VerifyZoneInstance(uint32 zone_id, uint16 instance_id);
uint16 GetInstanceID(uint32 zone, uint32 charid, int16 version);
uint16 GetInstanceID(uint32 zone, uint32 character_id, int16 version);
std::vector<uint16> GetInstanceIDs(uint32 zone_id, uint32 character_id);
uint8_t GetInstanceVersion(uint16 instance_id);
uint32 GetTimeRemainingInstance(uint16 instance_id, bool &is_perma);
uint32 GetTimeRemainingInstance(uint16 instance_id, bool& is_perma);
uint32 GetInstanceZoneID(uint16 instance_id);
void AssignGroupToInstance(uint32 gid, uint32 instance_id);
void AssignRaidToInstance(uint32 rid, uint32 instance_id);
void AssignGroupToInstance(uint32 group_id, uint32 instance_id);
void AssignRaidToInstance(uint32 raid_id, uint32 instance_id);
void DeleteInstance(uint16 instance_id);
void FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 charid, uint32 group_id);
void FlagInstanceByRaidLeader(uint32 zone_id, int16 version, uint32 charid, uint32 raid_id);
void GetCharactersInInstance(uint16 instance_id, std::list<uint32> &character_ids);
void FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 group_id);
void FlagInstanceByRaidLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 raid_id);
void GetCharactersInInstance(uint16 instance_id, std::list<uint32>& character_ids);
void PurgeExpiredInstances();
void SetInstanceDuration(uint16 instance_id, uint32 new_duration);
void CleanupInstanceCorpses();
/* Adventure related. */
void UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win = false, bool remove = false);
bool GetAdventureStats(uint32 char_id, AdventureStats_Struct *as);
void UpdateAdventureStatsEntry(uint32 character_id, uint8 theme_id, bool is_win = false, bool is_remove = false);
bool GetAdventureStats(uint32 character_id, AdventureStats_Struct* as);
/* Account Related */
const std::string GetLiveChar(uint32 account_id);
bool SetAccountStatus(const std::string& account_name, int16 status);
bool SetLocalPassword(uint32 account_id, const std::string& password);
bool UpdateLiveChar(const std::string& name, uint32 account_id);
int16 CheckStatus(uint32 account_id);
void SetAccountCRCField(uint32 account_id, const std::string& field_name, uint64 checksum);
uint32 CheckLogin(const std::string& name, const std::string& password, const std::string& loginserver, int16* status = 0);
uint32 CreateAccount(
const std::string& name,
const std::string& password,
int16 status,
const std::string& 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
);
bool DeleteAccount(const char *name, const char* loginserver);
bool GetLiveChar(uint32 account_id, char* cname);
bool SetAccountStatus(const char* name, int16 status);
bool SetAccountStatus(const std::string& account_name, int16 status);
bool SetLocalPassword(uint32 accid, const char* password);
bool UpdateLiveChar(char* charname, uint32 account_id);
uint8 GetAgreementFlag(uint32 account_id);
void SetAgreementFlag(uint32 account_id);
int16 CheckStatus(uint32 account_id);
int GetIPExemption(const std::string& account_ip);
void SetIPExemption(const std::string& account_ip, int exemption_amount);
void SetAccountCRCField(uint32 account_id, std::string field_name, uint64 checksum);
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 account_id);
void GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus);
void SetAgreementFlag(uint32 account_id);
int GetIPExemption(std::string account_ip);
void SetIPExemption(std::string account_ip, int exemption_amount);
int GetInstanceID(uint32 char_id, uint32 zone_id);
int GetInstanceID(uint32 character_id, uint32 zone_id);
/* Groups */
std::string GetGroupLeaderForLogin(std::string character_name);
char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr);
uint32 GetGroupID(const char* name);
void ClearGroup(uint32 gid = 0);
void ClearGroupLeader(uint32 gid = 0);
void SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ismerc = false);
void SetGroupLeaderName(uint32 gid, const char* name);
std::string GetGroupLeaderForLogin(const std::string& character_name);
char* GetGroupLeadershipInfo(
uint32 group_id,
char* leaderbuf,
char* maintank = nullptr,
char* assist = nullptr,
char* puller = nullptr,
char* marknpc = nullptr,
char* mentoree = nullptr,
int* mentor_percent = nullptr,
GroupLeadershipAA_Struct* GLAA = nullptr
);
std::string GetGroupLeaderName(uint32 group_id);
uint32 GetGroupID(const std::string& name);
void ClearGroup(uint32 group_id = 0);
void ClearGroupLeader(uint32 group_id = 0);
void SetGroupLeaderName(uint32 group_id, const std::string& name);
/* Raids */
const std::string GetRaidLeaderName(uint32 raid_id);
uint32 GetRaidID(const std::string& name);
void ClearRaid(uint32 raid_id = 0);
void ClearRaidDetails(uint32 raid_id = 0);
void ClearRaidLeader(uint32 group_id = std::numeric_limits<uint32>::max(), uint32 raid_id = 0);
void GetGroupLeadershipInfo(
uint32 group_id,
uint32 raid_id,
char* maintank = nullptr,
char* assist = nullptr,
char* puller = nullptr,
char* marknpc = nullptr,
char* mentoree = nullptr,
int* mentor_percent = nullptr,
GroupLeadershipAA_Struct* GLAA = nullptr
);
void GetRaidLeadershipInfo(
uint32 raid_id,
char* maintank = nullptr,
char* assist = nullptr,
char* puller = nullptr,
char* marknpc = nullptr,
RaidLeadershipAA_Struct* RLAA = nullptr
);
void SetRaidGroupLeaderInfo(uint32 group_id, uint32 raid_id);
const char *GetRaidLeaderName(uint32 rid);
uint32 GetRaidID(const char* name);
void ClearRaid(uint32 rid = 0);
void ClearRaidDetails(uint32 rid = 0);
void ClearRaidLeader(uint32 gid = 0xFFFFFFFF, uint32 rid = 0);
void GetGroupLeadershipInfo(uint32 gid, uint32 rid, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr);
void GetRaidLeadershipInfo(uint32 rid, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, RaidLeadershipAA_Struct* RLAA = nullptr);
void SetRaidGroupLeaderInfo(uint32 gid, uint32 rid);
void PurgeAllDeletedDataBuckets();
void PurgeAllDeletedDataBuckets();
/* Database Variables */
bool GetVariable(const std::string& name, std::string& value);
bool SetVariable(const std::string& name, const std::string& value);
bool LoadVariables();
bool GetVariable(std::string varname, std::string &varvalue);
bool SetVariable(const std::string& varname, const std::string &varvalue);
bool LoadVariables();
uint8 GetPEQZone(uint32 zone_id, uint32 version);
uint32 GetServerType();
void AddReport(const std::string& who, const std::string& against, const std::string& lines);
struct TimeOfDay_Struct LoadTime(time_t& realtime);
bool SaveTime(int8 minute, int8 hour, int8 day, int8 month, int16 year);
void ClearMerchantTemp();
void ClearPTimers(uint32 character_id);
void SetFirstLogon(uint32 character_id, uint8 first_logon);
void SetLFG(uint32 character_id, bool is_lfg);
void SetLFP(uint32 character_id, bool is_lfp);
void SetLoginFlags(uint32 character_id, bool is_lfp, bool is_lfg, uint8 first_logon);
/* General Queries */
int64 CountInvSnapshots();
void ClearInvSnapshots(bool from_now = false);
bool GetZoneGraveyard(const uint32 graveyard_id, uint32* graveyard_zoneid = 0, float* graveyard_x = 0, float* graveyard_y = 0, float* graveyard_z = 0, float* graveyard_heading = 0);
bool LoadPTimers(uint32 charid, PTimerList &into);
uint8 GetPEQZone(uint32 zone_id, uint32 version);
uint8 GetMinStatus(uint32 zone_id, uint32 instance_version);
uint8 GetRaceSkill(uint8 skillid, uint8 in_race);
uint8 GetServerType();
uint8 GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 in_level);
void AddReport(std::string who, std::string against, std::string lines);
struct TimeOfDay_Struct LoadTime(time_t &realtime);
bool SaveTime(int8 minute, int8 hour, int8 day, int8 month, int16 year);
void ClearMerchantTemp();
void ClearPTimers(uint32 charid);
void SetFirstLogon(uint32 CharID, uint8 firstlogon);
void SetLFG(uint32 CharID, bool LFG);
void SetLFP(uint32 CharID, bool LFP);
void SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon);
int CountInvSnapshots();
void ClearInvSnapshots(bool from_now = false);
void SourceDatabaseTableFromUrl(std::string table_name, std::string url);
void SourceSqlFromUrl(std::string url);
void SourceDatabaseTableFromUrl(const std::string& table_name, const std::string& url);
void SourceSqlFromUrl(const std::string& url);
private:
Mutex Mvarcache;
Mutex Mvarcache;
VarCache_Struct varcache;
/* Groups, utility methods. */
void ClearAllGroupLeaders();
void ClearAllGroups();
void ClearAllGroupLeaders();
void ClearAllGroups();
/* Raid, utility methods. */
void ClearAllRaids();
+37 -1
View File
@@ -5431,8 +5431,44 @@ ADD PRIMARY KEY (`id`);
.match = "varchar(30)",
.sql = R"(
ALTER TABLE `rule_values`
MODIFY COLUMN `rule_value` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `rule_name`;
MODIFY COLUMN `rule_value` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `rule_name`;
)"
},
ManifestEntry{
.version = 9267,
.description = "2024_02_18_group_id_bot_id.sql",
.check = "SHOW COLUMNS FROM `group_id` LIKE 'bot_id'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `group_id`
CHANGE COLUMN `groupid` `group_id` int(11) UNSIGNED NOT NULL DEFAULT 0 FIRST,
CHANGE COLUMN `charid` `character_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `group_id`,
CHANGE COLUMN `ismerc` `merc_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `name`,
ADD COLUMN `bot_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`,
MODIFY COLUMN `name` varchar(64) NOT NULL DEFAULT '' AFTER `character_id`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`group_id`, `character_id`, `bot_id`, `merc_id`) USING BTREE;
ALTER TABLE `group_id`
MODIFY COLUMN `character_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `name`;
)"
},
ManifestEntry{
.version = 9268,
.description = "2024_03_23_skill_caps.sql",
.check = "SHOW COLUMNS FROM `skill_caps` LIKE 'skill_id'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `skill_caps`
CHANGE COLUMN `skillID` `skill_id` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 FIRST,
CHANGE COLUMN `class` `class_id` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `skill_id`,
ADD COLUMN `id` int(3) UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`id`) USING BTREE,
ADD INDEX `level_skill_cap`(`skill_id`, `class_id`, `level`, `cap`);
)",
.content_schema_update = true,
}
// -- template; copy/paste this when you need to create a new entry
// ManifestEntry{
+10 -5
View File
@@ -421,20 +421,25 @@ void Database::AssignGroupToInstance(uint32 group_id, uint32 instance_id)
auto zone_id = GetInstanceZoneID(instance_id);
auto version = GetInstanceVersion(instance_id);
auto l = GroupIdRepository::GetWhere(
const auto& l = GroupIdRepository::GetWhere(
*this,
fmt::format(
"groupid = {}",
"`group_id` = {}",
group_id
)
);
if (l.empty()) {
return;
}
for (const auto& e : l) {
if (!GetInstanceID(zone_id, e.charid, version)) {
AddClientToInstance(instance_id, e.charid);
if (!e.character_id) {
continue;
}
if (!GetInstanceID(zone_id, e.character_id, version)) {
AddClientToInstance(instance_id, e.character_id);
}
}
}
@@ -504,7 +509,7 @@ void Database::FlagInstanceByRaidLeader(uint32 zone_id, int16 version, uint32 ch
return;
}
auto raid_leader_id = GetCharacterID(GetRaidLeaderName(raid_id));
auto raid_leader_id = GetCharacterID(GetRaidLeaderName(raid_id).c_str());
auto raid_leader_instance_id = GetInstanceID(zone_id, raid_leader_id, version);
if (!raid_leader_instance_id) {
+2
View File
@@ -89,6 +89,8 @@ namespace EQ
using RoF2::invslot::SLOT_INVALID;
using RoF2::invslot::SLOT_BEGIN;
using RoF2::invslot::SLOT_QUEST;
using Titanium::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE;
const int16 SLOT_AUGMENT_GENERIC_RETURN = 1001; // clients don't appear to use this method... (internal inventory return value)
+5
View File
@@ -130,6 +130,11 @@ enum CrystalReclaimTypes
Radiant = 4,
};
namespace ItemStackSizeConstraint {
constexpr int16 Minimum = 1;
constexpr int16 Maximum = 1000;
}
///////////////////////////////////////////////////////////////////////////////
+5
View File
@@ -82,6 +82,10 @@ Zone extensions and features
#define QUEST_GLOBAL_DIRECTORY "global"
#endif
// Number of quest items a Quest NPC can hold
#define MAX_NPC_QUEST_INVENTORY 24
//the min ratio at which a mob's speed is reduced
#define FLEE_HP_MINSPEED 22
//number of tics to try to run straight away before looking again
@@ -111,6 +115,7 @@ Zone extensions and features
#define SKILL_MAX_LEVEL 75
/*
Zone Numerical configuration
+2
View File
@@ -25,6 +25,8 @@ struct LootItem {
uint16 trivial_max_level;
uint16 npc_min_level;
uint16 npc_max_level;
uint8 quest;
uint8 pet;
};
typedef std::list<LootItem*> LootItems;
+2
View File
@@ -165,6 +165,8 @@ namespace RoF2
const int16 SLOT_INVALID = IINVALID;
const int16 SLOT_BEGIN = INULL;
const int16 SLOT_QUEST = 9999;
const int16 POSSESSIONS_BEGIN = slotCharm;
const int16 POSSESSIONS_END = slotCursor;
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
+2
View File
@@ -162,6 +162,8 @@ namespace RoF
const int16 SLOT_INVALID = IINVALID;
const int16 SLOT_BEGIN = INULL;
const int16 SLOT_QUEST = 9999;
const int16 POSSESSIONS_BEGIN = slotCharm;
const int16 POSSESSIONS_END = slotCursor;
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
+2
View File
@@ -152,6 +152,8 @@ namespace SoD
const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000;
const int16 SLOT_QUEST = 9999;
const int16 POSSESSIONS_BEGIN = slotCharm;
const int16 POSSESSIONS_END = slotCursor;
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
+2
View File
@@ -152,6 +152,8 @@ namespace SoF
const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000;
const int16 SLOT_QUEST = 9999;
const int16 POSSESSIONS_BEGIN = slotCharm;
const int16 POSSESSIONS_END = slotCursor;
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
+2
View File
@@ -151,6 +151,8 @@ namespace Titanium
const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000;
const int16 SLOT_QUEST = 9999;
const int16 POSSESSIONS_BEGIN = slotCharm;
const int16 POSSESSIONS_END = slotCursor;
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
+2
View File
@@ -152,6 +152,8 @@ namespace UF
const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000;
const int16 SLOT_QUEST = 9999;
const int16 POSSESSIONS_BEGIN = slotCharm;
const int16 POSSESSIONS_END = slotCursor;
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
+1 -1
View File
@@ -1379,7 +1379,7 @@ uint32 GetPlayerRaceValue(uint16 race_id) {
}
}
uint32 GetPlayerRaceBit(uint16 race_id) {
uint16 GetPlayerRaceBit(uint16 race_id) {
switch (race_id) {
case HUMAN:
return PLAYER_RACE_HUMAN_BIT;
+1 -1
View File
@@ -124,7 +124,7 @@ bool IsPlayerRace(uint16 race_id);
const std::string GetPlayerRaceAbbreviation(uint16 race_id);
uint32 GetPlayerRaceValue(uint16 race_id);
uint32 GetPlayerRaceBit(uint16 race_id);
uint16 GetPlayerRaceBit(uint16 race_id);
uint16 GetRaceIDFromPlayerRaceValue(uint32 player_race_value);
uint16 GetRaceIDFromPlayerRaceBit(uint32 player_race_bit);
+44
View File
@@ -44,7 +44,51 @@ public:
*/
// Custom extended repository methods here
static int16 GetAccountStatus(Database& db, const uint32 account_id)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT `status`, TIMESTAMPDIFF(SECOND, NOW(), `suspendeduntil`) FROM `{}` WHERE `{}` = {}",
TableName(),
PrimaryKey(),
account_id
)
);
if (!results.Success() || !results.RowCount()) {
return 0;
}
auto row = results.begin();
int16 status = static_cast<int16>(Strings::ToInt(row[0]));
int date_diff = 0;
if (row[1]) {
date_diff = Strings::ToInt(row[1]);
}
if (date_diff > 0) {
status = -1;
}
return status;
}
static bool UpdatePassword(Database& db, const uint32 account_id, const std::string& password)
{
auto results = db.QueryDatabase(
fmt::format(
"UPDATE `{}` SET `password` = MD5('{}') WHERE `{}` = {}",
TableName(),
password,
PrimaryKey(),
account_id
)
);
return results.Success();
}
};
#endif //EQEMU_ACCOUNT_REPOSITORY_H
@@ -44,7 +44,65 @@ public:
*/
// Custom extended repository methods here
static void UpdateAdventureStatsEntry(Database& db, uint32 character_id, uint8 theme_id, bool is_win, bool is_remove)
{
std::string field;
switch (theme_id) {
case LDoNThemes::GUK: {
field = "guk_";
break;
}
case LDoNThemes::MIR: {
field = "mir_";
break;
}
case LDoNThemes::MMC: {
field = "mmc_";
break;
}
case LDoNThemes::RUJ: {
field = "ruj_";
break;
}
case LDoNThemes::TAK: {
field = "tak_";
break;
}
}
field += is_win ? "wins" : "losses";
auto e = FindOne(db, character_id);
if (!e.player_id && !is_remove) {
const std::string& query = fmt::format(
"INSERT INTO `{}` SET `{}` = 1, `{}` = {}",
TableName(),
field,
PrimaryKey(),
character_id
);
db.QueryDatabase(query);
return;
}
const std::string& field_operation = is_remove ? "-" : "+";
const std::string& query = fmt::format(
"UPDATE `{}` SET `{}` = {} {} 1 WHERE `{}` = {}",
TableName(),
field,
field,
field_operation,
PrimaryKey(),
character_id
);
db.QueryDatabase(query);
}
};
#endif //EQEMU_ADVENTURE_STATS_REPOSITORY_H
@@ -22,7 +22,7 @@ public:
int32_t accid;
std::string ip;
int32_t count;
std::string lastused;
time_t lastused;
};
static std::string PrimaryKey()
@@ -46,7 +46,7 @@ public:
"accid",
"ip",
"count",
"lastused",
"UNIX_TIMESTAMP(lastused)",
};
}
@@ -130,7 +130,7 @@ public:
e.accid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.ip = row[1] ? row[1] : "";
e.count = row[2] ? static_cast<int32_t>(atoi(row[2])) : 1;
e.lastused = row[3] ? row[3] : std::time(nullptr);
e.lastused = strtoll(row[3] ? row[3] : "-1", nullptr, 10);
return e;
}
@@ -167,7 +167,7 @@ public:
v.push_back(columns[0] + " = " + std::to_string(e.accid));
v.push_back(columns[1] + " = '" + Strings::Escape(e.ip) + "'");
v.push_back(columns[2] + " = " + std::to_string(e.count));
v.push_back(columns[3] + " = '" + Strings::Escape(e.lastused) + "'");
v.push_back(columns[3] + " = FROM_UNIXTIME(" + (e.lastused > 0 ? std::to_string(e.lastused) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
@@ -192,7 +192,7 @@ public:
v.push_back(std::to_string(e.accid));
v.push_back("'" + Strings::Escape(e.ip) + "'");
v.push_back(std::to_string(e.count));
v.push_back("'" + Strings::Escape(e.lastused) + "'");
v.push_back("FROM_UNIXTIME(" + (e.lastused > 0 ? std::to_string(e.lastused) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
@@ -225,7 +225,7 @@ public:
v.push_back(std::to_string(e.accid));
v.push_back("'" + Strings::Escape(e.ip) + "'");
v.push_back(std::to_string(e.count));
v.push_back("'" + Strings::Escape(e.lastused) + "'");
v.push_back("FROM_UNIXTIME(" + (e.lastused > 0 ? std::to_string(e.lastused) : "null") + ")");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -262,7 +262,7 @@ public:
e.accid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.ip = row[1] ? row[1] : "";
e.count = row[2] ? static_cast<int32_t>(atoi(row[2])) : 1;
e.lastused = row[3] ? row[3] : std::time(nullptr);
e.lastused = strtoll(row[3] ? row[3] : "-1", nullptr, 10);
all_entries.push_back(e);
}
@@ -290,7 +290,7 @@ public:
e.accid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.ip = row[1] ? row[1] : "";
e.count = row[2] ? static_cast<int32_t>(atoi(row[2])) : 1;
e.lastused = row[3] ? row[3] : std::time(nullptr);
e.lastused = strtoll(row[3] ? row[3] : "-1", nullptr, 10);
all_entries.push_back(e);
}
@@ -368,7 +368,7 @@ public:
v.push_back(std::to_string(e.accid));
v.push_back("'" + Strings::Escape(e.ip) + "'");
v.push_back(std::to_string(e.count));
v.push_back("'" + Strings::Escape(e.lastused) + "'");
v.push_back("FROM_UNIXTIME(" + (e.lastused > 0 ? std::to_string(e.lastused) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
@@ -394,7 +394,7 @@ public:
v.push_back(std::to_string(e.accid));
v.push_back("'" + Strings::Escape(e.ip) + "'");
v.push_back(std::to_string(e.count));
v.push_back("'" + Strings::Escape(e.lastused) + "'");
v.push_back("FROM_UNIXTIME(" + (e.lastused > 0 ? std::to_string(e.lastused) : "null") + ")");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -19,34 +19,37 @@
class BaseGroupIdRepository {
public:
struct GroupId {
int32_t groupid;
int32_t charid;
uint32_t group_id;
std::string name;
int8_t ismerc;
uint32_t character_id;
uint32_t bot_id;
uint32_t merc_id;
};
static std::string PrimaryKey()
{
return std::string("groupid");
return std::string("group_id");
}
static std::vector<std::string> Columns()
{
return {
"groupid",
"charid",
"group_id",
"name",
"ismerc",
"character_id",
"bot_id",
"merc_id",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"groupid",
"charid",
"group_id",
"name",
"ismerc",
"character_id",
"bot_id",
"merc_id",
};
}
@@ -87,10 +90,11 @@ public:
{
GroupId e{};
e.groupid = 0;
e.charid = 0;
e.name = "";
e.ismerc = 0;
e.group_id = 0;
e.name = "";
e.character_id = 0;
e.bot_id = 0;
e.merc_id = 0;
return e;
}
@@ -101,7 +105,7 @@ public:
)
{
for (auto &group_id : group_ids) {
if (group_id.groupid == group_id_id) {
if (group_id.group_id == group_id_id) {
return group_id;
}
}
@@ -127,10 +131,11 @@ public:
if (results.RowCount() == 1) {
GroupId e{};
e.groupid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.charid = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
e.name = row[2] ? row[2] : "";
e.ismerc = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
e.group_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.name = row[1] ? row[1] : "";
e.character_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.bot_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.merc_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
return e;
}
@@ -164,10 +169,11 @@ public:
auto columns = Columns();
v.push_back(columns[0] + " = " + std::to_string(e.groupid));
v.push_back(columns[1] + " = " + std::to_string(e.charid));
v.push_back(columns[2] + " = '" + Strings::Escape(e.name) + "'");
v.push_back(columns[3] + " = " + std::to_string(e.ismerc));
v.push_back(columns[0] + " = " + std::to_string(e.group_id));
v.push_back(columns[1] + " = '" + Strings::Escape(e.name) + "'");
v.push_back(columns[2] + " = " + std::to_string(e.character_id));
v.push_back(columns[3] + " = " + std::to_string(e.bot_id));
v.push_back(columns[4] + " = " + std::to_string(e.merc_id));
auto results = db.QueryDatabase(
fmt::format(
@@ -175,7 +181,7 @@ public:
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.groupid
e.group_id
)
);
@@ -189,10 +195,11 @@ public:
{
std::vector<std::string> v;
v.push_back(std::to_string(e.groupid));
v.push_back(std::to_string(e.charid));
v.push_back(std::to_string(e.group_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
v.push_back(std::to_string(e.ismerc));
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.merc_id));
auto results = db.QueryDatabase(
fmt::format(
@@ -203,7 +210,7 @@ public:
);
if (results.Success()) {
e.groupid = results.LastInsertedID();
e.group_id = results.LastInsertedID();
return e;
}
@@ -222,10 +229,11 @@ public:
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.groupid));
v.push_back(std::to_string(e.charid));
v.push_back(std::to_string(e.group_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
v.push_back(std::to_string(e.ismerc));
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.merc_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -259,10 +267,11 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
GroupId e{};
e.groupid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.charid = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
e.name = row[2] ? row[2] : "";
e.ismerc = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
e.group_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.name = row[1] ? row[1] : "";
e.character_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.bot_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.merc_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
all_entries.push_back(e);
}
@@ -287,10 +296,11 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
GroupId e{};
e.groupid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.charid = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
e.name = row[2] ? row[2] : "";
e.ismerc = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
e.group_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.name = row[1] ? row[1] : "";
e.character_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.bot_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.merc_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
all_entries.push_back(e);
}
@@ -365,10 +375,11 @@ public:
{
std::vector<std::string> v;
v.push_back(std::to_string(e.groupid));
v.push_back(std::to_string(e.charid));
v.push_back(std::to_string(e.group_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
v.push_back(std::to_string(e.ismerc));
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.merc_id));
auto results = db.QueryDatabase(
fmt::format(
@@ -391,10 +402,11 @@ public:
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.groupid));
v.push_back(std::to_string(e.charid));
v.push_back(std::to_string(e.group_id));
v.push_back("'" + Strings::Escape(e.name) + "'");
v.push_back(std::to_string(e.ismerc));
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.merc_id));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -105,7 +105,7 @@ public:
e.gid = 0;
e.leadername = "";
e.marknpc = "";
e.leadershipaa = 0;
e.leadershipaa = "";
e.maintank = "";
e.assist = "";
e.puller = "";
@@ -150,7 +150,7 @@ public:
e.gid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.leadername = row[1] ? row[1] : "";
e.marknpc = row[2] ? row[2] : "";
e.leadershipaa = row[3] ? row[3] : 0;
e.leadershipaa = row[3] ? row[3] : "";
e.maintank = row[4] ? row[4] : "";
e.assist = row[5] ? row[5] : "";
e.puller = row[6] ? row[6] : "";
@@ -302,7 +302,7 @@ public:
e.gid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.leadername = row[1] ? row[1] : "";
e.marknpc = row[2] ? row[2] : "";
e.leadershipaa = row[3] ? row[3] : 0;
e.leadershipaa = row[3] ? row[3] : "";
e.maintank = row[4] ? row[4] : "";
e.assist = row[5] ? row[5] : "";
e.puller = row[6] ? row[6] : "";
@@ -335,7 +335,7 @@ public:
e.gid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
e.leadername = row[1] ? row[1] : "";
e.marknpc = row[2] ? row[2] : "";
e.leadershipaa = row[3] ? row[3] : 0;
e.leadershipaa = row[3] ? row[3] : "";
e.maintank = row[4] ? row[4] : "";
e.assist = row[5] ? row[5] : "";
e.puller = row[6] ? row[6] : "";
@@ -19,8 +19,9 @@
class BaseSkillCapsRepository {
public:
struct SkillCaps {
uint8_t skillID;
uint8_t class_;
uint32_t id;
uint8_t skill_id;
uint8_t class_id;
uint8_t level;
uint32_t cap;
uint8_t class_;
@@ -28,14 +29,15 @@ public:
static std::string PrimaryKey()
{
return std::string("skillID");
return std::string("id");
}
static std::vector<std::string> Columns()
{
return {
"skillID",
"`class`",
"id",
"skill_id",
"class_id",
"level",
"cap",
"class_",
@@ -45,8 +47,9 @@ public:
static std::vector<std::string> SelectColumns()
{
return {
"skillID",
"`class`",
"id",
"skill_id",
"class_id",
"level",
"cap",
"class_",
@@ -90,11 +93,12 @@ public:
{
SkillCaps e{};
e.skillID = 0;
e.class_ = 0;
e.level = 0;
e.cap = 0;
e.class_ = 0;
e.id = 0;
e.skill_id = 0;
e.class_id = 0;
e.level = 0;
e.cap = 0;
e.class_ = 0;
return e;
}
@@ -105,7 +109,7 @@ public:
)
{
for (auto &skill_caps : skill_capss) {
if (skill_caps.skillID == skill_caps_id) {
if (skill_caps.id == skill_caps_id) {
return skill_caps;
}
}
@@ -131,11 +135,12 @@ public:
if (results.RowCount() == 1) {
SkillCaps e{};
e.skillID = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
e.class_ = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.level = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.cap = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.class_ = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.skill_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.class_id = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.level = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.cap = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.class_ = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 0;
return e;
}
@@ -169,11 +174,11 @@ public:
auto columns = Columns();
v.push_back(columns[0] + " = " + std::to_string(e.skillID));
v.push_back(columns[1] + " = " + std::to_string(e.class_));
v.push_back(columns[2] + " = " + std::to_string(e.level));
v.push_back(columns[3] + " = " + std::to_string(e.cap));
v.push_back(columns[4] + " = " + std::to_string(e.class_));
v.push_back(columns[1] + " = " + std::to_string(e.skill_id));
v.push_back(columns[2] + " = " + std::to_string(e.class_id));
v.push_back(columns[3] + " = " + std::to_string(e.level));
v.push_back(columns[4] + " = " + std::to_string(e.cap));
v.push_back(columns[5] + " = " + std::to_string(e.class_));
auto results = db.QueryDatabase(
fmt::format(
@@ -181,7 +186,7 @@ public:
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.skillID
e.id
)
);
@@ -195,8 +200,9 @@ public:
{
std::vector<std::string> v;
v.push_back(std::to_string(e.skillID));
v.push_back(std::to_string(e.class_));
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.skill_id));
v.push_back(std::to_string(e.class_id));
v.push_back(std::to_string(e.level));
v.push_back(std::to_string(e.cap));
v.push_back(std::to_string(e.class_));
@@ -210,7 +216,7 @@ public:
);
if (results.Success()) {
e.skillID = results.LastInsertedID();
e.id = results.LastInsertedID();
return e;
}
@@ -229,8 +235,9 @@ public:
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.skillID));
v.push_back(std::to_string(e.class_));
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.skill_id));
v.push_back(std::to_string(e.class_id));
v.push_back(std::to_string(e.level));
v.push_back(std::to_string(e.cap));
v.push_back(std::to_string(e.class_));
@@ -267,11 +274,12 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
SkillCaps e{};
e.skillID = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
e.class_ = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.level = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.cap = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.class_ = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.skill_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.class_id = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.level = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.cap = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.class_ = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 0;
all_entries.push_back(e);
}
@@ -296,11 +304,12 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) {
SkillCaps e{};
e.skillID = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
e.class_ = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.level = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.cap = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.class_ = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.skill_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.class_id = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
e.level = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.cap = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.class_ = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 0;
all_entries.push_back(e);
}
@@ -375,8 +384,9 @@ public:
{
std::vector<std::string> v;
v.push_back(std::to_string(e.skillID));
v.push_back(std::to_string(e.class_));
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.skill_id));
v.push_back(std::to_string(e.class_id));
v.push_back(std::to_string(e.level));
v.push_back(std::to_string(e.cap));
v.push_back(std::to_string(e.class_));
@@ -402,8 +412,9 @@ public:
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.skillID));
v.push_back(std::to_string(e.class_));
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.skill_id));
v.push_back(std::to_string(e.class_id));
v.push_back(std::to_string(e.level));
v.push_back(std::to_string(e.cap));
v.push_back(std::to_string(e.class_));
@@ -23,7 +23,7 @@ public:
std::string varname;
std::string value;
std::string information;
std::string ts;
time_t ts;
};
static std::string PrimaryKey()
@@ -49,7 +49,7 @@ public:
"varname",
"value",
"information",
"ts",
"UNIX_TIMESTAMP(ts)",
};
}
@@ -135,7 +135,7 @@ public:
e.varname = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : "";
e.information = row[3] ? row[3] : "";
e.ts = row[4] ? row[4] : std::time(nullptr);
e.ts = strtoll(row[4] ? row[4] : "-1", nullptr, 10);
return e;
}
@@ -172,7 +172,7 @@ public:
v.push_back(columns[1] + " = '" + Strings::Escape(e.varname) + "'");
v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'");
v.push_back(columns[3] + " = '" + Strings::Escape(e.information) + "'");
v.push_back(columns[4] + " = '" + Strings::Escape(e.ts) + "'");
v.push_back(columns[4] + " = FROM_UNIXTIME(" + (e.ts > 0 ? std::to_string(e.ts) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
@@ -198,7 +198,7 @@ public:
v.push_back("'" + Strings::Escape(e.varname) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back("'" + Strings::Escape(e.information) + "'");
v.push_back("'" + Strings::Escape(e.ts) + "'");
v.push_back("FROM_UNIXTIME(" + (e.ts > 0 ? std::to_string(e.ts) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
@@ -232,7 +232,7 @@ public:
v.push_back("'" + Strings::Escape(e.varname) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back("'" + Strings::Escape(e.information) + "'");
v.push_back("'" + Strings::Escape(e.ts) + "'");
v.push_back("FROM_UNIXTIME(" + (e.ts > 0 ? std::to_string(e.ts) : "null") + ")");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -270,7 +270,7 @@ public:
e.varname = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : "";
e.information = row[3] ? row[3] : "";
e.ts = row[4] ? row[4] : std::time(nullptr);
e.ts = strtoll(row[4] ? row[4] : "-1", nullptr, 10);
all_entries.push_back(e);
}
@@ -299,7 +299,7 @@ public:
e.varname = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : "";
e.information = row[3] ? row[3] : "";
e.ts = row[4] ? row[4] : std::time(nullptr);
e.ts = strtoll(row[4] ? row[4] : "-1", nullptr, 10);
all_entries.push_back(e);
}
@@ -378,7 +378,7 @@ public:
v.push_back("'" + Strings::Escape(e.varname) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back("'" + Strings::Escape(e.information) + "'");
v.push_back("'" + Strings::Escape(e.ts) + "'");
v.push_back("FROM_UNIXTIME(" + (e.ts > 0 ? std::to_string(e.ts) : "null") + ")");
auto results = db.QueryDatabase(
fmt::format(
@@ -405,7 +405,7 @@ public:
v.push_back("'" + Strings::Escape(e.varname) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back("'" + Strings::Escape(e.information) + "'");
v.push_back("'" + Strings::Escape(e.ts) + "'");
v.push_back("FROM_UNIXTIME(" + (e.ts > 0 ? std::to_string(e.ts) : "null") + ")");
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
@@ -59,6 +59,24 @@ public:
return NewEntity();
}
// insert with ON DUPLICATE KEY UPDATE to leave rows that exist unchanged
static int InsertUpdateMany(Database& db, const std::vector<CharRecipeList>& entries)
{
std::vector<std::string> values;
values.reserve(entries.size());
for (const auto& e: entries)
{
values.emplace_back(fmt::format("({},{},{})", e.char_id, e.recipe_id, e.madecount));
}
auto results = db.QueryDatabase(fmt::format(
"INSERT INTO {0} (char_id, recipe_id, madecount) VALUES {1} ON DUPLICATE KEY UPDATE {2}={2}",
TableName(), fmt::join(values, ","), PrimaryKey()));
return results.Success() ? results.RowsAffected() : 0;
}
};
#endif //EQEMU_CHAR_RECIPE_LIST_REPOSITORY_H
+9 -1
View File
@@ -44,7 +44,15 @@ public:
*/
// Custom extended repository methods here
static void ClearAllGroups(Database& db)
{
db.QueryDatabase(
fmt::format(
"DELETE FROM `{}`",
TableName()
)
);
}
};
#endif //EQEMU_GROUP_ID_REPOSITORY_H
@@ -44,7 +44,15 @@ public:
*/
// Custom extended repository methods here
static void ClearAllGroupLeaders(Database& db)
{
db.QueryDatabase(
fmt::format(
"DELETE FROM `{}`",
TableName()
)
);
}
};
#endif //EQEMU_GROUP_LEADERS_REPOSITORY_H
@@ -191,4 +191,5 @@ public:
return UpdateOne(db, m);
}
};
#endif //EQEMU_GUILD_MEMBERS_REPOSITORY_H
@@ -44,7 +44,30 @@ public:
*/
// Custom extended repository methods here
static int64 CountInventorySnapshots(Database& db)
{
const std::string& query = "SELECT COUNT(*) FROM (SELECT * FROM `inventory_snapshots` a GROUP BY `charid`, `time_index`) b";
auto results = db.QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
return -1;
}
auto row = results.begin();
const int64 count = Strings::ToBigInt(row[0]);
if (count > std::numeric_limits<int>::max()) {
return -2;
}
if (count < 0) {
return -3;
}
return count;
}
};
#endif //EQEMU_INVENTORY_SNAPSHOTS_REPOSITORY_H
@@ -66,6 +66,16 @@ public:
return results.Success() ? results.RowsAffected() : 0;
}
static void ClearAllRaidDetails(Database& db)
{
db.QueryDatabase(
fmt::format(
"DELETE FROM `{}`",
TableName()
)
);
}
};
#endif //EQEMU_RAID_DETAILS_REPOSITORY_H
@@ -1,297 +0,0 @@
#ifndef EQEMU_RAID_LEADERS_REPOSITORY_H
#define EQEMU_RAID_LEADERS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
class RaidLeadersRepository {
public:
struct RaidLeaders {
int gid;
int rid;
std::string marknpc;
std::string maintank;
std::string assist;
std::string puller;
std::string leadershipaa;
std::string mentoree;
int mentor_percent;
};
static std::string PrimaryKey()
{
return std::string("");
}
static std::vector<std::string> Columns()
{
return {
"gid",
"rid",
"marknpc",
"maintank",
"assist",
"puller",
"leadershipaa",
"mentoree",
"mentor_percent",
};
}
static std::string ColumnsRaw()
{
return std::string(Strings::Implode(", ", Columns()));
}
static std::string InsertColumnsRaw()
{
std::vector<std::string> insert_columns;
for (auto &column : Columns()) {
if (column == PrimaryKey()) {
continue;
}
insert_columns.push_back(column);
}
return std::string(Strings::Implode(", ", insert_columns));
}
static std::string TableName()
{
return std::string("raid_leaders");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
ColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
InsertColumnsRaw()
);
}
static RaidLeaders NewEntity()
{
RaidLeaders entry{};
entry.gid = 0;
entry.rid = 0;
entry.marknpc = "";
entry.maintank = "";
entry.assist = "";
entry.puller = "";
entry.leadershipaa = 0;
entry.mentoree = "";
entry.mentor_percent = 0;
return entry;
}
static RaidLeaders GetRaidLeadersEntry(
const std::vector<RaidLeaders> &raid_leaderss,
int raid_leaders_id
)
{
for (auto &raid_leaders : raid_leaderss) {
if (raid_leaders. == raid_leaders_id) {
return raid_leaders;
}
}
return NewEntity();
}
static RaidLeaders FindOne(
int raid_leaders_id
)
{
auto results = database.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
BaseSelect(),
raid_leaders_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
RaidLeaders entry{};
entry.gid = atoi(row[0]);
entry.rid = atoi(row[1]);
entry.marknpc = row[2];
entry.maintank = row[3];
entry.assist = row[4];
entry.puller = row[5];
entry.leadershipaa = row[6];
entry.mentoree = row[7];
entry.mentor_percent = atoi(row[8]);
return entry;
}
return NewEntity();
}
static int DeleteOne(
int raid_leaders_id
)
{
auto results = database.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
raid_leaders_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
RaidLeaders raid_leaders_entry
)
{
std::vector<std::string> update_values;
auto columns = Columns();
update_values.push_back(columns[0] + " = " + std::to_string(raid_leaders_entry.gid));
update_values.push_back(columns[1] + " = " + std::to_string(raid_leaders_entry.rid));
update_values.push_back(columns[2] + " = '" + Strings::Escape(raid_leaders_entry.marknpc) + "'");
update_values.push_back(columns[3] + " = '" + Strings::Escape(raid_leaders_entry.maintank) + "'");
update_values.push_back(columns[4] + " = '" + Strings::Escape(raid_leaders_entry.assist) + "'");
update_values.push_back(columns[5] + " = '" + Strings::Escape(raid_leaders_entry.puller) + "'");
update_values.push_back(columns[6] + " = '" + Strings::Escape(raid_leaders_entry.leadershipaa) + "'");
update_values.push_back(columns[7] + " = '" + Strings::Escape(raid_leaders_entry.mentoree) + "'");
update_values.push_back(columns[8] + " = " + std::to_string(raid_leaders_entry.mentor_percent));
auto results = database.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", update_values),
PrimaryKey(),
raid_leaders_entry.
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static RaidLeaders InsertOne(
RaidLeaders raid_leaders_entry
)
{
std::vector<std::string> insert_values;
insert_values.push_back(std::to_string(raid_leaders_entry.gid));
insert_values.push_back(std::to_string(raid_leaders_entry.rid));
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.marknpc) + "'");
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.maintank) + "'");
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.assist) + "'");
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.puller) + "'");
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.leadershipaa) + "'");
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.mentoree) + "'");
insert_values.push_back(std::to_string(raid_leaders_entry.mentor_percent));
auto results = database.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", insert_values)
)
);
if (results.Success()) {
raid_leaders_entry.id = results.LastInsertedID();
return raid_leaders_entry;
}
raid_leaders_entry = InstanceListRepository::NewEntity();
return raid_leaders_entry;
}
static int InsertMany(
std::vector<RaidLeaders> raid_leaders_entries
)
{
std::vector<std::string> insert_chunks;
for (auto &raid_leaders_entry: raid_leaders_entries) {
std::vector<std::string> insert_values;
insert_values.push_back(std::to_string(raid_leaders_entry.gid));
insert_values.push_back(std::to_string(raid_leaders_entry.rid));
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.marknpc) + "'");
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.maintank) + "'");
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.assist) + "'");
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.puller) + "'");
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.leadershipaa) + "'");
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.mentoree) + "'");
insert_values.push_back(std::to_string(raid_leaders_entry.mentor_percent));
insert_chunks.push_back("(" + Strings::Implode(",", insert_values) + ")");
}
std::vector<std::string> insert_values;
auto results = database.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<RaidLeaders> All()
{
std::vector<RaidLeaders> all_entries;
auto results = database.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
RaidLeaders entry{};
entry.gid = atoi(row[0]);
entry.rid = atoi(row[1]);
entry.marknpc = row[2];
entry.maintank = row[3];
entry.assist = row[4];
entry.puller = row[5];
entry.leadershipaa = row[6];
entry.mentoree = row[7];
entry.mentor_percent = atoi(row[8]);
all_entries.push_back(entry);
}
return all_entries;
}
};
#endif //EQEMU_RAID_LEADERS_REPOSITORY_H
+11 -1
View File
@@ -60,7 +60,7 @@ public:
);
return results.Success() ? results.RowsAffected() : 0;
}
static int UpdateRaidAssister(
Database& db,
int32_t raid_id,
@@ -98,5 +98,15 @@ public:
return results.Success() ? results.RowsAffected() : 0;
}
static void ClearAllRaids(Database& db)
{
db.QueryDatabase(
fmt::format(
"DELETE FROM `{}`",
TableName()
)
);
}
};
#endif //EQEMU_RAID_MEMBERS_REPOSITORY_H
+4 -1
View File
@@ -329,6 +329,7 @@ RULE_BOOL(World, UseItemLinksForKeyRing, false, "Uses item links for Key Ring Li
RULE_BOOL(World, UseOldShadowKnightClassExport, true, "Disable to have Shadowknight show as Shadow Knight (live-like)")
RULE_STRING(World, IPExemptionZones, "", "Comma-delimited list of zones to exclude from IP-limit checks. Empty string to disable.")
RULE_STRING(World, MOTD, "", "Server MOTD sent on login, change from empty to have this be used instead of variables table 'motd' value")
RULE_STRING(World, Rules, "", "Server Rules, change from empty to have this be used instead of variables table 'rules' value, lines are pipe (|) separated, example: A|B|C")
RULE_CATEGORY_END()
RULE_CATEGORY(Zone)
@@ -354,6 +355,7 @@ RULE_INT(Zone, GlobalLootMultiplier, 1, "Sets Global Loot drop multiplier for da
RULE_BOOL(Zone, KillProcessOnDynamicShutdown, true, "When process has booted a zone and has hit its zone shut down timer, it will hard kill the process to free memory back to the OS")
RULE_INT(Zone, SpawnEventMin, 3, "When strict is set in spawn_events, specifies the max EQ minutes into the trigger hour a spawn_event will fire. Going below 3 may cause the spawn_event to not fire.")
RULE_INT(Zone, ForageChance, 25, "Chance of foraging from zone table vs global table")
RULE_INT(Zone, FishingChance, 399, "Chance of fishing from zone table vs global table")
RULE_BOOL(Zone, AllowCrossZoneSpellsOnBots, false, "Set to true to allow cross zone spells (cast/remove) to affect bots")
RULE_BOOL(Zone, AllowCrossZoneSpellsOnMercs, false, "Set to true to allow cross zone spells (cast/remove) to affect mercenaries")
RULE_BOOL(Zone, AllowCrossZoneSpellsOnPets, false, "Set to true to allow cross zone spells (cast/remove) to affect pets")
@@ -449,7 +451,8 @@ RULE_BOOL(Spells, Jun182014HundredHandsRevamp, false, "This should be true for i
RULE_BOOL(Spells, SwarmPetTargetLock, false, "Use old method of swarm pets target locking till target dies then despawning")
RULE_BOOL(Spells, NPC_UseFocusFromSpells, true, "Allow NPC to use most spell derived focus effects")
RULE_BOOL(Spells, NPC_UseFocusFromItems, false, "Allow NPC to use most item derived focus effects")
RULE_BOOL(Spells, UseAdditiveFocusFromWornSlot, false, "Allows an additive focus effect to be calculated from worn slot")
RULE_BOOL(Spells, UseAdditiveFocusFromWornSlot, false, "Allows an additive focus effect to be calculated from worn slot. Does not apply limits checks. Can only have one additive focus rule be true.")
RULE_BOOL(Spells, UseAdditiveFocusFromWornSlotWithLimits, false, "Allows an additive focus effect to be calculated from worn slot. Applies normal limit checks. Can only have one additive focus rule be true.")
RULE_BOOL(Spells, AlwaysSendTargetsBuffs, false, "Ignore Leadership Alternate Abilities level if true")
RULE_BOOL(Spells, FlatItemExtraSpellAmt, false, "Allow SpellDmg stat to affect all spells, regardless of cast time/cooldown/etc")
RULE_BOOL(Spells, IgnoreSpellDmgLvlRestriction, false, "Ignore the 5 level spread on applying SpellDmg")
+4 -3
View File
@@ -348,7 +348,7 @@ std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message)
if (ch != startpos)
{
std::string str(startpos, ch - startpos);
new_message += EQ::SayLinkEngine::GenerateQuestSaylink(str, false, str);
new_message += Saylink::Create(str);
}
in_bracket_state = false;
}
@@ -417,11 +417,12 @@ SaylinkRepository::Saylink EQ::SayLinkEngine::GetOrSaveSaylink(std::string sayli
return {};
}
std::string Saylink::Create(const std::string &saylink_text, bool silent, const std::string &link_name)
std::string Saylink::Create(const std::string& saylink_text, bool silent, const std::string& link_name)
{
return EQ::SayLinkEngine::GenerateQuestSaylink(saylink_text, silent, (link_name.empty() ? saylink_text : link_name));
}
std::string Saylink::Silent(const std::string &saylink_text, const std::string &link_name) {
std::string Saylink::Silent(const std::string& saylink_text, const std::string& link_name)
{
return EQ::SayLinkEngine::GenerateQuestSaylink(saylink_text, true, (link_name.empty() ? saylink_text : link_name));
}
+1 -1
View File
@@ -130,7 +130,7 @@ namespace EQ
class Saylink {
public:
static std::string Create(const std::string &saylink_text, bool silent, const std::string &link_name = "");
static std::string Create(const std::string &saylink_text, bool silent = false, const std::string &link_name = "");
static std::string Silent(const std::string &saylink_text, const std::string &link_name = "");
};
+1
View File
@@ -273,6 +273,7 @@
#define ServerOP_ReloadFactions 0x4126
#define ServerOP_ReloadLoot 0x4127
#define ServerOP_ReloadBaseData 0x4128
#define ServerOP_ReloadSkillCaps 0x4129
#define ServerOP_CZDialogueWindow 0x4500
#define ServerOP_CZLDoNUpdate 0x4501
+1 -1
View File
@@ -60,7 +60,7 @@ SharedTaskRequest SharedTask::GetRequestCharacters(Database &db, uint32_t reques
request.group_type = SharedTaskRequestGroupType::Group;
auto characters = CharacterDataRepository::GetWhere(
db, fmt::format(
"id IN (select charid from group_id where groupid = (select groupid from group_id where charid = {}))",
"id IN (select character_id from group_id where group_id = (select group_id from group_id where character_id = {}))",
requested_character_id
)
);
+4 -134
View File
@@ -45,6 +45,7 @@
#include "repositories/loottable_repository.h"
#include "repositories/character_item_recast_repository.h"
#include "repositories/character_corpses_repository.h"
#include "repositories/skill_caps_repository.h"
namespace ItemField
{
@@ -793,8 +794,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
}
if (cv_conflict) {
char char_name[64] = "";
GetCharName(char_id, char_name);
const std::string& char_name = GetCharName(char_id);
LogError("ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])",
char_name,
char_id,
@@ -1626,136 +1626,6 @@ bool SharedDatabase::GetCommandSubSettings(std::vector<CommandSubsettingsReposit
return true;
}
bool SharedDatabase::LoadSkillCaps(const std::string &prefix) {
skill_caps_mmf.reset(nullptr);
try {
const auto Config = EQEmuConfig::get();
EQ::IPCMutex mutex("skill_caps");
mutex.Lock();
std::string file_name = fmt::format("{}/{}{}", path.GetSharedMemoryPath(), prefix, std::string("skill_caps"));
LogInfo("Loading [{}]", file_name);
skill_caps_mmf = std::make_unique<EQ::MemoryMappedFile>(file_name);
LogInfo("Loaded skill caps via shared memory");
mutex.Unlock();
} catch(std::exception &ex) {
LogError("Error loading skill caps: {}", ex.what());
return false;
}
return true;
}
void SharedDatabase::LoadSkillCaps(void *data) {
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);
const std::string query = "SELECT skillID, class, level, cap FROM skill_caps ORDER BY skillID, class, level";
auto results = QueryDatabase(query);
if (!results.Success()) {
LogError("Error loading skill caps from database: {}", results.ErrorMessage().c_str());
return;
}
for(auto& row = results.begin(); row != results.end(); ++row) {
const uint8 skillID = Strings::ToUnsignedInt(row[0]);
const uint8 class_ = Strings::ToUnsignedInt(row[1]) - 1;
const uint8 level = Strings::ToUnsignedInt(row[2]);
const uint16 cap = Strings::ToUnsignedInt(row[3]);
if(skillID >= skill_count || class_ >= class_count || level >= level_count)
continue;
const uint32 index = (((class_ * skill_count) + skillID) * level_count) + level;
skill_caps_table[index] = cap;
}
}
uint16 SharedDatabase::GetSkillCap(uint8 Class_, EQ::skills::SkillType Skill, uint8 Level) const
{
if(!skill_caps_mmf) {
return 0;
}
if(Class_ == 0)
return 0;
int SkillMaxLevel = RuleI(Character, SkillCapMaxLevel);
if(SkillMaxLevel < 1) {
SkillMaxLevel = RuleI(Character, MaxLevel);
}
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) {
return 0;
}
if(Level > static_cast<uint8>(SkillMaxLevel)){
Level = static_cast<uint8>(SkillMaxLevel);
}
const uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count) + Level;
const uint16 *skill_caps_table = static_cast<uint16*>(skill_caps_mmf->Get());
return skill_caps_table[index];
}
uint8 SharedDatabase::GetTrainLevel(uint8 Class_, EQ::skills::SkillType Skill, uint8 Level) const
{
if(!skill_caps_mmf) {
return 0;
}
if(Class_ == 0)
return 0;
int SkillMaxLevel = RuleI(Character, SkillCapMaxLevel);
if (SkillMaxLevel < 1) {
SkillMaxLevel = RuleI(Character, MaxLevel);
}
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) {
return 0;
}
uint8 ret = 0;
if(Level > static_cast<uint8>(SkillMaxLevel)) {
const uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count);
const uint16 *skill_caps_table = static_cast<uint16*>(skill_caps_mmf->Get());
for(uint8 x = 0; x < Level; x++){
if(skill_caps_table[index + x]){
ret = x;
break;
}
}
}
else
{
const uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count);
const uint16 *skill_caps_table = static_cast<uint16*>(skill_caps_mmf->Get());
for(int x = 0; x < SkillMaxLevel; x++){
if(skill_caps_table[index + x]){
ret = x;
break;
}
}
}
if(ret > GetSkillCap(Class_, Skill, Level))
ret = static_cast<uint8>(GetSkillCap(Class_, Skill, Level));
return ret;
}
void SharedDatabase::LoadDamageShieldTypes(SPDat_Spell_Struct* sp, int32 iMaxSpellID) {
const std::string query = StringFormat("SELECT `spellid`, `type` FROM `damageshieldtypes` WHERE `spellid` > 0 "
"AND `spellid` <= %i", iMaxSpellID);
@@ -1984,9 +1854,9 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
sp[tempid].min_range = Strings::ToFloat(row[231]);
sp[tempid].no_remove = Strings::ToBool(row[232]);
sp[tempid].damage_shield_type = 0;
}
}
LoadDamageShieldTypes(sp, max_spells);
LoadDamageShieldTypes(sp, max_spells);
}
void SharedDatabase::LoadCharacterInspectMessage(uint32 character_id, InspectMessage_Struct* message) {
-8
View File
@@ -160,14 +160,6 @@ public:
uint32 GetSharedItemsCount() { return m_shared_items_count; }
uint32 GetItemsCount();
/**
* skills
*/
void LoadSkillCaps(void *data);
bool LoadSkillCaps(const std::string &prefix);
uint16 GetSkillCap(uint8 Class_, EQ::skills::SkillType Skill, uint8 Level) const;
uint8 GetTrainLevel(uint8 Class_, EQ::skills::SkillType Skill, uint8 Level) const;
/**
* spells
*/
+91
View File
@@ -0,0 +1,91 @@
#include "skill_caps.h"
SkillCaps *SkillCaps::SetContentDatabase(Database *db)
{
m_content_database = db;
return this;
}
SkillCapsRepository::SkillCaps SkillCaps::GetSkillCap(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level)
{
if (!IsPlayerClass(class_id)) {
return SkillCapsRepository::NewEntity();
}
for (const auto &e: m_skill_caps) {
if (
e.class_id == class_id &&
e.level == level &&
static_cast<EQ::skills::SkillType>(e.skill_id) == skill_id
) {
return e;
}
}
return SkillCapsRepository::NewEntity();
}
uint8 SkillCaps::GetTrainLevel(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level)
{
if (
!IsPlayerClass(class_id) ||
class_id > Class::PLAYER_CLASS_COUNT ||
static_cast<uint32>(skill_id) > (EQ::skills::HIGHEST_SKILL + 1)
) {
return 0;
}
const uint8 skill_cap_max_level = (
RuleI(Character, SkillCapMaxLevel) > 0 ?
RuleI(Character, SkillCapMaxLevel) :
RuleI(Character, MaxLevel)
);
const uint8 max_level = level > skill_cap_max_level ? level : skill_cap_max_level;
for (const auto &e: m_skill_caps) {
for (uint8 current_level = 1; current_level <= max_level; current_level++) {
if (
e.class_id == class_id &&
static_cast<EQ::skills::SkillType>(e.skill_id) == skill_id &&
e.level == current_level
) {
return current_level;
}
}
}
return 0;
}
void SkillCaps::LoadSkillCaps()
{
const auto &l = SkillCapsRepository::All(*m_content_database);
m_skill_caps.reserve(l.size());
for (const auto &e: l) {
if (
e.level < 1 ||
!IsPlayerClass(e.class_id) ||
static_cast<EQ::skills::SkillType>(e.skill_id) >= EQ::skills::SkillCount
) {
continue;
}
m_skill_caps.emplace_back(e);
}
LogInfo(
"Loaded [{}] Skill Cap Entr{}",
l.size(),
l.size() != 1 ? "ies" : "y"
);
}
void SkillCaps::ReloadSkillCaps()
{
ClearSkillCaps();
LoadSkillCaps();
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef CODE_SKILL_CAPS_H
#define CODE_SKILL_CAPS_H
#include "repositories/skill_caps_repository.h"
#include "types.h"
#include "classes.h"
#include "skills.h"
class SkillCaps {
public:
inline void ClearSkillCaps() { m_skill_caps.clear(); }
SkillCapsRepository::SkillCaps GetSkillCap(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level);
uint8 GetTrainLevel(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level);
void LoadSkillCaps();
void ReloadSkillCaps();
SkillCaps *SetContentDatabase(Database *db);
private:
Database *m_content_database{};
std::vector<SkillCapsRepository::SkillCaps> m_skill_caps = {};
};
extern SkillCaps skill_caps;
#endif //CODE_SKILL_CAPS_H
+1 -1
View File
@@ -18,10 +18,10 @@
*/
#include "skills.h"
#include "classes.h"
#include <string.h>
bool EQ::skills::IsTradeskill(SkillType skill)
{
switch (skill) {
-1
View File
@@ -26,7 +26,6 @@
#include <map>
#include <vector>
namespace EQ
{
namespace skills {
+1 -1
View File
@@ -1257,7 +1257,7 @@ typedef enum {
#define SE_Ff_Override_NotFocusable 460 // implemented, @Fc, Allow spell to be focused event if flagged with 'not_focusable' in spell table, base: 1
#define SE_ImprovedDamage2 461 // implemented, @Fc, On Caster, spell damage mod pct, base: min pct, limit: max pct
#define SE_FcDamageAmt2 462 // implemented, @Fc, On Caster, spell damage mod flat amt, base: amt
//#define SE_Shield_Target 463 //
#define SE_Shield_Target 463 // implemented, Base1 % damage shielded on target
#define SE_PC_Pet_Rampage 464 // implemented - Base1 % chance to do rampage for base2 % of damage each melee round
#define SE_PC_Pet_AE_Rampage 465 // implemented - Base1 % chance to do AE rampage for base2 % of damage each melee round
#define SE_PC_Pet_Flurry_Chance 466 // implemented - Base1 % chance to do flurry from double attack hit.
+2 -2
View File
@@ -25,7 +25,7 @@
// Build variables
// these get injected during the build pipeline
#define CURRENT_VERSION "22.47.0-dev" // always append -dev to the current version for custom-builds
#define CURRENT_VERSION "22.48.0-dev" // always append -dev to the current version for custom-builds
#define LOGIN_VERSION "0.8.0"
#define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__
@@ -42,7 +42,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9266
#define CURRENT_BINARY_DATABASE_VERSION 9268
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9043
#endif
+225 -891
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "eqemu-server",
"version": "22.47.0",
"version": "22.48.0",
"repository": {
"type": "git",
"url": "https://github.com/EQEmu/Server.git"
+28
View File
@@ -0,0 +1,28 @@
# Description
Please include a summary of the changes and the related issue (Why is this change necessary). Please also include relevant motivation and context. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# Testing
Attach images and describe testing done to validate functionality.
Clients tested:
# Checklist
- [ ] I have tested my changes
- [ ] I have performed a self-review of my code. Ensuring variables, functions and methods are named in a human-readable way, comments are added only where naming of variables, functions and methods can't give enough context.
- [ ] I have made corresponding changes to the documentation (if applicable, if not delete this line)
- [ ] I own the changes of my code and take responsibility for the potential issues that occur
- [ ] If my changes make database schema changes, I have tested the changes on a local database (attach image). Updated version.h CURRENT_BINARY_DATABASE_VERSION to the new version. (Delete this if not applicable)
-2
View File
@@ -4,13 +4,11 @@ SET(shared_memory_sources
items.cpp
main.cpp
spells.cpp
skill_caps.cpp
)
SET(shared_memory_headers
items.h
spells.h
skill_caps.h
)
ADD_EXECUTABLE(shared_memory ${shared_memory_sources} ${shared_memory_headers})
+1 -17
View File
@@ -28,7 +28,6 @@
#include "../common/eqemu_exception.h"
#include "../common/strings.h"
#include "items.h"
#include "skill_caps.h"
#include "spells.h"
#include "../common/content/world_content_service.h"
#include "../common/zone_store.h"
@@ -183,7 +182,6 @@ int main(int argc, char **argv)
bool load_all = true;
bool load_items = false;
bool load_loot = false;
bool load_skill_caps = false;
bool load_spells = false;
if (argc > 1) {
@@ -197,11 +195,7 @@ int main(int argc, char **argv)
break;
case 's':
if (strcasecmp("skill_caps", argv[i]) == 0) {
load_skill_caps = true;
load_all = false;
}
else if (strcasecmp("spells", argv[i]) == 0) {
if (strcasecmp("spells", argv[i]) == 0) {
load_spells = true;
load_all = false;
}
@@ -236,16 +230,6 @@ int main(int argc, char **argv)
}
}
if (load_all || load_skill_caps) {
LogInfo("Loading skill caps");
try {
LoadSkillCaps(&content_db, hotfix_name);
} catch (std::exception &ex) {
LogError("{}", ex.what());
return 1;
}
}
if (load_all || load_spells) {
LogInfo("Loading spells");
try {
-45
View File
@@ -1,45 +0,0 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2013 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "skill_caps.h"
#include "../common/global_define.h"
#include "../common/shareddb.h"
#include "../common/ipc_mutex.h"
#include "../common/memory_mapped_file.h"
#include "../common/eqemu_exception.h"
#include "../common/classes.h"
#include "../common/features.h"
void LoadSkillCaps(SharedDatabase *database, const std::string &prefix) {
EQ::IPCMutex mutex("skill_caps");
mutex.Lock();
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));
auto Config = EQEmuConfig::get();
std::string file_name = Config->SharedMemDir + prefix + std::string("skill_caps");
EQ::MemoryMappedFile mmf(file_name, size);
mmf.ZeroFile();
void *ptr = mmf.Get();
database->LoadSkillCaps(ptr);
mutex.Unlock();
}
-29
View File
@@ -1,29 +0,0 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2013 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __EQEMU_SHARED_MEMORY_SKILL_CAPS_H
#define __EQEMU_SHARED_MEMORY_SKILL_CAPS_H
#include <string>
#include "../common/eqemu_config.h"
class SharedDatabase;
void LoadSkillCaps(SharedDatabase *database, const std::string &prefix);
#endif
@@ -280,7 +280,7 @@ foreach my $table_to_generate (@tables) {
# column names (string)
$column_names_quoted .= sprintf("\t\t\t\"%s\",\n", format_column_name_for_mysql($column_name));
if ($data_type =~ /datetime/) {
if ($data_type =~ /datetime|timestamp/) {
$select_column_names_quoted .= sprintf("\t\t\t\"UNIX_TIMESTAMP(%s)\",\n", format_column_name_for_mysql($column_name));
}
else {
@@ -293,7 +293,7 @@ foreach my $table_to_generate (@tables) {
if ($data_type =~ /int|float|double|decimal/) {
$query_value = sprintf('" + std::to_string(e.%s));', $column_name_formatted);
}
elsif ($data_type =~ /datetime/) {
elsif ($data_type =~ /datetime|timestamp/) {
$query_value = sprintf('FROM_UNIXTIME(" + (e.%s > 0 ? std::to_string(e.%s) : "null") + ")");', $column_name_formatted, $column_name_formatted);
}
@@ -309,7 +309,7 @@ foreach my $table_to_generate (@tables) {
if ($data_type =~ /int|float|double|decimal/) {
$value = sprintf('std::to_string(e.%s)', $column_name_formatted);
}
elsif ($data_type =~ /datetime/) {
elsif ($data_type =~ /datetime|timestamp/) {
$value = sprintf('"FROM_UNIXTIME(" + (e.%s > 0 ? std::to_string(e.%s) : "null") + ")"', $column_name_formatted, $column_name_formatted);
}
@@ -332,7 +332,7 @@ foreach my $table_to_generate (@tables) {
$all_entries .= sprintf("\t\t\te.%-${longest_column_length}s = row[%s] ? strtoll(row[%s], nullptr, 10) : %s;\n", $column_name_formatted, $index, $index, $default_value);
$find_one_entries .= sprintf("\t\t\te.%-${longest_column_length}s = row[%s] ? strtoll(row[%s], nullptr, 10) : %s;\n", $column_name_formatted, $index, $index, $default_value);
}
elsif ($data_type =~ /datetime/) {
elsif ($data_type =~ /datetime|timestamp/) {
$all_entries .= sprintf("\t\t\te.%-${longest_column_length}s = strtoll(row[%s] ? row[%s] : \"-1\", nullptr, 10);\n", $column_name_formatted, $index, $index);
$find_one_entries .= sprintf("\t\t\te.%-${longest_column_length}s = strtoll(row[%s] ? row[%s] : \"-1\", nullptr, 10);\n", $column_name_formatted, $index, $index);
}
@@ -591,7 +591,7 @@ sub translate_mysql_data_type_to_c
elsif ($mysql_data_type =~ /double/) {
$struct_data_type = 'double';
}
elsif ($mysql_data_type =~ /datetime/) {
elsif ($mysql_data_type =~ /datetime|timestamp/) {
$struct_data_type = 'time_t';
}
+40 -12
View File
@@ -53,6 +53,9 @@
#include "../common/repositories/inventory_repository.h"
#include "../common/events/player_event_logs.h"
#include "../common/content/world_content_service.h"
#include "../common/repositories/group_id_repository.h"
#include "../common/repositories/character_data_repository.h"
#include "../common/skill_caps.h"
#include <iostream>
#include <iomanip>
@@ -190,9 +193,10 @@ bool Client::CanTradeFVNoDropItem()
void Client::SendEnterWorld(std::string name)
{
char char_name[64] = { 0 };
if (is_player_zoning && database.GetLiveChar(GetAccountID(), char_name)) {
if(database.GetAccountIDByChar(char_name) != GetAccountID()) {
std::string live_name {};
if (is_player_zoning) {
live_name = database.GetLiveChar(GetAccountID());
if(database.GetAccountIDByChar(live_name) != GetAccountID()) {
eqs->Close();
return;
} else {
@@ -200,8 +204,8 @@ void Client::SendEnterWorld(std::string name)
}
}
auto outapp = new EQApplicationPacket(OP_EnterWorld, strlen(char_name) + 1);
memcpy(outapp->pBuffer,char_name,strlen(char_name)+1);
auto outapp = new EQApplicationPacket(OP_EnterWorld, live_name.length() + 1);
memcpy(outapp->pBuffer, live_name.c_str(), live_name.length() + 1);
QueuePacket(outapp);
safe_delete(outapp);
}
@@ -764,9 +768,15 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
auto ew = (EnterWorld_Struct *) app->pBuffer;
strn0cpy(char_name, ew->name, sizeof(char_name));
uint32 temporary_account_id = 0;
charid = database.GetCharacterInfo(char_name, &temporary_account_id, &zone_id, &instance_id);
if (!charid) {
const auto& l = CharacterDataRepository::GetWhere(
database,
fmt::format(
"`name` = '{}' LIMIT 1",
Strings::Escape(char_name)
)
);
if (l.empty()) {
LogInfo("Could not get CharInfo for [{}]", char_name);
eqs->Close();
return true;
@@ -784,13 +794,24 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
instance_id = r.instance.id;
}
const auto& e = l.front();
// Make sure this account owns this character
if (temporary_account_id != account_id) {
LogInfo("Account [{}] does not own the character named [{}] from account [{}]", account_id, char_name, temporary_account_id);
if (e.account_id != account_id) {
LogInfo(
"Account [{}] does not own the character named [{}] from account [{}]",
account_id,
char_name,
e.account_id
);
eqs->Close();
return true;
}
charid = e.id;
zone_id = e.zone_id;
instance_id = e.zone_instance;
// This can probably be moved outside and have another method return requested info (don't forget to remove the #include "../common/shareddb.h" above)
// (This is a literal translation of the original process..I don't see why it can't be changed to a single-target query over account iteration)
if (!is_player_zoning) {
@@ -881,7 +902,14 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
}
if(!is_player_zoning) {
database.SetGroupID(char_name, 0, charid);
GroupIdRepository::DeleteWhere(
database,
fmt::format(
"`character_id` = {} AND `name` = '{}'",
charid,
Strings::Escape(char_name)
)
);
database.SetLoginFlags(charid, false, false, 1);
} else {
auto group_id = database.GetGroupID(char_name);
@@ -2107,7 +2135,7 @@ void Client::SetClassStartingSkills(PlayerProfile_Struct *pp)
i == EQ::skills::SkillAlcoholTolerance || i == EQ::skills::SkillBindWound)
continue;
pp->skills[i] = content_db.GetSkillCap(pp->class_, (EQ::skills::SkillType)i, 1);
pp->skills[i] = skill_caps.GetSkillCap(pp->class_, (EQ::skills::SkillType)i, 1).cap;
}
}
+1 -1
View File
@@ -344,7 +344,7 @@ bool ClientListEntry::CheckAuth(uint32 loginserver_account_id, const char *key_p
paccountid = database.CreateAccount(
loginserver_account_name,
0,
std::string(),
default_account_status,
source_loginserver,
LSID()
+12 -4
View File
@@ -94,12 +94,20 @@ void ClientList::GetCLEIP(uint32 in_ip) {
int count = 0;
iterator.Reset();
const auto& zones = Strings::Split(RuleS(World, IPExemptionZones), ",");
while (iterator.MoreElements()) {
cle = iterator.GetData();
const auto zones = Strings::Split(RuleS(World, IPExemptionZones), ",");
for (const auto &z : zones) {
if (Strings::ToUnsignedInt(z) == cle->zone()) {
if (!zones.empty() && cle->zone()) {
auto it = std::ranges::find_if(
zones,
[cle](const auto& z) {
return Strings::ToUnsignedInt(z) == cle->zone();
}
);
if (it != zones.end()) {
iterator.Advance();
continue;
}
+1 -2
View File
@@ -57,8 +57,7 @@ struct EQ::Net::ConsoleLoginStatus CheckLogin(const std::string &username, const
return ret;
}
char account_name[64];
database.GetAccountName(ret.account_id, account_name);
const std::string& account_name = database.GetAccountName(ret.account_id);
ret.account_name = account_name;
ret.status = database.CheckStatus(ret.account_id);
+1
View File
@@ -156,6 +156,7 @@ std::vector<Reload> reload_types = {
Reload{.command = "opcodes", .opcode = ServerOP_ReloadOpcodes, .desc = "Opcodes"},
Reload{.command = "perl_export", .opcode = ServerOP_ReloadPerlExportSettings, .desc = "Perl Event Export Settings"},
Reload{.command = "rules", .opcode = ServerOP_ReloadRules, .desc = "Rules"},
Reload{.command = "skill_caps", .opcode = ServerOP_ReloadSkillCaps, .desc = "Skill Caps"},
Reload{.command = "static", .opcode = ServerOP_ReloadStaticZoneData, .desc = "Static Zone Data"},
Reload{.command = "tasks", .opcode = ServerOP_ReloadTasks, .desc = "Tasks"},
Reload{.command = "titles", .opcode = ServerOP_ReloadTitles, .desc = "Titles"},
+4 -1
View File
@@ -86,8 +86,9 @@
#include "world_boot.h"
#include "../common/path_manager.h"
#include "../common/events/player_event_logs.h"
#include "../common/skill_caps.h"
SkillCaps skill_caps;
ZoneStore zone_store;
ClientList client_list;
GroupLFPList LFPGroupList;
@@ -193,6 +194,8 @@ int main(int argc, char **argv)
->SetExpansionContext()
->ReloadContentFlags();
skill_caps.SetContentDatabase(&content_db)->LoadSkillCaps();
std::unique_ptr<EQ::Net::ServertalkServer> server_connection;
server_connection = std::make_unique<EQ::Net::ServertalkServer>();
-4
View File
@@ -292,10 +292,6 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
LogError("Error: Could not load item data. But ignoring");
}
if (!content_db.LoadSkillCaps(std::string(hotfix_name))) {
LogError("Error: Could not load skill cap data. But ignoring");
}
guild_mgr.LoadGuilds();
guild_mgr.LoadTributes();
+6 -5
View File
@@ -49,6 +49,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/patches/patches.h"
#include "../zone/data_bucket.h"
#include "../common/repositories/guild_tributes_repository.h"
#include "../common/skill_caps.h"
extern ClientList client_list;
extern GroupLFPList LFPGroupList;
@@ -1436,6 +1437,11 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
zoneserver_list.SendPacket(pack);
break;
}
case ServerOP_ReloadSkillCaps: {
zoneserver_list.SendPacket(pack);
skill_caps.ReloadSkillCaps();
break;
}
case ServerOP_ReloadRules: {
zoneserver_list.SendPacket(pack);
RuleManager::Instance()->LoadRules(&database, "default", true);
@@ -1496,11 +1502,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
LogInfo("Error: Could not load item data. But ignoring");
}
LogInfo("Loading skill caps");
if (!content_db.LoadSkillCaps(hotfix_name)) {
LogInfo("Error: Could not load skill cap data. But ignoring");
}
zoneserver_list.SendPacket(pack);
break;
}
+3
View File
@@ -51,6 +51,7 @@ SET(zone_sources
loot.cpp
lua_bot.cpp
lua_bit.cpp
lua_buff.cpp
lua_corpse.cpp
lua_client.cpp
lua_door.cpp
@@ -105,6 +106,7 @@ SET(zone_sources
pathfinder_null.cpp
pathing.cpp
perl_bot.cpp
perl_buff.cpp
perl_client.cpp
perl_doors.cpp
perl_entity.cpp
@@ -207,6 +209,7 @@ SET(zone_headers
horse.h
lua_bot.h
lua_bit.h
lua_buff.h
lua_client.h
lua_corpse.h
lua_door.h
+1 -3
View File
@@ -57,9 +57,7 @@ EQ::Net::WebsocketLoginStatus CheckLogin(
return ret;
}
char account_name[64];
database.GetAccountName(static_cast<uint32>(ret.account_id), account_name);
ret.account_name = account_name;
ret.account_name = database.GetAccountName(static_cast<uint32>(ret.account_id));
ret.logged_in = true;
ret.status = database.CheckStatus(ret.account_id);
return ret;
+109 -32
View File
@@ -1687,13 +1687,6 @@ bool Mob::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
}
}
//used by complete heal and #heal
void Mob::Heal()
{
SetMaxHP();
SendHPUpdate();
}
void Client::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic, eSpecialAttacks special)
{
if (dead || IsCorpse())
@@ -2618,12 +2611,6 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
}
if (m.member) {
m.member->RecordKilledNPCEvent(this);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
parse->EventNPC(EVENT_KILLED_MERIT, this, m.member, "killed", 0);
}
if (RuleB(NPC, EnableMeritBasedFaction)) {
m.member->SetFactionLevel(
m.member->CharacterID(),
@@ -2683,18 +2670,11 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
}
}
/* Send the EVENT_KILLED_MERIT event and update kill tasks
* for all group members */
/* Update kill tasks for all group members */
for (const auto& m : killer_group->members) {
if (m && m->IsClient()) {
Client* c = m->CastToClient();
c->RecordKilledNPCEvent(this);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0);
}
if (RuleB(NPC, EnableMeritBasedFaction)) {
c->SetFactionLevel(
c->CharacterID(),
@@ -2756,13 +2736,6 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
}
}
/* Send the EVENT_KILLED_MERIT event */
give_exp_client->RecordKilledNPCEvent(this);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
parse->EventNPC(EVENT_KILLED_MERIT, this, give_exp_client, "killed", 0);
}
if (RuleB(NPC, EnableMeritBasedFaction)) {
give_exp_client->SetFactionLevel(
give_exp_client->CharacterID(),
@@ -2843,6 +2816,9 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
entity_list.RemoveFromAutoXTargets(this);
// Here we create the corpse.
DeleteInvalidQuestLoot();
corpse = new Corpse(
this,
&m_loot_items,
@@ -2991,6 +2967,17 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
m_combat_record.Stop();
if (give_exp_client && !IsCorpse()) {
const auto& v = give_exp_client->GetRaidOrGroupOrSelf(true);
for (const auto& m : v) {
m->CastToClient()->RecordKilledNPCEvent(this);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
parse->EventNPC(EVENT_KILLED_MERIT, this, m, "killed", 0);
}
}
}
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH_COMPLETE)) {
const auto& export_string = fmt::format(
"{} {} {} {} {} {} {} {} {}",
@@ -3980,6 +3967,18 @@ bool Mob::CheckDoubleAttack()
}
void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType skill_used, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks special) {
#ifdef LUA_EQEMU
int64 lua_ret = 0;
bool ignore_default = false;
lua_ret = LuaParser::Instance()->CommonDamage(this, attacker, damage, spell_id, static_cast<int>(skill_used), avoidable, buffslot, iBuffTic, static_cast<int>(special), ignore_default);
if (lua_ret != 0) {
damage = lua_ret;
}
if (ignore_default) {
//return lua_ret;
}
#endif
// This method is called with skill_used=ABJURE for Damage Shield damage.
bool FromDamageShield = (skill_used == EQ::skills::SkillAbjuration);
bool ignore_invul = false;
@@ -4707,6 +4706,19 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
void Mob::HealDamage(uint64 amount, Mob* caster, uint16 spell_id)
{
#ifdef LUA_EQEMU
uint64 lua_ret = 0;
bool ignore_default = false;
lua_ret = LuaParser::Instance()->HealDamage(this, caster, amount, spell_id, ignore_default);
if (lua_ret != 0) {
amount = lua_ret;
}
if (ignore_default) {
//return lua_ret;
}
#endif
int64 maxhp = GetMaxHP();
int64 curhp = GetHP();
uint64 acthealed = 0;
@@ -6301,9 +6313,24 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
hit.damage_done += (hit.damage_done * pct_damage_reduction / 100) + defender->GetPositionalDmgTakenAmt(this);
if (defender->GetShielderID()) {
DoShieldDamageOnShielder(defender, hit.damage_done, hit.skill);
hit.damage_done -= hit.damage_done * defender->GetShieldTargetMitigation() / 100; //Default shielded takes 50 pct damage
if (defender->GetShielderID() || defender->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT]) {
bool use_shield_ability = true;
//If defender is being shielded by an ability AND has a shield spell effect buff use highest mitigation value.
if ((defender->GetShieldTargetMitigation() && defender->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT]) &&
(defender->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT] >= defender->GetShieldTargetMitigation())){
bool use_shield_ability = false;
}
//use targeted /shield ability values
if (defender->GetShielderID() && use_shield_ability) {
DoShieldDamageOnShielder(defender, hit.damage_done, hit.skill);
hit.damage_done -= hit.damage_done * defender->GetShieldTargetMitigation() / 100; //Default shielded takes 50 pct damage
}
//use spell effect SPA 463 values
else if (defender->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT]){
DoShieldDamageOnShielderSpellEffect(defender, hit.damage_done, hit.skill);
hit.damage_done -= hit.damage_done * defender->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT] / 100;
}
}
CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
@@ -6346,7 +6373,57 @@ void Mob::DoShieldDamageOnShielder(Mob *shield_target, int64 hit_damage_done, EQ
hit_damage_done -= hit_damage_done * mitigation / 100;
shielder->Damage(this, hit_damage_done, SPELL_UNKNOWN, skillInUse, true, -1, false, m_specialattacks);
shielder->CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
}
void Mob::DoShieldDamageOnShielderSpellEffect(Mob* shield_target, int64 hit_damage_done, EQ::skills::SkillType skillInUse)
{
if (!shield_target) {
return;
}
/*
SPA 463 SE_SHIELD_TARGET
Live description: "Shields your target, taking a percentage of their damage".
Only example spell on live is an NPC who uses it during a raid event "Laurion's Song" expansion. SPA 54492 'Guardian Stance' Described as 100% Melee Shielding
Example of mechanic. Base value = 70. Caster puts buff on target. Each melee hit Buff Target takes 70% less damage, Buff Caster receives 30% of the melee damage.
Added mechanic to cause buff to fade if target or caster are seperated by a distance greater than the casting range of the spell. This allows similiar mechanics
to the /shield ability, without a range removal mechanic it would be too easy to abuse if put on a player spell. *can not confirm live does this currently
Can not be cast on self.
*/
if (shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT])
{
if (shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT] >= 0)
{
Mob *shielder = entity_list.GetMob(shield_target->buffs[shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT]].casterid);
if (!shielder) {
shield_target->BuffFadeBySlot(shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT]);
return;
}
int shield_spell_id = shield_target->buffs[shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT]].spellid;
if (!IsValidSpell(shield_spell_id)) {
return;
}
float max_range = spells[shield_spell_id].range;
if (spells[shield_spell_id].aoe_range > max_range) {
max_range = spells[shield_spell_id].aoe_range;
}
max_range += 5.0f; //small buffer in case casted at exactly max range.
if (shield_target->CalculateDistance(shielder->GetX(), shielder->GetY(), shielder->GetZ()) > max_range) {
shield_target->BuffFadeBySlot(shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT]);
return;
}
int mitigation = 100 - shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT];
hit_damage_done -= hit_damage_done * mitigation / 100;
shielder->Damage(this, hit_damage_done, SPELL_UNKNOWN, skillInUse, true, -1, false, m_specialattacks);
}
}
}
void Mob::CommonBreakInvisibleFromCombat()
+24 -1
View File
@@ -2090,7 +2090,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
if (focus)
{
if (WornType){
if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) {
if (RuleB(Spells, UseAdditiveFocusFromWornSlotWithLimits)) {
new_bonus->FocusEffectsWornWithLimits[focus] = spells[spell_id].effect_id[i];
}
else if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) {
new_bonus->FocusEffectsWorn[focus] += spells[spell_id].base_value[i];
}
}
@@ -3298,6 +3301,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
break;
}
case SE_Shield_Target:
{
if (new_bonus->ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT] < effect_value) {
new_bonus->ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT] = effect_value;
new_bonus->ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT] = buffslot;
}
break;
}
case SE_TriggerMeleeThreshold:
new_bonus->TriggerMeleeThreshold = true;
break;
@@ -4989,6 +5001,7 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id)
case SE_MaxHPChange:
if (negate_spellbonus) { spellbonuses.MaxHPChange = effect_value; }
if (negate_aabonus) { aabonuses.MaxHPChange = effect_value; }
if (negate_aabonus) { aabonuses.MaxHP = effect_value; }
if (negate_itembonus) { itembonuses.MaxHPChange = effect_value; }
break;
@@ -5916,6 +5929,7 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id)
if (negate_itembonus) { itembonuses.SkillProcSuccess[e] = effect_value; }
if (negate_aabonus) { aabonuses.SkillProcSuccess[e] = effect_value; }
}
break;
}
case SE_SkillProcAttempt: {
@@ -5925,6 +5939,15 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id)
if (negate_itembonus) { itembonuses.SkillProc[e] = effect_value; }
if (negate_aabonus) { aabonuses.SkillProc[e] = effect_value; }
}
break;
}
case SE_Shield_Target: {
if (negate_spellbonus) {
spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT] = effect_value;
spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT] = effect_value;
}
break;
}
}
}
+179 -121
View File
@@ -27,6 +27,7 @@
#include "../common/repositories/bot_starting_items_repository.h"
#include "../common/data_verification.h"
#include "../common/repositories/criteria/content_filter_criteria.h"
#include "../common/skill_caps.h"
// This constructor is used during the bot create command
Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), ping_timer(1) {
@@ -1171,7 +1172,7 @@ uint16 Bot::GetPrimarySkillValue() {
}
uint16 Bot::MaxSkill(EQ::skills::SkillType skillid, uint16 class_, uint16 level) const {
return(content_db.GetSkillCap(class_, skillid, level));
return skill_caps.GetSkillCap(class_, skillid, level).cap;
}
uint32 Bot::GetTotalATK() {
@@ -2287,7 +2288,7 @@ bool Bot::TryMeditate() {
if (!IsMoving() && !spellend_timer.Enabled()) {
if (GetTarget() && AI_EngagedCastCheck()) {
BotMeditate(false);
} else if (GetArchetype() == ARCHETYPE_CASTER) {
} else if (GetArchetype() == Archetype::Caster) {
BotMeditate(true);
}
@@ -2684,16 +2685,19 @@ bool Bot::IsValidTarget(
return false;
}
const bool valid_target_state = (
HOLDING ||
bool invalid_target_state = false;
if (HOLDING ||
!tar->IsNPC() ||
tar->IsMezzed() ||
lo_distance > leash_distance ||
tar_distance > leash_distance
);
const bool valid_target = !GetAttackingFlag() && !CheckLosFN(tar) && !leash_owner->CheckLosFN(tar);
tar_distance > leash_distance ||
(!GetAttackingFlag() && !CheckLosFN(tar) && !leash_owner->CheckLosFN(tar)) ||
!IsAttackAllowed(tar)
) {
invalid_target_state = true;
}
if (valid_target_state || valid_target || !IsAttackAllowed(tar)) {
if (invalid_target_state) {
// Normally, we wouldn't want to do this without class checks..but, too many issues can arise if we let enchanter animation pets run rampant
if (HasPet()) {
GetPet()->RemoveFromHateList(tar);
@@ -2752,7 +2756,7 @@ Mob* Bot::GetBotTarget(Client* bot_owner)
}
}
if (GetArchetype() == ARCHETYPE_CASTER) {
if (GetArchetype() == Archetype::Caster) {
BotMeditate(true);
}
}
@@ -3340,7 +3344,7 @@ void Bot::LoadAndSpawnAllZonedBots(Client* bot_owner) {
}
if (bot_spawn_limit >= 0 && spawned_bots_count >= bot_spawn_limit) {
database.SetGroupID(b->GetCleanName(), 0, b->GetBotID());
Group::RemoveFromGroup(b);
g->UpdatePlayer(bot_owner);
continue;
}
@@ -3352,7 +3356,7 @@ void Bot::LoadAndSpawnAllZonedBots(Client* bot_owner) {
bot_spawn_limit_class >= 0 &&
spawned_bot_count_class >= bot_spawn_limit_class
) {
database.SetGroupID(b->GetCleanName(), 0, b->GetBotID());
Group::RemoveFromGroup(b);
g->UpdatePlayer(bot_owner);
continue;
}
@@ -3372,7 +3376,7 @@ void Bot::LoadAndSpawnAllZonedBots(Client* bot_owner) {
}
if (!bot_owner->HasGroup()) {
database.SetGroupID(b->GetCleanName(), 0, b->GetBotID());
Group::RemoveFromGroup(b);
}
}
}
@@ -3629,8 +3633,8 @@ bool Bot::RemoveBotFromGroup(Bot* bot, Group* group) {
bot->SetFollowID(0);
if (group->DelMember(bot)) {
group->DelMemberOOZ(bot->GetName());
database.SetGroupID(bot->GetCleanName(), 0, bot->GetBotID());
if (group->GroupCount() < 1) {
Group::RemoveFromGroup(bot);
if (group->GroupCount() < 2) {
group->DisbandGroup();
}
}
@@ -3643,7 +3647,7 @@ bool Bot::RemoveBotFromGroup(Bot* bot, Group* group) {
group->members[i]->SetFollowID(0);
}
group->DisbandGroup();
database.SetGroupID(bot->GetCleanName(), 0, bot->GetBotID());
Group::RemoveFromGroup(bot);
}
Result = true;
}
@@ -5186,31 +5190,26 @@ void Bot::ProcessBotOwnerRefDelete(Mob* botOwner) {
}
}
int64 Bot::CalcMaxMana() {
switch(GetCasterClass()) {
case 'I':
max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + aabonuses.Mana + GroupLeadershipAAManaEnhancement());
max_mana += itembonuses.heroic_max_mana;
case 'W': {
max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + aabonuses.Mana + GroupLeadershipAAManaEnhancement());
max_mana += itembonuses.heroic_max_mana;
break;
}
case 'N': {
max_mana = 0;
break;
}
default: {
LogDebug("Invalid Class [{}] in CalcMaxMana", GetCasterClass());
max_mana = 0;
break;
}
int64 Bot::CalcMaxMana()
{
if (IsIntelligenceCasterClass() || IsWisdomCasterClass()) {
max_mana = (
GenerateBaseManaPoints() +
itembonuses.Mana +
spellbonuses.Mana +
aabonuses.Mana +
GroupLeadershipAAManaEnhancement()
);
max_mana += itembonuses.heroic_max_mana;
} else {
max_mana = 0;
}
if (current_mana > max_mana)
if (current_mana > max_mana) {
current_mana = max_mana;
else if (max_mana < 0)
} else if (max_mana < 0) {
max_mana = 0;
}
return max_mana;
}
@@ -5525,87 +5524,107 @@ bool Bot::DoCastSpell(uint16 spell_id, uint16 target_id, EQ::spells::CastingSlot
return Result;
}
int32 Bot::GenerateBaseManaPoints() {
int32 bot_mana = 0;
int32 WisInt = 0;
int32 Bot::GenerateBaseManaPoints()
{
int32 bot_mana = 0;
int32 WisInt = 0;
int32 MindLesserFactor, MindFactor;
int wisint_mana = 0;
int base_mana = 0;
int ConvertedWisInt = 0;
switch(GetCasterClass()) {
case 'I':
WisInt = INT;
if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
if (WisInt > 100) {
ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
if (WisInt > 201)
ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
}
else
ConvertedWisInt = WisInt;
int wisint_mana = 0;
int base_mana = 0;
int ConvertedWisInt = 0;
if (GetLevel() < 41) {
wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000);
base_mana = (GetLevel() * 15);
} else if (GetLevel() < 81) {
wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100));
base_mana = (600 + ((GetLevel() - 40) * 30));
} else {
wisint_mana = (9 * ConvertedWisInt);
base_mana = (1800 + ((GetLevel() - 80) * 18));
if (IsIntelligenceCasterClass()) {
WisInt = INT;
if (
GetOwner() &&
GetOwner()->CastToClient() &&
GetOwner()->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD &&
RuleB(Character, SoDClientUseSoDHPManaEnd)
) {
if (WisInt > 100) {
ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
if (WisInt > 201) {
ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
}
bot_mana = (base_mana + wisint_mana);
} else {
if (((WisInt - 199) / 2) > 0)
MindLesserFactor = ((WisInt - 199) / 2);
else
MindLesserFactor = 0;
MindFactor = WisInt - MindLesserFactor;
if (WisInt > 100)
bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
else
bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
ConvertedWisInt = WisInt;
}
break;
case 'W':
WisInt = WIS;
if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
if (WisInt > 100) {
ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
if (WisInt > 201)
ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
} else
ConvertedWisInt = WisInt;
if (GetLevel() < 41) {
wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000);
base_mana = (GetLevel() * 15);
} else if (GetLevel() < 81) {
wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100));
base_mana = (600 + ((GetLevel() - 40) * 30));
} else {
wisint_mana = (9 * ConvertedWisInt);
base_mana = (1800 + ((GetLevel() - 80) * 18));
}
bot_mana = (base_mana + wisint_mana);
if (GetLevel() < 41) {
wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000);
base_mana = (GetLevel() * 15);
} else if (GetLevel() < 81) {
wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100));
base_mana = (600 + ((GetLevel() - 40) * 30));
} else {
if (((WisInt - 199) / 2) > 0)
MindLesserFactor = ((WisInt - 199) / 2);
else
MindLesserFactor = 0;
MindFactor = (WisInt - MindLesserFactor);
if (WisInt > 100)
bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
else
bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
wisint_mana = (9 * ConvertedWisInt);
base_mana = (1800 + ((GetLevel() - 80) * 18));
}
break;
default:
bot_mana = 0;
break;
bot_mana = (base_mana + wisint_mana);
} else {
if (((WisInt - 199) / 2) > 0) {
MindLesserFactor = ((WisInt - 199) / 2);
} else {
MindLesserFactor = 0;
}
MindFactor = WisInt - MindLesserFactor;
if (WisInt > 100) {
bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
} else {
bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
}
}
} else if (IsWisdomCasterClass()) {
WisInt = WIS;
if (
GetOwner() &&
GetOwner()->CastToClient() &&
GetOwner()->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD &&
RuleB(Character, SoDClientUseSoDHPManaEnd)
) {
if (WisInt > 100) {
ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
if (WisInt > 201) {
ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
}
} else {
ConvertedWisInt = WisInt;
}
if (GetLevel() < 41) {
wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000);
base_mana = (GetLevel() * 15);
} else if (GetLevel() < 81) {
wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100));
base_mana = (600 + ((GetLevel() - 40) * 30));
} else {
wisint_mana = (9 * ConvertedWisInt);
base_mana = (1800 + ((GetLevel() - 80) * 18));
}
bot_mana = (base_mana + wisint_mana);
} else {
if (((WisInt - 199) / 2) > 0) {
MindLesserFactor = ((WisInt - 199) / 2);
} else {
MindLesserFactor = 0;
}
MindFactor = (WisInt - MindLesserFactor);
if (WisInt > 100) {
bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
} else {
bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
}
}
} else {
bot_mana = 0;
}
max_mana = bot_mana;
return bot_mana;
}
@@ -6216,11 +6235,10 @@ int64 Bot::CalcMaxHP() {
uint32 nd = 10000;
bot_hp += (GenerateBaseHitPoints() + itembonuses.HP);
bot_hp += itembonuses.heroic_max_hp;
nd += aabonuses.MaxHP;
nd += aabonuses.MaxHP + spellbonuses.MaxHPChange + itembonuses.MaxHPChange;
bot_hp = ((float)bot_hp * (float)nd / (float)10000);
bot_hp += (spellbonuses.HP + aabonuses.HP);
bot_hp += GroupLeadershipAAHealthEnhancement();
bot_hp += (bot_hp * ((spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000.0f));
max_hp = bot_hp;
if (current_hp > max_hp)
current_hp = max_hp;
@@ -6582,14 +6600,14 @@ void Bot::ProcessBotGroupInvite(Client* c, std::string const& botName) {
entity_list.AddGroup(g);
database.SetGroupLeaderName(g->GetID(), c->GetName());
g->SaveGroupLeaderAA();
database.SetGroupID(c->GetName(), g->GetID(), c->CharacterID());
database.SetGroupID(invitedBot->GetCleanName(), g->GetID(), invitedBot->GetBotID());
g->AddToGroup(c);
g->AddToGroup(invitedBot);
} else {
delete g;
}
} else {
if (AddBotToGroup(invitedBot, c->GetGroup())) {
database.SetGroupID(invitedBot->GetCleanName(), c->GetGroup()->GetID(), invitedBot->GetBotID());
c->GetGroup()->AddToGroup(invitedBot);
}
}
} else if (invitedBot->HasGroup()) {
@@ -6740,7 +6758,7 @@ void Bot::CalcBotStats(bool showtext) {
SetLevel(GetBotOwner()->GetLevel());
for (int sindex = 0; sindex <= EQ::skills::HIGHEST_SKILL; ++sindex) {
skills[sindex] = content_db.GetSkillCap(GetClass(), (EQ::skills::SkillType)sindex, GetLevel());
skills[sindex] = skill_caps.GetSkillCap(GetClass(), (EQ::skills::SkillType)sindex, GetLevel()).cap;
}
taunt_timer.Start(1000);
@@ -7640,18 +7658,58 @@ void Bot::SetDefaultBotStance() {
_botStance = defaultStance;
}
void Bot::BotGroupSay(Mob *speaker, const char *msg, ...) {
void Bot::BotGroupSay(Mob* speaker, const char* msg, ...) {
char buf[1000];
va_list ap;
va_start(ap, msg);
vsnprintf(buf, 1000, msg, ap);
va_end(ap);
if (speaker->HasGroup()) {
Group *g = speaker->GetGroup();
if (g)
g->GroupMessage(speaker->CastToMob(), Language::CommonTongue, Language::MaxValue, buf);
} else
speaker->Say("%s", buf);
if (speaker->IsRaidGrouped()) {
Raid* r = entity_list.GetRaidByBotName(speaker->GetName());
if (r) {
for (const auto& m : r->members) {
if (m.member && !m.is_bot) {
m.member->FilteredMessageString(
speaker,
Chat::PetResponse,
FilterSocials,
GENERIC_SAY,
speaker->GetCleanName(),
buf
);
}
}
}
}
else if (speaker->HasGroup()) {
Group* g = speaker->GetGroup();
if (g) {
for (auto& m : g->members) {
if (m && !m->IsBot()) {
m->FilteredMessageString(
speaker,
Chat::PetResponse,
FilterSocials,
GENERIC_SAY,
speaker->GetCleanName(),
buf
);
}
}
}
}
else {
//speaker->Say("%s", buf);
speaker->GetOwner()->FilteredMessageString(
speaker,
Chat::PetResponse,
FilterSocials,
GENERIC_SAY,
speaker->GetCleanName(),
buf
);
}
}
bool Bot::UseDiscipline(uint32 spell_id, uint32 target) {
+5 -32
View File
@@ -184,35 +184,11 @@ bool BotDatabase::QueryNameAvailablity(const std::string& bot_name, bool& availa
if (
bot_name.empty() ||
bot_name.size() > 60 ||
!database.CheckUsedName(bot_name)
database.IsNameUsed(bot_name)
) {
return false;
}
const auto& bot_data = BotDataRepository::GetWhere(
database,
fmt::format(
"`name` LIKE '{}' LIMIT 1",
bot_name
)
);
if (!bot_data.empty()) {
return false;
}
const auto& character_data = CharacterDataRepository::GetWhere(
database,
fmt::format(
"`name` LIKE '{}' LIMIT 1",
bot_name
)
);
if (!character_data.empty()) {
return false;
}
available_flag = true;
return true;
@@ -1788,7 +1764,8 @@ bool BotDatabase::CreateCloneBotInventory(const uint32 bot_id, const uint32 clon
}
for (auto& e : l) {
e.bot_id = clone_id;
e.inventories_index = 0;
e.bot_id = clone_id;
}
return BotInventoriesRepository::InsertMany(database, l);
@@ -1921,18 +1898,14 @@ bool BotDatabase::LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 g
const auto& l = GroupIdRepository::GetWhere(
database,
fmt::format(
"`groupid` = {} AND `name` IN (SELECT `name` FROM `bot_data` WHERE `owner_id` = {})",
"`group_id` = {} AND `bot_id` != 0 AND `name` IN (SELECT `name` FROM `bot_data` WHERE `owner_id` = {})",
group_id,
owner_id
)
);
if (l.empty()) {
return true;
}
for (const auto& e : l) {
group_list.push_back(e.charid);
group_list.emplace_back(e.bot_id);
}
return true;
+1 -1
View File
@@ -98,7 +98,7 @@ void Raid::HandleBotGroupDisband(uint32 owner, uint32 gid)
auto r_group_members = GetRaidGroupMembers(GetGroup(b->GetName()));
auto g = new Group(b);
entity_list.AddGroup(g);
database.SetGroupID(b->GetCleanName(), g->GetID(), b->GetBotID());
g->AddToGroup(b);
database.SetGroupLeaderName(g->GetID(), b->GetName());
for (auto m: r_group_members) {
+4 -4
View File
@@ -591,7 +591,7 @@ bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
//Only check archetype if spell is not a group spell
//Hybrids get all buffs
switch (tar->GetArchetype()) {
case ARCHETYPE_CASTER:
case Archetype::Caster:
//TODO: probably more caster specific spell effects in here
if (
(
@@ -606,7 +606,7 @@ bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
continue;
}
break;
case ARCHETYPE_MELEE:
case Archetype::Melee:
if (
(
IsEffectInSpell(s.SpellId, SE_IncreaseSpellHaste) ||
@@ -899,7 +899,7 @@ bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
switch (tar->GetArchetype())
{
case ARCHETYPE_CASTER:
case Archetype::Caster:
//TODO: probably more caster specific spell effects in here
if (IsEffectInSpell(s.SpellId, SE_AttackSpeed) || IsEffectInSpell(s.SpellId, SE_ATK) ||
IsEffectInSpell(s.SpellId, SE_STR) || IsEffectInSpell(s.SpellId, SE_ReverseDS))
@@ -907,7 +907,7 @@ bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
continue;
}
break;
case ARCHETYPE_MELEE:
case Archetype::Melee:
if (IsEffectInSpell(s.SpellId, SE_IncreaseSpellHaste) || IsEffectInSpell(s.SpellId, SE_ManaPool) ||
IsEffectInSpell(s.SpellId, SE_CastingLevel) || IsEffectInSpell(s.SpellId, SE_ManaRegen_v2) ||
IsEffectInSpell(s.SpellId, SE_CurrentMana))
+215 -58
View File
@@ -57,6 +57,7 @@ extern volatile bool RunLoops;
#include "queryserv.h"
#include "mob_movement_manager.h"
#include "cheat_manager.h"
#include "lua_parser.h"
#include "../common/repositories/character_alternate_abilities_repository.h"
#include "../common/repositories/account_flags_repository.h"
@@ -68,10 +69,12 @@ extern volatile bool RunLoops;
#include "../common/repositories/discovered_items_repository.h"
#include "../common/repositories/inventory_repository.h"
#include "../common/repositories/keyring_repository.h"
#include "../common/repositories/tradeskill_recipe_repository.h"
#include "../common/events/player_events.h"
#include "../common/events/player_event_logs.h"
#include "dialogue_window.h"
#include "../common/zone_store.h"
#include "../common/skill_caps.h"
extern QueryServ* QServ;
@@ -2225,8 +2228,8 @@ void Client::ChangeLastName(std::string last_name) {
bool Client::ChangeFirstName(const char* in_firstname, const char* gmname)
{
// check duplicate name
bool usedname = database.CheckUsedName((const char*) in_firstname);
if (!usedname) {
bool used_name = database.IsNameUsed((const char*) in_firstname);
if (used_name) {
return false;
}
@@ -2294,29 +2297,23 @@ void Client::ReadBook(BookRequest_Struct *book) {
BookText_Struct *out = (BookText_Struct *) outapp->pBuffer;
out->window = book->window;
if (ClientVersion() >= EQ::versions::ClientVersion::SoF) {
// SoF+ need to look up book type for the output message.
const EQ::ItemInstance *inst = nullptr;
if (book->invslot <= EQ::invbag::GENERAL_BAGS_END)
{
inst = m_inv[book->invslot];
}
if(inst)
out->type = inst->GetItem()->Book;
else
out->type = book->type;
}
else {
out->type = book->type;
}
out->type = book->type;
out->invslot = book->invslot;
out->target_id = book->target_id;
out->can_cast = 0; // todo: implement
out->can_scribe = 0; // todo: implement
out->can_scribe = false;
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && book->invslot <= EQ::invbag::GENERAL_BAGS_END)
{
const EQ::ItemInstance* inst = m_inv[book->invslot];
if (inst && inst->GetItem())
{
auto recipe = TradeskillRecipeRepository::GetWhere(content_db,
fmt::format("learned_by_item_id = {} LIMIT 1", inst->GetItem()->ID));
out->type = inst->GetItem()->Book;
out->can_scribe = !recipe.empty();
}
}
memcpy(out->booktext, booktxt2.c_str(), length);
@@ -2761,31 +2758,48 @@ void Client::CheckLanguageSkillIncrease(uint8 language_id, uint8 teacher_skill)
}
}
bool Client::HasSkill(EQ::skills::SkillType skill_id) const {
return((GetSkill(skill_id) > 0) && CanHaveSkill(skill_id));
}
bool Client::CanHaveSkill(EQ::skills::SkillType skill_id) const {
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);
//if you don't have it by max level, then odds are you never will?
}
uint16 Client::MaxSkill(EQ::skills::SkillType skillid, uint16 class_, uint16 level) const {
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == Class::Berserker && skillid == EQ::skills::Skill1HPiercing)
skillid = EQ::skills::Skill2HPiercing;
return(content_db.GetSkillCap(class_, skillid, level));
}
uint8 Client::SkillTrainLevel(EQ::skills::SkillType skillid, uint16 class_)
bool Client::HasSkill(EQ::skills::SkillType skill_id) const
{
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == Class::Berserker && skillid == EQ::skills::Skill1HPiercing)
skillid = EQ::skills::Skill2HPiercing;
return GetSkill(skill_id) > 0 && CanHaveSkill(skill_id);
}
return(content_db.GetTrainLevel(class_, skillid, RuleI(Character, MaxLevel)));
bool Client::CanHaveSkill(EQ::skills::SkillType skill_id) const
{
if (
ClientVersion() < EQ::versions::ClientVersion::RoF2 &&
class_ == Class::Berserker &&
skill_id == EQ::skills::Skill1HPiercing
) {
skill_id = EQ::skills::Skill2HPiercing;
}
return skill_caps.GetSkillCap(GetClass(), skill_id, RuleI(Character, MaxLevel)).cap > 0;
}
uint16 Client::MaxSkill(EQ::skills::SkillType skill_id, uint8 class_id, uint8 level) const
{
if (
ClientVersion() < EQ::versions::ClientVersion::RoF2 &&
class_id == Class::Berserker &&
skill_id == EQ::skills::Skill1HPiercing
) {
skill_id = EQ::skills::Skill2HPiercing;
}
return skill_caps.GetSkillCap(class_id, skill_id, level).cap;
}
uint8 Client::SkillTrainLevel(EQ::skills::SkillType skill_id, uint8 class_id)
{
if (
ClientVersion() < EQ::versions::ClientVersion::RoF2 &&
class_id == Class::Berserker &&
skill_id == EQ::skills::Skill1HPiercing
) {
skill_id = EQ::skills::Skill2HPiercing;
}
return skill_caps.GetTrainLevel(class_id, skill_id, RuleI(Character, MaxLevel));
}
uint16 Client::GetMaxSkillAfterSpecializationRules(EQ::skills::SkillType skillid, uint16 maxSkill)
@@ -3791,14 +3805,12 @@ void Client::GetRaidAAs(RaidLeadershipAA_Struct *into) const {
void Client::EnteringMessages(Client* client)
{
std::string rules;
if (database.GetVariable("Rules", rules)) {
uint8 flag = database.GetAgreementFlag(client->AccountID());
std::string rules = RuleS(World, Rules);
if (!rules.empty() || database.GetVariable("Rules", rules)) {
const uint8 flag = database.GetAgreementFlag(client->AccountID());
if (!flag) {
auto rules_link = Saylink::Silent(
"#serverrules",
"rules"
);
const std::string& rules_link = Saylink::Silent("#serverrules", "rules");
client->Message(
Chat::White,
@@ -3815,9 +3827,9 @@ void Client::EnteringMessages(Client* client)
void Client::SendRules()
{
std::string rules;
std::string rules = RuleS(World, Rules);
if (!database.GetVariable("Rules", rules)) {
if (rules.empty() && !database.GetVariable("Rules", rules)) {
return;
}
@@ -4449,7 +4461,7 @@ bool Client::GroupFollow(Client* inviter) {
}
//now we have a group id, can set inviter's id
database.SetGroupID(inviter->GetName(), group->GetID(), inviter->CharacterID(), false);
group->AddToGroup(inviter);
database.SetGroupLeaderName(group->GetID(), inviter->GetName());
group->UpdateGroupAAs();
@@ -6620,13 +6632,72 @@ void Client::SendAltCurrencies() {
void Client::SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount)
{
if (!zone->DoesAlternateCurrencyExist(currency_id)) {
return;
}
const uint32 current_amount = alternate_currency[currency_id];
const bool is_gain = new_amount > current_amount;
const uint32 change_amount = is_gain ? (new_amount - current_amount) : (current_amount - new_amount);
if (!change_amount) {
return;
}
alternate_currency[currency_id] = new_amount;
database.UpdateAltCurrencyValue(CharacterID(), currency_id, new_amount);
SendAlternateCurrencyValue(currency_id);
QuestEventID event_id = is_gain ? EVENT_ALT_CURRENCY_GAIN : EVENT_ALT_CURRENCY_LOSS;
if (parse->PlayerHasQuestSub(event_id)) {
const std::string &export_string = fmt::format(
"{} {} {}",
currency_id,
change_amount,
new_amount
);
parse->EventPlayer(event_id, this, export_string, 0);
}
}
bool Client::RemoveAlternateCurrencyValue(uint32 currency_id, uint32 amount)
{
if (!amount || !zone->DoesAlternateCurrencyExist(currency_id)) {
return false;
}
const uint32 current_amount = alternate_currency[currency_id];
if (current_amount < amount) {
return false;
}
const uint32 new_amount = (current_amount - amount);
alternate_currency[currency_id] = new_amount;
if (parse->PlayerHasQuestSub(EVENT_ALT_CURRENCY_LOSS)) {
const std::string &export_string = fmt::format(
"{} {} {}",
currency_id,
amount,
new_amount
);
parse->EventPlayer(EVENT_ALT_CURRENCY_LOSS, this, export_string, 0);
}
return true;
}
int Client::AddAlternateCurrencyValue(uint32 currency_id, int amount, bool is_scripted)
{
if (!zone->DoesAlternateCurrencyExist(currency_id)) {
return 0;
}
/* Added via Quest, rest of the logging methods may be done inline due to information available in that area of the code */
if (is_scripted) {
/* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */
@@ -6665,7 +6736,6 @@ int Client::AddAlternateCurrencyValue(uint32 currency_id, int amount, bool is_sc
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(
"{} {} {}",
@@ -6706,6 +6776,10 @@ void Client::SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null)
uint32 Client::GetAlternateCurrencyValue(uint32 currency_id) const
{
if (!zone->DoesAlternateCurrencyExist(currency_id)) {
return 0;
}
auto iter = alternate_currency.find(currency_id);
return iter == alternate_currency.end() ? 0 : (*iter).second;
@@ -6775,7 +6849,7 @@ void Client::UpdateClientXTarget(Client *c)
// IT IS NOT SAFE TO CALL THIS IF IT'S NOT INITIAL AGGRO
void Client::AddAutoXTarget(Mob *m, bool send)
{
if (m->IsBot() || (m->IsPet() && m->IsPetOwnerBot())) {
if (m->IsBot() || ((m->IsPet() || m->IsTempPet()) && m->IsPetOwnerBot())) {
return;
}
@@ -9171,6 +9245,7 @@ void Client::ShowDevToolsMenu()
menu_reload_six += " | " + Saylink::Silent("#reload quest", "Quests");
menu_reload_seven += Saylink::Silent("#reload rules", "Rules");
menu_reload_seven += " | " + Saylink::Silent("#reload skill_caps", "Skill Caps");
menu_reload_seven += " | " + Saylink::Silent("#reload static", "Static Zone Data");
menu_reload_seven += " | " + Saylink::Silent("#reload tasks", "Tasks");
@@ -11298,6 +11373,16 @@ void Client::SendReloadCommandMessages() {
).c_str()
);
auto skill_caps_link = Saylink::Silent("#reload skill_caps");
Message(
Chat::White,
fmt::format(
"Usage: {} - Reloads Skill Caps globally",
skill_caps_link
).c_str()
);
auto static_link = Saylink::Silent("#reload static");
Message(
@@ -11473,6 +11558,39 @@ void Client::SetLockSavePosition(bool lock_save_position)
Client::m_lock_save_position = lock_save_position;
}
void Client::SetAAPoints(uint32 points)
{
const uint32 current_points = m_pp.aapoints;
m_pp.aapoints = points;
QuestEventID event_id = points > current_points ? EVENT_AA_GAIN : EVENT_AA_LOSS;
const uint32 change = event_id == EVENT_AA_GAIN ? points - current_points : current_points - points;
if (parse->PlayerHasQuestSub(event_id)) {
parse->EventPlayer(event_id, this, std::to_string(change), 0);
}
SendAlternateAdvancementStats();
}
bool Client::RemoveAAPoints(uint32 points)
{
if (m_pp.aapoints < points) {
return false;
}
m_pp.aapoints -= points;
if (parse->PlayerHasQuestSub(EVENT_AA_LOSS)) {
parse->EventPlayer(EVENT_AA_LOSS, this, std::to_string(points), 0);
}
SendAlternateAdvancementStats();
return true;
}
void Client::AddAAPoints(uint32 points)
{
m_pp.aapoints += points;
@@ -11530,6 +11648,14 @@ void Client::RegisterBug(BugReport_Struct* r) {
b.bug_report = r->bug_report;
b.system_info = r->system_info;
#ifdef LUA_EQEMU
bool ignore_default = false;
LuaParser::Instance()->RegisterBug(this, b, ignore_default);
if (ignore_default) {
return;
}
#endif
auto n = BugReportsRepository::InsertOne(database, b);
if (!n.id) {
Message(Chat::White, "Failed to created your bug report."); // Client sends success message
@@ -11679,7 +11805,7 @@ void Client::MaxSkills()
auto current_skill_value = (
EQ::skills::IsSpecializedSkill(s.first) ?
MAX_SPECIALIZED_SKILL :
content_db.GetSkillCap(GetClass(), s.first, GetLevel())
skill_caps.GetSkillCap(GetClass(), s.first, GetLevel()).cap
);
if (GetSkill(s.first) < current_skill_value) {
@@ -12225,3 +12351,34 @@ int Client::GetEXPPercentage()
return static_cast<int>(std::round(scaled * 100.0 / 330.0)); // unscaled pct
}
std::vector<Mob*> Client::GetRaidOrGroupOrSelf(bool clients_only)
{
std::vector<Mob*> v;
if (IsRaidGrouped()) {
Raid* r = GetRaid();
if (r) {
for (const auto& m : r->members) {
if (m.member && (!m.is_bot || !clients_only)) {
v.emplace_back(m.member);
}
}
}
} else if (IsGrouped()) {
Group* g = GetGroup();
if (g) {
for (const auto& m : g->members) {
if (m && (m->IsClient() || !clients_only)) {
v.emplace_back(m);
}
}
}
} else {
v.emplace_back(this);
}
return v;
}
+9 -4
View File
@@ -272,6 +272,8 @@ public:
int GetQuiverHaste(int delay);
void DoAttackRounds(Mob *target, int hand, bool IsFromSpell = false);
std::vector<Mob*> GetRaidOrGroupOrSelf(bool clients_only = false);
void AI_Init();
void AI_Start(uint32 iMoveDelay = 0);
void AI_Stop();
@@ -348,6 +350,7 @@ public:
int GetRecipeMadeCount(uint32 recipe_id);
bool HasRecipeLearned(uint32 recipe_id);
bool CanIncreaseTradeskill(EQ::skills::SkillType tradeskill);
void ScribeRecipes(uint32_t item_id) const;
bool GetRevoked() const { return revoked; }
void SetRevoked(bool rev) { revoked = rev; }
@@ -812,9 +815,9 @@ public:
void SetHoTT(uint32 mobid);
void ShowSkillsWindow();
uint16 MaxSkill(EQ::skills::SkillType skillid, uint16 class_, uint16 level) const;
inline uint16 MaxSkill(EQ::skills::SkillType skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); }
uint8 SkillTrainLevel(EQ::skills::SkillType skillid, uint16 class_);
uint16 MaxSkill(EQ::skills::SkillType skill_id, uint8 class_id, uint8 level) const;
inline uint16 MaxSkill(EQ::skills::SkillType skill_id) const { return MaxSkill(skill_id, GetClass(), GetLevel()); }
uint8 SkillTrainLevel(EQ::skills::SkillType skill_id, uint8 class_id);
void MaxSkills();
void SendTradeskillSearchResults(const std::string &query, unsigned long objtype, unsigned long someid);
@@ -933,8 +936,9 @@ public:
void ResetAlternateAdvancementTimers();
void ResetOnDeathAlternateAdvancement();
void SetAAPoints(uint32 points) { m_pp.aapoints = points; SendAlternateAdvancementStats(); }
void SetAAPoints(uint32 points);
void AddAAPoints(uint32 points);
bool RemoveAAPoints(uint32 points);
int GetAAPoints() { return m_pp.aapoints; }
int GetSpentAA() { return m_pp.aapoints_spent; }
uint32 GetRequiredAAExperience();
@@ -1539,6 +1543,7 @@ public:
void SendAltCurrencies();
void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount);
int AddAlternateCurrencyValue(uint32 currency_id, int amount, bool is_scripted = false);
bool RemoveAlternateCurrencyValue(uint32 currency_id, uint32 amount);
void SendAlternateCurrencyValues();
void SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null = true);
uint32 GetAlternateCurrencyValue(uint32 currency_id) const;
+86 -93
View File
@@ -322,11 +322,12 @@ int64 Client::CalcMaxHP()
//but the actual effect sent on live causes the client
//to apply it to (basehp + itemhp).. I will oblige to the client's whims over
//the aa description
nd += aabonuses.MaxHP; //Natural Durability, Physical Enhancement, Planar Durability
nd += aabonuses.MaxHP + spellbonuses.MaxHPChange + itembonuses.MaxHPChange; //Natural Durability, Physical Enhancement, Planar Durability (MaxHP and MaxHPChange are SPA214)
max_hp = (float)max_hp * (float)nd / (float)10000; //this is to fix the HP-above-495k issue
max_hp += spellbonuses.HP + aabonuses.HP;
max_hp += GroupLeadershipAAHealthEnhancement();
max_hp += max_hp * ((spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000.0f);
if (current_hp > max_hp) {
current_hp = max_hp;
}
@@ -523,28 +524,26 @@ int32 Client::GetRawItemAC()
int64 Client::CalcMaxMana()
{
switch (GetCasterClass()) {
case 'I':
case 'W': {
max_mana = (CalcBaseMana() + itembonuses.Mana + spellbonuses.Mana + aabonuses.Mana + GroupLeadershipAAManaEnhancement());
break;
}
case 'N': {
max_mana = 0;
break;
}
default: {
LogSpells("Invalid Class [{}] in CalcMaxMana", GetCasterClass());
max_mana = 0;
break;
}
if (IsIntelligenceCasterClass() || IsWisdomCasterClass()) {
max_mana = (
CalcBaseMana() +
itembonuses.Mana +
spellbonuses.Mana +
aabonuses.Mana +
GroupLeadershipAAManaEnhancement()
);
} else {
max_mana = 0;
}
if (max_mana < 0) {
max_mana = 0;
}
if (current_mana > max_mana) {
current_mana = max_mana;
}
int mana_perc_cap = spellbonuses.ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP];
if (mana_perc_cap) {
int curMana_cap = (max_mana * mana_perc_cap) / 100;
@@ -552,96 +551,90 @@ int64 Client::CalcMaxMana()
current_mana = curMana_cap;
}
}
LogSpells("for [{}] returning [{}]", GetName(), max_mana);
return max_mana;
}
int64 Client::CalcBaseMana()
{
int ConvertedWisInt = 0;
int MindLesserFactor, MindFactor;
int WisInt = 0;
int64 base_mana = 0;
int wisint_mana = 0;
int64 max_m = 0;
switch (GetCasterClass()) {
case 'I':
WisInt = GetINT();
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
ConvertedWisInt = WisInt;
int over200 = WisInt;
if (WisInt > 100) {
if (WisInt > 200) {
over200 = (WisInt - 200) / -2 + WisInt;
}
ConvertedWisInt = (3 * over200 - 300) / 2 + over200;
}
auto base_data = zone->GetBaseData(GetLevel(), GetClass());
if (base_data.level == GetLevel()) {
max_m = base_data.mana + (ConvertedWisInt * base_data.mana_fac) + itembonuses.heroic_max_mana;
int ConvertedWisInt = 0;
int MindLesserFactor, MindFactor;
int WisInt = 0;
int64 base_mana = 0;
int wisint_mana = 0;
int64 max_m = 0;
if (IsIntelligenceCasterClass()) {
WisInt = GetINT();
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
ConvertedWisInt = WisInt;
int over200 = WisInt;
if (WisInt > 100) {
if (WisInt > 200) {
over200 = (WisInt - 200) / -2 + WisInt;
}
ConvertedWisInt = (3 * over200 - 300) / 2 + over200;
}
else {
if ((( WisInt - 199 ) / 2) > 0) {
MindLesserFactor = ( WisInt - 199 ) / 2;
}
else {
MindLesserFactor = 0;
}
MindFactor = WisInt - MindLesserFactor;
if (WisInt > 100) {
max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
}
else {
max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
}
auto base_data = zone->GetBaseData(GetLevel(), GetClass());
if (base_data.level == GetLevel()) {
max_m = base_data.mana + (ConvertedWisInt * base_data.mana_fac) + itembonuses.heroic_max_mana;
}
break;
case 'W':
WisInt = GetWIS();
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
ConvertedWisInt = WisInt;
int over200 = WisInt;
if (WisInt > 100) {
if (WisInt > 200) {
over200 = (WisInt - 200) / -2 + WisInt;
}
ConvertedWisInt = (3 * over200 - 300) / 2 + over200;
}
auto base_data = zone->GetBaseData(GetLevel(), GetClass());
if (base_data.level == GetLevel()) {
max_m = base_data.mana + (ConvertedWisInt * base_data.mana_fac) + itembonuses.heroic_max_mana;
}
} else {
if (((WisInt - 199) / 2) > 0) {
MindLesserFactor = (WisInt - 199) / 2;
} else {
MindLesserFactor = 0;
}
else {
if ((( WisInt - 199 ) / 2) > 0) {
MindLesserFactor = ( WisInt - 199 ) / 2;
}
else {
MindLesserFactor = 0;
}
MindFactor = WisInt - MindLesserFactor;
if (WisInt > 100) {
max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
}
else {
max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
}
MindFactor = WisInt - MindLesserFactor;
if (WisInt > 100) {
max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
} else {
max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
}
break;
case 'N': {
max_m = 0;
break;
}
} else if (IsWisdomCasterClass()) {
WisInt = GetWIS();
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
ConvertedWisInt = WisInt;
int over200 = WisInt;
if (WisInt > 100) {
if (WisInt > 200) {
over200 = (WisInt - 200) / -2 + WisInt;
}
ConvertedWisInt = (3 * over200 - 300) / 2 + over200;
}
default: {
LogDebug("Invalid Class [{}] in CalcMaxMana", GetCasterClass());
max_m = 0;
break;
auto base_data = zone->GetBaseData(GetLevel(), GetClass());
if (base_data.level == GetLevel()) {
max_m = base_data.mana + (ConvertedWisInt * base_data.mana_fac) + itembonuses.heroic_max_mana;
}
} else {
if (((WisInt - 199) / 2) > 0) {
MindLesserFactor = (WisInt - 199) / 2;
} else {
MindLesserFactor = 0;
}
MindFactor = WisInt - MindLesserFactor;
if (WisInt > 100) {
max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
} else {
max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
}
}
} else {
max_m = 0;
}
#if EQDEBUG >= 11
LogDebug("Client::CalcBaseMana() called for [{}] - returning [{}]", GetName(), max_m);
#endif
return max_m;
}
+22 -9
View File
@@ -1530,8 +1530,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
}
} //else, somebody from our group is already here...
if (!group)
database.SetGroupID(GetName(), 0, CharacterID(), false); //cannot re-establish group, kill it
if (!group) { //cannot re-establish group, kill it
Group::RemoveFromGroup(this);
}
}
else { //no group id
@@ -4168,10 +4169,11 @@ void Client::Handle_OP_BookButton(const EQApplicationPacket* app)
BookButton_Struct* book = reinterpret_cast<BookButton_Struct*>(app->pBuffer);
const EQ::ItemInstance* const inst = GetInv().GetItem(book->invslot);
if (inst && inst->GetItem()->Book)
if (inst && inst->GetItem())
{
// todo: if scribe book learn recipes and delete book from inventory
// todo: if cast book use its spell on target and delete book from inventory (unless reusable?)
// todo: cast spell button (unknown if anything on live uses this)
ScribeRecipes(inst->GetItem()->ID);
DeleteItemInInventory(book->invslot, 1, true);
}
EQApplicationPacket outapp(OP_FinishWindow, 0);
@@ -5595,9 +5597,20 @@ void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app)
}
// Prevent the client from creating more than they have.
const uint32 amount = EQ::ClampUpper(quantity, current_quantity);
uint32 amount = EQ::ClampUpper(quantity, current_quantity);
const uint32 item_id = is_radiant ? RuleI(Zone, RadiantCrystalItemID) : RuleI(Zone, EbonCrystalItemID);
const auto item = database.GetItem(item_id);
// Prevent pulling more than max stack size or 1,000 (if stackable), whichever is lesser
const uint32 max_reclaim_amount = EQ::Clamp(
item && item->Stackable ? item->StackSize : ItemStackSizeConstraint::Minimum,
ItemStackSizeConstraint::Minimum,
ItemStackSizeConstraint::Maximum
);
if (amount > max_reclaim_amount) {
amount = max_reclaim_amount;
}
const bool success = SummonItem(item_id, amount);
if (!success) {
return;
@@ -6862,7 +6875,7 @@ void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app)
Client *c = entity_list.GetClientByName(gmn->oldname);
LogInfo("GM([{}]) changeing players name. Old:[{}] New:[{}]", GetName(), gmn->oldname, gmn->newname);
const bool used_name = database.CheckUsedName(gmn->newname);
const bool used_name = database.IsNameUsed(gmn->newname);
if (!c) {
Message(Chat::Red, fmt::format("{} not found for name change. Operation failed!", gmn->oldname).c_str());
return;
@@ -6873,7 +6886,7 @@ void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app)
return;
}
if (!used_name) {
if (used_name) {
Message(Chat::Red, fmt::format("{} is already in use. Operation failed!", gmn->newname).c_str());
return;
}
@@ -7177,7 +7190,7 @@ void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app)
if (!GetMerc())
{
database.SetGroupID(GetName(), 0, CharacterID(), false);
Group::RemoveFromGroup(this);
}
return;
}
+6 -6
View File
@@ -1064,9 +1064,9 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I
);
SpellOnTarget(resurrection_sickness_spell_id, this);
} else if (SpellID == SPELL_DIVINE_REZ) {
SetHP(GetMaxHP());
SetMana(GetMaxMana());
SetEndurance(GetMaxEndurance());
RestoreHealth();
RestoreMana();
RestoreEndurance();
} else {
SetHP(GetMaxHP() / 20);
SetMana(GetMaxMana() / 20);
@@ -2177,9 +2177,9 @@ void Client::HandleRespawnFromHover(uint32 Option)
FastQueuePacket(&outapp);
CalcBonuses();
SetHP(GetMaxHP());
SetMana(GetMaxMana());
SetEndurance(GetMaxEndurance());
RestoreHealth();
RestoreMana();
RestoreEndurance();
m_Position.x = chosen->x;
m_Position.y = chosen->y;
+2
View File
@@ -208,6 +208,7 @@ int command_init(void)
command_add("scribespells", "[Max level] [Min level] - Scribe all spells for you or your player target that are usable by them, up to level specified. (may freeze client for a few seconds)", AccountStatus::GMLeadAdmin, command_scribespells) ||
command_add("sendzonespawns", "Refresh spawn list for all clients in zone", AccountStatus::GMLeadAdmin, command_sendzonespawns) ||
command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", AccountStatus::Player, command_sensetrap) ||
command_add("serverrules", "Show server rules", AccountStatus::Player, command_serverrules) ||
command_add("set", "Set command used to set various things", AccountStatus::Guide, command_set) ||
command_add("show", "Show command used to show various things", AccountStatus::Guide, command_show) ||
command_add("shutdown", "Shut this zone process down", AccountStatus::GMLeadAdmin, command_shutdown) ||
@@ -897,6 +898,7 @@ void command_bot(Client *c, const Seperator *sep)
#include "gm_commands/scribespells.cpp"
#include "gm_commands/sendzonespawns.cpp"
#include "gm_commands/sensetrap.cpp"
#include "gm_commands/serverrules.cpp"
#include "gm_commands/set.cpp"
#include "gm_commands/show.cpp"
#include "gm_commands/shutdown.cpp"
+14 -7
View File
@@ -19,9 +19,11 @@
#define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from
#define SEE_POSITION 0.5f //ratio of GetSize() where NPCs try to see for LOS
#define ARCHETYPE_HYBRID 1
#define ARCHETYPE_CASTER 2
#define ARCHETYPE_MELEE 3
namespace Archetype {
constexpr uint8 Hybrid = 1;
constexpr uint8 Caster = 2;
constexpr uint8 Melee = 3;
};
#define CON_GREEN 2
#define CON_LIGHTBLUE 18
@@ -285,7 +287,7 @@ struct StatBonuses {
int32 AC;
int64 HP;
int64 HPRegen;
int64 MaxHP;
int64 MaxHP; //same bonus as MaxHPChange when applied to spells and item bonuses
int64 ManaRegen;
int64 EnduranceRegen;
int64 Mana;
@@ -411,7 +413,7 @@ struct StatBonuses {
int32 MeleeLifetap; //i
int32 Vampirism; //i
int32 HealRate; // Spell effect that influences effectiveness of heals
int32 MaxHPChange; // Spell Effect
int32 MaxHPChange; // percent change in hit points (aabonuses use variable MaxHP)
int16 SkillDmgTaken[EQ::skills::HIGHEST_SKILL + 2]; // All Skills + -1
int32 HealAmt; // Item Effect
int32 SpellDmg; // Item Effect
@@ -443,7 +445,8 @@ struct StatBonuses {
int32 SongRange; // increases range of beneficial bard songs
uint32 HPToManaConvert; // Uses HP to cast spells at specific conversion
int32 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have.
int16 FocusEffectsWorn[HIGHEST_FOCUS+1]; // Optional to allow focus effects to be applied additively from worn slot
int16 FocusEffectsWorn[HIGHEST_FOCUS+1]; // Optional to allow focus effects to be applied additively from worn slot, limits do not apply
int32 FocusEffectsWornWithLimits[HIGHEST_FOCUS + 1];// Optional to allow focus effects to be applied additively from worn slot, limits apply
bool NegateEffects; // Check if you contain a buff with negate effect. (only spellbonuses)
int32 SkillDamageAmount2[EQ::skills::HIGHEST_SKILL + 2]; // Adds skill specific damage
uint32 NegateAttacks[3]; // 0 = bool HasEffect 1 = Buff Slot 2 = Max damage absorbed per hit
@@ -509,6 +512,7 @@ struct StatBonuses {
uint8 invisibility; // invisibility level
uint8 invisibility_verse_undead; // IVU level
uint8 invisibility_verse_animal; // IVA level
int32 ShieldTargetSpa[2]; // [0] base = % mitigation amount, [1] buff slot
// AAs
int32 TrapCircumvention; // reduce chance to trigger a trap.
@@ -654,6 +658,8 @@ namespace SBIndex {
constexpr uint16 COMBAT_PROC_SPELL_ID = 1; // SPA
constexpr uint16 COMBAT_PROC_RATE_MOD = 2; // SPA
constexpr uint16 COMBAT_PROC_REUSE_TIMER = 3; // SPA
constexpr uint16 SHIELD_TARGET_MITIGATION_PERCENT = 0; // SPA 463
constexpr uint16 SHIELD_TARGET_BUFFSLOT = 1; // SPA 463
};
@@ -826,7 +832,8 @@ struct ExtraAttackOptions {
armor_pen_percent(0.0f), armor_pen_flat(0),
crit_percent(1.0f), crit_flat(0.0f),
hate_percent(1.0f), hate_flat(0), hit_chance(0),
melee_damage_bonus_flat(0), skilldmgtaken_bonus_flat(0)
melee_damage_bonus_flat(0), skilldmgtaken_bonus_flat(0),
range_percent(0)
{ }
float damage_percent;
+1 -1
View File
@@ -334,7 +334,7 @@ void DialogueWindow::Render(Client *c, std::string markdown)
if (responses.size() > 1) {
for (auto &r: responses) {
bracket_responses.emplace_back(
fmt::format("[{}]", Saylink::Create(r, false))
fmt::format("[{}]", Saylink::Create(r))
);
}
}
+28
View File
@@ -56,6 +56,7 @@ void perl_register_doors();
void perl_register_expedition();
void perl_register_expedition_lock_messages();
void perl_register_bot();
void perl_register_buff();
#endif // EMBPERL_XS_CLASSES
#endif // EMBPERL_XS
@@ -200,6 +201,8 @@ const char* QuestEventSubroutines[_LargestEventID] = {
"EVENT_ENTITY_VARIABLE_DELETE",
"EVENT_ENTITY_VARIABLE_SET",
"EVENT_ENTITY_VARIABLE_UPDATE",
"EVENT_AA_LOSS",
"EVENT_SPELL_BLOCKED",
// Add new events before these or Lua crashes
"EVENT_SPELL_EFFECT_BOT",
@@ -1174,6 +1177,7 @@ void PerlembParser::MapFunctions()
perl_register_expedition();
perl_register_expedition_lock_messages();
perl_register_bot();
perl_register_buff();
#endif // EMBPERL_XS_CLASSES
}
@@ -1934,6 +1938,25 @@ void PerlembParser::ExportEventVariables(
break;
}
case EVENT_SPELL_BLOCKED: {
Seperator sep(data);
const uint32 blocking_spell_id = Strings::ToUnsignedInt(sep.arg[0]);
const uint32 cast_spell_id = Strings::ToUnsignedInt(sep.arg[1]);
ExportVar(package_name.c_str(), "blocking_spell_id", blocking_spell_id);
ExportVar(package_name.c_str(), "cast_spell_id", cast_spell_id);
if (IsValidSpell(blocking_spell_id)) {
ExportVar(package_name.c_str(), "blocking_spell", "Spell", (void*) &spells[blocking_spell_id]);
}
if (IsValidSpell(cast_spell_id)) {
ExportVar(package_name.c_str(), "cast_spell", "Spell", (void*) &spells[cast_spell_id]);
}
break;
}
//tradeskill events
case EVENT_COMBINE_SUCCESS:
case EVENT_COMBINE_FAILURE: {
@@ -2273,6 +2296,11 @@ void PerlembParser::ExportEventVariables(
break;
}
case EVENT_AA_LOSS: {
ExportVar(package_name.c_str(), "aa_lost", data);
break;
}
case EVENT_AA_EXP_GAIN: {
ExportVar(package_name.c_str(), "aa_exp_gained", data);
break;
+45 -18
View File
@@ -380,12 +380,12 @@ void Perl__settimer(std::string timer_name, uint32 seconds)
void Perl__settimer(std::string timer_name, uint32 seconds, Mob* m)
{
quest_manager.settimer(timer_name, seconds);
quest_manager.settimer(timer_name, seconds, m);
}
void Perl__settimer(std::string timer_name, uint32 seconds, EQ::ItemInstance* inst)
{
quest_manager.settimer(timer_name, seconds);
quest_manager.settimerMS(timer_name, seconds * 1000, inst);
}
void Perl__settimerMS(std::string timer_name, uint32 milliseconds)
@@ -1579,9 +1579,7 @@ std::string Perl__GetCharactersInInstance(uint16 instance_id)
char_id_string = fmt::format("{} player(s) in instance: ", character_ids.size());
auto iter = character_ids.begin();
while (iter != character_ids.end()) {
char char_name[64];
database.GetCharName(*iter, char_name);
char_id_string += char_name;
char_id_string += database.GetCharName(*iter);
char_id_string += "(";
char_id_string += itoa(*iter);
char_id_string += ")";
@@ -1654,20 +1652,19 @@ void Perl__FlagInstanceByRaidLeader(uint32 zone, uint16 version)
quest_manager.FlagInstanceByRaidLeader(zone, version);
}
std::string Perl__saylink(const char* text)
std::string Perl__saylink(std::string text)
{
// const cast is safe since, target api doesn't modify it
return quest_manager.saylink(const_cast<char*>(text), false, text);
return Saylink::Create(text);
}
std::string Perl__saylink(const char* text, bool silent)
std::string Perl__saylink(std::string text, bool silent)
{
return quest_manager.saylink(const_cast<char*>(text), silent, text);
return Saylink::Create(text, silent);
}
std::string Perl__saylink(const char* text, bool silent, const char* link_name)
std::string Perl__saylink(std::string text, bool silent, std::string link_name)
{
return quest_manager.saylink(const_cast<char*>(text), silent, link_name);
return Saylink::Create(text, silent, link_name);
}
std::string Perl__getcharnamebyid(uint32 char_id)
@@ -5833,6 +5830,31 @@ uint16 Perl__GetBotRaceByID(uint32 bot_id)
return database.botdb.GetBotRaceByID(bot_id);
}
std::string Perl__silent_saylink(std::string text)
{
return Saylink::Silent(text);
}
std::string Perl__silent_saylink(std::string text, std::string link_name)
{
return Saylink::Silent(text, link_name);
}
uint16 Perl__get_class_bitmask(uint8 class_id)
{
return GetPlayerClassBit(class_id);
}
uint32 Perl__get_deity_bitmask(uint16 deity_id)
{
return static_cast<uint32>(EQ::deity::GetDeityBitmask(static_cast<EQ::deity::DeityType>(deity_id)));
}
uint16 Perl__get_race_bitmask(uint16 race_id)
{
return GetPlayerRaceBit(race_id);
}
void perl_register_quest()
{
perl::interpreter perl(PERL_GET_THX);
@@ -6479,9 +6501,11 @@ void perl_register_quest()
package.add("getconsiderlevelname", &Perl__getconsiderlevelname);
package.add("gethexcolorcode", &Perl__gethexcolorcode);
package.add("getcurrencyid", &Perl__getcurrencyid);
package.add("get_class_bitmask", &Perl__get_class_bitmask);
package.add("get_data", &Perl__get_data);
package.add("get_data_expires", &Perl__get_data_expires);
package.add("get_data_remaining", &Perl__get_data_remaining);
package.add("get_deity_bitmask", &Perl__get_deity_bitmask);
package.add("get_dz_task_id", &Perl__get_dz_task_id);
package.add("getexpmodifierbycharid", (double(*)(uint32, uint32))&Perl__getexpmodifierbycharid);
package.add("getexpmodifierbycharid", (double(*)(uint32, uint32, int16))&Perl__getexpmodifierbycharid);
@@ -6514,6 +6538,7 @@ void perl_register_quest()
package.add("getgroupidbycharid", &Perl__getgroupidbycharid);
package.add("getinventoryslotname", &Perl__getinventoryslotname);
package.add("getraididbycharid", &Perl__getraididbycharid);
package.add("get_race_bitmask", &Perl__get_race_bitmask);
package.add("get_recipe_component_item_ids", &Perl__GetRecipeComponentItemIDs);
package.add("get_recipe_container_item_ids", &Perl__GetRecipeContainerItemIDs);
package.add("get_recipe_fail_item_ids", &Perl__GetRecipeFailItemIDs);
@@ -6644,9 +6669,9 @@ void perl_register_quest()
package.add("say", (void(*)(const char*, uint8, int))&Perl__say);
package.add("say", (void(*)(const char*, uint8, int, int))&Perl__say);
package.add("say", (void(*)(const char*, uint8, int, int, int))&Perl__say);
package.add("saylink", (std::string(*)(const char*))&Perl__saylink);
package.add("saylink", (std::string(*)(const char*, bool))&Perl__saylink);
package.add("saylink", (std::string(*)(const char*, bool, const char*))&Perl__saylink);
package.add("saylink", (std::string(*)(std::string))&Perl__saylink);
package.add("saylink", (std::string(*)(std::string, bool))&Perl__saylink);
package.add("saylink", (std::string(*)(std::string, bool, std::string))&Perl__saylink);
package.add("scribespells", (int(*)(int))&Perl__scribespells);
package.add("scribespells", (int(*)(int, int))&Perl__scribespells);
package.add("secondstotime", &Perl__secondstotime);
@@ -6684,9 +6709,9 @@ void perl_register_quest()
package.add("settarget", &Perl__settarget);
package.add("settime", (void(*)(int, int))&Perl__settime);
package.add("settime", (void(*)(int, int, bool))&Perl__settime);
package.add("settimer", (void(*)(std::string, uint32))&Perl__settimer),
package.add("settimer", (void(*)(std::string, uint32, EQ::ItemInstance*))&Perl__settimer),
package.add("settimer", (void(*)(std::string, uint32, Mob*))&Perl__settimer),
package.add("settimer", (void(*)(std::string, uint32))&Perl__settimer);
package.add("settimer", (void(*)(std::string, uint32, EQ::ItemInstance*))&Perl__settimer);
package.add("settimer", (void(*)(std::string, uint32, Mob*))&Perl__settimer);
package.add("settimerMS", (void(*)(std::string, uint32))&Perl__settimerMS);
package.add("settimerMS", (void(*)(std::string, uint32, EQ::ItemInstance*))&Perl__settimerMS);
package.add("settimerMS", (void(*)(std::string, uint32, Mob*))&Perl__settimerMS);
@@ -6698,6 +6723,8 @@ void perl_register_quest()
package.add("signal", (void(*)(int, int))&Perl__signal);
package.add("signalwith", (void(*)(int, int))&Perl__signalwith);
package.add("signalwith", (void(*)(int, int, int))&Perl__signalwith);
package.add("silent_saylink", (std::string(*)(std::string))&Perl__silent_saylink);
package.add("silent_saylink", (std::string(*)(std::string, std::string))&Perl__silent_saylink);
package.add("snow", &Perl__snow);
package.add("spawn", &Perl__spawn);
package.add("spawn2", &Perl__spawn2);
+8 -1
View File
@@ -2152,7 +2152,7 @@ Group *EntityList::GetGroupByLeaderName(const char *leader)
iterator = group_list.begin();
while (iterator != group_list.end()) {
if (!strcmp((*iterator)->GetLeaderName(), leader))
if (!strcmp((*iterator)->GetLeaderName().c_str(), leader))
return *iterator;
++iterator;
}
@@ -2675,6 +2675,10 @@ void EntityList::RemoveAllMobs()
{
auto it = mob_list.begin();
while (it != mob_list.end()) {
if (!it->second) {
++it;
continue;
}
safe_delete(it->second);
free_ids.push(it->first);
it = mob_list.erase(it);
@@ -2812,6 +2816,9 @@ bool EntityList::RemoveMob(uint16 delete_id)
auto it = mob_list.find(delete_id);
if (it != mob_list.end()) {
if (!it->second) {
return false;
}
if (npc_list.count(delete_id)) {
entity_list.RemoveNPC(delete_id);
}
+2
View File
@@ -142,6 +142,8 @@ typedef enum {
EVENT_ENTITY_VARIABLE_DELETE,
EVENT_ENTITY_VARIABLE_SET,
EVENT_ENTITY_VARIABLE_UPDATE,
EVENT_AA_LOSS,
EVENT_SPELL_BLOCKED,
// Add new events before these or Lua crashes
EVENT_SPELL_EFFECT_BOT,
+1 -8
View File
@@ -125,7 +125,7 @@ bool ExpeditionRequest::CanGroupRequest(Group* group)
}
// Group::GetLeaderName() is broken if group formed across zones, ask database instead
m_leader_name = m_leader ? m_leader->GetName() : GetGroupLeaderName(group->GetID()); // group->GetLeaderName();
m_leader_name = m_leader ? m_leader->GetName() : group->GetLeaderName();
m_leader_id = m_leader ? m_leader->CharacterID() : database.GetCharacterID(m_leader_name);
std::vector<std::string> member_names;
@@ -148,13 +148,6 @@ bool ExpeditionRequest::CanGroupRequest(Group* group)
return CanMembersJoin(member_names);
}
std::string ExpeditionRequest::GetGroupLeaderName(uint32_t group_id)
{
char leader_name_buffer[64] = { 0 };
database.GetGroupLeadershipInfo(group_id, leader_name_buffer);
return std::string(leader_name_buffer);
}
bool ExpeditionRequest::CanMembersJoin(const std::vector<std::string>& member_names)
{
if (member_names.empty())
-1
View File
@@ -54,7 +54,6 @@ private:
bool CanRaidRequest(Raid* raid);
bool CanGroupRequest(Group* group);
bool CheckMembersForConflicts(const std::vector<std::string>& member_names);
std::string GetGroupLeaderName(uint32_t group_id);
bool IsPlayerCountValidated();
bool SaveLeaderLockouts(const std::vector<ExpeditionLockoutTimer>& leader_lockouts);
void SendLeaderMemberInExpedition(const std::string& member_name, bool is_solo);
+32 -37
View File
@@ -44,17 +44,14 @@ extern WorldServer worldserver;
struct NPCType;
//max number of items which can be in the foraging table
//for a given zone.
#define FORAGE_ITEM_LIMIT 50
//max number of items which can be in the foraging
// and fishing tables for a given zone.
constexpr uint8 FORAGE_ITEM_LIMIT = 50;
constexpr uint8 FISHING_ITEM_LIMIT = 50;
uint32 ZoneDatabase::LoadForage(uint32 zone_id, uint8 skill_level)
{
uint32 forage_items[FORAGE_ITEM_LIMIT];
for (uint16 slot_id = 0; slot_id < FORAGE_ITEM_LIMIT; slot_id++) {
forage_items[slot_id] = 0;
}
uint32 forage_items[FORAGE_ITEM_LIMIT] = {};
const auto& l = ForageRepository::GetWhere(
*this,
@@ -77,7 +74,7 @@ uint32 ZoneDatabase::LoadForage(uint32 zone_id, uint8 skill_level)
l.size() != 1 ? "s" : ""
);
int forage_chances[FORAGE_ITEM_LIMIT];
int forage_chances[FORAGE_ITEM_LIMIT] = {};
int current_chance = 0;
uint32 item_id = 0;
@@ -118,13 +115,8 @@ uint32 ZoneDatabase::LoadForage(uint32 zone_id, uint8 skill_level)
uint32 ZoneDatabase::LoadFishing(uint32 zone_id, uint8 skill_level, uint32 &npc_id, uint8 &npc_chance)
{
uint32 fishing_items[50];
int fishing_chances[50];
for (uint16 slot_id = 0; slot_id < 50; slot_id++) {
fishing_items[slot_id] = 0;
fishing_chances[slot_id] = 0;
}
uint32 fishing_items[FISHING_ITEM_LIMIT] = {};
int fishing_chances[FISHING_ITEM_LIMIT] = {};
const auto& l = FishingRepository::GetWhere(
*this,
@@ -146,15 +138,15 @@ uint32 ZoneDatabase::LoadFishing(uint32 zone_id, uint8 skill_level, uint32 &npc_
l.size() != 1 ? "s" : ""
);
uint32 npc_ids[50];
uint32 npc_chances[50];
uint32 npc_ids[FISHING_ITEM_LIMIT] = {};
uint32 npc_chances[FISHING_ITEM_LIMIT] = {};
int current_chance = 0;
uint32 item_id = 0;
uint8 count = 0;
for (const auto &e: l) {
if (count >= 50) {
if (count >= FISHING_ITEM_LIMIT) {
break;
}
@@ -164,6 +156,8 @@ uint32 ZoneDatabase::LoadFishing(uint32 zone_id, uint8 skill_level, uint32 &npc_
npc_chances[count] = e.npc_chance;
current_chance = fishing_chances[count];
count++;
}
npc_id = 0;
@@ -175,7 +169,7 @@ uint32 ZoneDatabase::LoadFishing(uint32 zone_id, uint8 skill_level, uint32 &npc_
const int roll = zone->random.Int(1, current_chance);
for (uint16 i = 0; i < count; i++) {
for (uint8 i = 0; i < count; i++) {
if (roll > fishing_chances[i]) {
continue;
}
@@ -269,32 +263,33 @@ void Client::GoFish(bool guarantee, bool use_bait)
fishing_timer.Disable();
//we're doing this a second time (1st in Client::Handle_OP_Fishing) to make sure that, between when we started fishing & now, we're still able to fish (in case we move, change equip, etc)
if (!CanFish()) //if we can't fish here, we don't need to bother with the rest
if (!CanFish()) { //if we can't fish here, we don't need to bother with the rest
return;
}
//multiple entries yeilds higher probability of dropping...
uint32 common_fish_ids[MAX_COMMON_FISH_IDS] = {
1038, // Tattered Cloth Sandals
1038, // Tattered Cloth Sandals
1038, // Tattered Cloth Sandals
1038, // Tattered Cloth Sandals
1038, // Tattered Cloth Sandals
1038, // Tattered Cloth Sandals
13019, // Fresh Fish
13076, // Fish Scales
13076, // Fish Scales
7007, // Rusty Dagger
7007, // Rusty Dagger
7007 // Rusty Dagger
7007, // Rusty Dagger
7007, // Rusty Dagger
7007 // Rusty Dagger
};
//success formula is not researched at all
int fishing_skill = GetSkill(EQ::skills::SkillFishing); //will take into account skill bonuses on pole & bait
uint16 fishing_skill = GetSkill(EQ::skills::SkillFishing); //will take into account skill bonuses on pole & bait
//make sure we still have a fishing pole on:
int32 bslot = m_inv.HasItemByUse(EQ::item::ItemTypeFishingBait, 1, invWhereWorn | invWherePersonal);
int16 bslot = m_inv.HasItemByUse(EQ::item::ItemTypeFishingBait, 1, invWhereWorn | invWherePersonal);
const EQ::ItemInstance* Bait = nullptr;
if (bslot != INVALID_INDEX)
if (bslot != INVALID_INDEX) {
Bait = m_inv.GetItem(bslot);
}
//if the bait isnt equipped, need to add its skill bonus
if (bslot >= EQ::invslot::GENERAL_BEGIN && Bait != nullptr && Bait->GetItem()->SkillModType == EQ::skills::SkillFishing) {
@@ -309,8 +304,8 @@ void Client::GoFish(bool guarantee, bool use_bait)
if (guarantee || zone->random.Int(0,175) < fishing_skill) {
uint32 food_id = 0;
//25% chance to fish an item.
if (zone->random.Int(0, 399) <= fishing_skill ) {
//chance to fish a zone item.
if (zone->random.Int(0, RuleI(Zone, FishingChance)) <= fishing_skill ) {
uint32 npc_id = 0;
uint8 npc_chance = 0;
food_id = content_db.LoadFishing(m_pp.zone_id, fishing_skill, npc_id, npc_chance);
@@ -348,7 +343,7 @@ void Client::GoFish(bool guarantee, bool use_bait)
DeleteItemInInventory(bslot, 1, true); //do we need client update?
}
if(food_id == 0) {
if (food_id == 0) {
int index = zone->random.Int(0, MAX_COMMON_FISH_IDS-1);
food_id = (RuleB(Character, UseNoJunkFishing) ? 13019 : common_fish_ids[index]);
}
@@ -449,9 +444,9 @@ void Client::ForageItem(bool guarantee) {
13419, // Vegetables
13048, // Rabbit Meat
13047, // Roots
13044, // Pod Of Water
14905, // mushroom
13106 // Fishing Grubs
13044, // Pod of Water
14905, // Mushroom
13106 // Fishing Grubs
};
// these may need to be fine tuned, I am just guessing here
+4 -4
View File
@@ -82,7 +82,7 @@ void command_logs(Client *c, const Seperator *sep)
}
gmsay.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
Saylink::Create(
fmt::format("#logs set gmsay {} {}", index, i), false, std::to_string(i)
)
);
@@ -96,7 +96,7 @@ void command_logs(Client *c, const Seperator *sep)
}
file.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
Saylink::Create(
fmt::format("#logs set file {} {}", index, i), false, std::to_string(i)
)
);
@@ -110,7 +110,7 @@ void command_logs(Client *c, const Seperator *sep)
}
console.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
Saylink::Create(
fmt::format("#logs set console {} {}", index, i), false, std::to_string(i)
)
);
@@ -124,7 +124,7 @@ void command_logs(Client *c, const Seperator *sep)
}
discord.emplace_back(
EQ::SayLinkEngine::GenerateQuestSaylink(
Saylink::Create(
fmt::format("#logs set discord {} {}", index, i), false, std::to_string(i)
)
);
+2 -2
View File
@@ -116,13 +116,13 @@ void command_lootsim(Client *c, const Seperator *sep)
c->Message(Chat::White, fmt::format("# Global Loot Table ID [{}]", id).c_str());
c->SendChatLineBreak();
loot_table = zone->GetLootTable(loottable_id);
loot_table = zone->GetLootTable(id);
if (!loot_table) {
c->Message(Chat::Red, fmt::format("Global Loot table not found [{}]", id).c_str());
continue;
}
le = zone->GetLootTableEntries(loottable_id);
le = zone->GetLootTableEntries(id);
// translate above for loop using loot_table_entries
for (auto &e: le) {
+1 -1
View File
@@ -53,7 +53,7 @@ void command_movechar(Client *c, const Seperator *sep)
return;
}
const bool moved = database.MoveCharacterToZone(character_name.c_str(), zone_id);
const bool moved = database.MoveCharacterToZone(character_name, zone_id);
std::string moved_string = moved ? "Succeeded" : "Failed";
c->Message(
Chat::White,
+5
View File
@@ -34,6 +34,7 @@ void command_reload(Client *c, const Seperator *sep)
bool is_perl_export = !strcasecmp(sep->arg[1], "perl_export");
bool is_quest = !strcasecmp(sep->arg[1], "quest") || (is_rq_alias);
bool is_rules = !strcasecmp(sep->arg[1], "rules");
bool is_skill_caps = !strcasecmp(sep->arg[1], "skill_caps");
bool is_static = !strcasecmp(sep->arg[1], "static");
bool is_tasks = !strcasecmp(sep->arg[1], "tasks");
bool is_titles = !strcasecmp(sep->arg[1], "titles");
@@ -66,6 +67,7 @@ void command_reload(Client *c, const Seperator *sep)
!is_perl_export &&
!is_quest &&
!is_rules &&
!is_skill_caps &&
!is_static &&
!is_tasks &&
!is_titles &&
@@ -161,6 +163,9 @@ void command_reload(Client *c, const Seperator *sep)
} else if (is_rules) {
c->Message(Chat::White, "Attempting to reload Rules globally.");
pack = new ServerPacket(ServerOP_ReloadRules, 0);
} else if (is_skill_caps) {
c->Message(Chat::White, "Attempting to reload Skill Caps globally.");
pack = new ServerPacket(ServerOP_ReloadSkillCaps, 0);
} else if (is_static) {
c->Message(Chat::White, "Attempting to reload Static Zone Data globally.");
pack = new ServerPacket(ServerOP_ReloadStaticZoneData, 0);
+1 -1
View File
@@ -7,7 +7,7 @@ void SetHPFull(Client *c, const Seperator *sep)
t = c->GetTarget();
}
t->Heal();
t->RestoreHealth();
c->Message(
Chat::White,
+183 -135
View File
@@ -203,155 +203,141 @@ void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinu
}
}
bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 CharacterID, bool ismerc)
bool Group::AddMember(Mob* new_member, std::string new_member_name, uint32 character_id, bool is_merc)
{
bool InZone = true;
bool in_zone = true;
// This method should either be passed a Mob*, if the new member is in this zone, or a nullptr Mob*
// and the name and CharacterID of the new member, if they are out of zone.
if(!newmember && !NewMemberName)
{
// and the name and character_id of the new member, if they are out of zone.
if (!new_member && new_member_name.empty()) {
return false;
}
if(GroupCount() >= MAX_GROUP_MEMBERS) //Sanity check for merging groups together.
{
if (GroupCount() >= MAX_GROUP_MEMBERS) { //Sanity check for merging groups together.
return false;
}
if(!newmember)
{
InZone = false;
}
else
{
NewMemberName = newmember->GetCleanName();
if (!new_member) {
in_zone = false;
} else {
new_member_name = new_member->GetCleanName();
if(newmember->IsClient())
{
CharacterID = newmember->CastToClient()->CharacterID();
if (new_member->IsClient()) {
character_id = new_member->CastToClient()->CharacterID();
}
if(newmember->IsMerc())
{
Client* owner = newmember->CastToMerc()->GetMercenaryOwner();
if(owner)
{
CharacterID = owner->CastToClient()->CharacterID();
if (new_member->IsMerc()) {
Client* o = new_member->CastToMerc()->GetMercenaryOwner();
if (o) {
character_id = o->CastToClient()->CharacterID();
}
ismerc = true;
is_merc = true;
}
}
// See if they are already in the group
uint32 i = 0;
for (i = 0; i < MAX_GROUP_MEMBERS; ++i)
{
if (!strcasecmp(membername[i], NewMemberName))
{
for (const auto& m : membername) {
if (Strings::EqualFold(m, new_member_name)) {
return false;
}
}
// Put them in the group
for (i = 0; i < MAX_GROUP_MEMBERS; ++i)
{
if (membername[i][0] == '\0')
{
if(InZone)
{
members[i] = newmember;
for (int slot_id = 0; slot_id < MAX_GROUP_MEMBERS; ++slot_id) {
if (membername[slot_id][0] == '\0') {
if (in_zone) {
members[slot_id] = new_member;
}
strcpy(membername[i], NewMemberName);
MemberRoles[i] = 0;
strcpy(membername[slot_id], new_member_name.c_str());
MemberRoles[slot_id] = 0;
break;
}
}
// Is this even possible based on the above loops? Remove?
if (i == MAX_GROUP_MEMBERS)
{
return false;
}
int x=1;
int x = 1;
//build the template join packet
auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct));
GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer;
strcpy(gj->membername, NewMemberName);
gj->action = groupActJoin;
auto gj = (GroupJoin_Struct*) outapp->pBuffer;
strcpy(gj->membername, new_member_name.c_str());
gj->action = groupActJoin;
gj->leader_aas = LeaderAbilities;
for (i = 0;i < MAX_GROUP_MEMBERS; i++)
{
if (members[i] != nullptr && members[i] != newmember)
{
for (int slot_id = 0; slot_id < MAX_GROUP_MEMBERS; slot_id++) {
if (members[slot_id] && members[slot_id] != new_member) {
//fill in group join & send it
strcpy(gj->yourname, members[i]->GetCleanName());
if(members[i]->IsClient())
{
members[i]->CastToClient()->QueuePacket(outapp);
strcpy(gj->yourname, members[slot_id]->GetCleanName());
if (members[slot_id]->IsClient()) {
members[slot_id]->CastToClient()->QueuePacket(outapp);
//put new member into existing group members' list(s)
strcpy(members[i]->CastToClient()->GetPP().groupMembers[GroupCount()-1], NewMemberName);
strcpy(
members[slot_id]->CastToClient()->GetPP().groupMembers[GroupCount() - 1],
new_member_name.c_str()
);
}
//put existing group member(s) into the new member's list
if(InZone && newmember && newmember->IsClient())
{
if(IsLeader(members[i]))
{
strcpy(newmember->CastToClient()->GetPP().groupMembers[0], members[i]->GetCleanName());
}
else
{
strcpy(newmember->CastToClient()->GetPP().groupMembers[x], members[i]->GetCleanName());
if (in_zone && new_member && new_member->IsClient()) {
if (IsLeader(members[slot_id])) {
strcpy(new_member->CastToClient()->GetPP().groupMembers[0], members[slot_id]->GetCleanName());
} else {
strcpy(new_member->CastToClient()->GetPP().groupMembers[x], members[slot_id]->GetCleanName());
++x;
}
}
}
}
if(InZone && newmember)
{
if (in_zone && new_member) {
//put new member in his own list.
newmember->SetGrouped(true);
new_member->SetGrouped(true);
if(newmember->IsClient())
{
strcpy(newmember->CastToClient()->GetPP().groupMembers[x], NewMemberName);
newmember->CastToClient()->Save();
database.SetGroupID(NewMemberName, GetID(), newmember->CastToClient()->CharacterID(), false);
SendMarkedNPCsToMember(newmember->CastToClient());
if (new_member->IsClient()) {
strcpy(new_member->CastToClient()->GetPP().groupMembers[x], new_member_name.c_str());
NotifyMainTank(newmember->CastToClient(), 1);
NotifyMainAssist(newmember->CastToClient(), 1);
NotifyPuller(newmember->CastToClient(), 1);
new_member->CastToClient()->Save();
AddToGroup(new_member);
SendMarkedNPCsToMember(new_member->CastToClient());
NotifyMainTank(new_member->CastToClient(), 1);
NotifyMainAssist(new_member->CastToClient(), 1);
NotifyPuller(new_member->CastToClient(), 1);
}
if(newmember->IsMerc())
{
Client* owner = newmember->CastToMerc()->GetMercenaryOwner();
if(owner)
{
database.SetGroupID(NewMemberName, GetID(), owner->CharacterID(), true);
if (new_member->IsMerc()) {
Client* o = new_member->CastToMerc()->GetMercenaryOwner();
if (o) {
AddToGroup(new_member);
}
}
Group* group = newmember->CastToClient()->GetGroup();
if (group) {
group->SendHPManaEndPacketsTo(newmember);
group->SendHPPacketsFrom(newmember);
Group* g = new_member->CastToClient()->GetGroup();
if (g) {
g->SendHPManaEndPacketsTo(new_member);
g->SendHPPacketsFrom(new_member);
}
}
else
{
database.SetGroupID(NewMemberName, GetID(), CharacterID, ismerc);
} else {
AddToGroup(
AddToGroupRequest{
.mob = nullptr,
.member_name = new_member_name,
.character_id = character_id,
}
);
}
if (newmember && newmember->IsClient())
newmember->CastToClient()->JoinGroupXTargets(this);
if (new_member && new_member->IsClient()) {
new_member->CastToClient()->JoinGroupXTargets(this);
}
safe_delete(outapp);
@@ -362,20 +348,18 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte
return true;
}
void Group::AddMember(const char *NewMemberName)
void Group::AddMember(const std::string& new_member_name)
{
// This method should be called when both the new member and the group leader are in a different zone to this one.
for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i)
if(!strcasecmp(membername[i], NewMemberName))
{
for (const auto& m : membername) {
if (Strings::EqualFold(m, new_member_name)) {
return;
}
}
for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i)
{
if (membername[i][0] == '\0')
{
strcpy(membername[i], NewMemberName);
for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) {
if (membername[i][0] == '\0') {
strcpy(membername[i], new_member_name.c_str());
MemberRoles[i] = 0;
break;
}
@@ -620,7 +604,7 @@ bool Group::DelMemberOOZ(const char *Name) {
if(!strcasecmp(Name, membername[i]))
// This shouldn't be called if the member is in this zone.
if(!members[i]) {
if(!strncmp(GetLeaderName(), Name, 64))
if(!strncmp(GetLeaderName().c_str(), Name, 64))
{
//TODO: Transfer leadership if leader disbands OOZ.
UpdateGroupAAs();
@@ -703,7 +687,7 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
}
}
if (!GetLeaderName())
if (GetLeaderName().empty())
{
DisbandGroup();
return true;
@@ -752,7 +736,7 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
if(oldmember->IsClient())
{
database.SetGroupID(oldmember->GetCleanName(), 0, oldmember->CastToClient()->CharacterID(), false);
RemoveFromGroup(oldmember);
}
if(oldmember->IsMerc())
@@ -760,7 +744,7 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
Client* owner = oldmember->CastToMerc()->GetMercenaryOwner();
if(owner)
{
database.SetGroupID(oldmember->GetCleanName(), 0, owner->CharacterID(), true);
RemoveFromGroup(oldmember);
}
}
@@ -943,7 +927,7 @@ void Group::DisbandGroup(bool joinraid) {
}
strcpy(gu->yourname, members[i]->GetCleanName());
database.SetGroupID(members[i]->GetCleanName(), 0, members[i]->CastToClient()->CharacterID(), false);
RemoveFromGroup(members[i]);
members[i]->CastToClient()->QueuePacket(outapp);
SendMarkedNPCsToMember(members[i]->CastToClient(), true);
if (!joinraid)
@@ -955,7 +939,7 @@ void Group::DisbandGroup(bool joinraid) {
Client* owner = members[i]->CastToMerc()->GetMercenaryOwner();
if(owner)
{
database.SetGroupID(members[i]->GetCleanName(), 0, owner->CharacterID(), true);
RemoveFromGroup(members[i]);
}
}
@@ -1148,31 +1132,30 @@ void Group::TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float
bool Group::LearnMembers() {
auto rows = GroupIdRepository::GetWhere(
const auto& l = GroupIdRepository::GetWhere(
database,
fmt::format(
"groupid = {}",
"`group_id` = {}",
GetID()
)
);
if (rows.empty()) {
if (l.empty()) {
LogError(
"Error getting group members for group [{}]",
GetID()
);
}
for(int i = 0; i < MAX_GROUP_MEMBERS; ++i)
{
for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) {
members[i] = nullptr;
memset(membername[i],0,64);
memset(membername[i], 0, 64);
MemberRoles[i] = 0;
}
int memberIndex = 0;
for (const auto& member : rows) {
if (memberIndex >= MAX_GROUP_MEMBERS) {
int member_index = 0;
for (const auto& e : l) {
if (member_index >= MAX_GROUP_MEMBERS) {
LogError(
"Too many members in group [{}]",
GetID()
@@ -1180,14 +1163,15 @@ bool Group::LearnMembers() {
break;
}
if (member.name.empty()) {
members[memberIndex] = nullptr;
membername[memberIndex][0] = '\0';
if (e.name.empty()) {
members[member_index] = nullptr;
membername[member_index][0] = '\0';
} else {
members[memberIndex] = nullptr;
strn0cpy(membername[memberIndex], member.name.c_str(), 64);
members[member_index] = nullptr;
strn0cpy(membername[member_index], e.name.c_str(), 64);
}
++memberIndex;
++member_index;
}
VerifyGroup();
@@ -1264,10 +1248,10 @@ void Client::LeaveGroup() {
else
{
//force things a little
database.SetGroupID(GetCleanName(), 0, CharacterID(), false);
if (GetMerc())
{
database.SetGroupID(GetMerc()->GetCleanName(), 0, CharacterID(), true);
Group::RemoveFromGroup(this);
if (GetMerc()) {
Group::RemoveFromGroup(GetMerc());
}
}
@@ -1676,7 +1660,7 @@ void Group::NotifyMainTank(Client *c, uint8 toggle)
strn0cpy(grs->Name1, MainTankName.c_str(), sizeof(grs->Name1));
strn0cpy(grs->Name2, GetLeaderName(), sizeof(grs->Name2));
strn0cpy(grs->Name2, GetLeaderName().c_str(), sizeof(grs->Name2));
grs->RoleNumber = 1;
@@ -1729,7 +1713,7 @@ void Group::NotifyMainAssist(Client *c, uint8 toggle)
strn0cpy(grs->Name1, MainAssistName.c_str(), sizeof(grs->Name1));
strn0cpy(grs->Name2, GetLeaderName(), sizeof(grs->Name2));
strn0cpy(grs->Name2, GetLeaderName().c_str(), sizeof(grs->Name2));
grs->RoleNumber = 2;
@@ -1771,7 +1755,7 @@ void Group::NotifyPuller(Client *c, uint8 toggle)
strn0cpy(grs->Name1, PullerName.c_str(), sizeof(grs->Name1));
strn0cpy(grs->Name2, GetLeaderName(), sizeof(grs->Name2));
strn0cpy(grs->Name2, GetLeaderName().c_str(), sizeof(grs->Name2));
grs->RoleNumber = 3;
@@ -2511,8 +2495,72 @@ 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);
std::string Group::GetLeaderName() {
return database.GetGroupLeaderName(GetID());
}
void Group::RemoveFromGroup(Mob* m)
{
uint32 bot_id = 0;
uint32 character_id = 0;
uint32 merc_id = 0;
if (m->IsBot()) {
bot_id = m->CastToBot()->GetBotID();
} else if (m->IsClient()) {
character_id = m->CastToClient()->CharacterID();
} else if (m->IsMerc()) {
merc_id = m->CastToMerc()->GetMercenaryID();
}
GroupIdRepository::DeleteWhere(
database,
fmt::format(
"`character_id` = {} AND `bot_id` = {} AND `merc_id` = {}",
character_id,
bot_id,
merc_id
)
);
}
void Group::AddToGroup(Mob* m)
{
AddToGroup(
AddToGroupRequest{
.mob = m
}
);
}
// Handles database-side, should eventually be consolidated to handle memory-based group stuff as well
void Group::AddToGroup(AddToGroupRequest r)
{
uint32 bot_id = 0;
uint32 character_id = r.character_id;
uint32 merc_id = 0;
std::string name = r.member_name;
if (r.mob) {
if (r.mob->IsBot()) {
bot_id = r.mob->CastToBot()->GetBotID();
} else if (r.mob->IsClient()) {
character_id = r.mob->CastToClient()->CharacterID();
} else if (r.mob->IsMerc()) {
merc_id = r.mob->CastToMerc()->GetMercenaryID();
}
name = r.mob->GetCleanName();
}
GroupIdRepository::ReplaceOne(
database,
GroupIdRepository::GroupId{
.group_id = GetID(),
.name = name,
.character_id = character_id,
.bot_id = bot_id,
.merc_id = merc_id
}
);
}
+13 -5
View File
@@ -52,8 +52,15 @@ public:
Group(uint32 gid);
~Group();
bool AddMember(Mob* newmember, const char* NewMemberName = nullptr, uint32 CharacterID = 0, bool ismerc = false);
void AddMember(const char* NewMemberName);
struct AddToGroupRequest {
Mob* mob = nullptr;
// Only used cross-zone, otherwise use Mob* mob
std::string member_name = std::string();
uint32 character_id = 0;
};
bool AddMember(Mob* new_member, std::string new_member_name = std::string(), uint32 character_id = 0, bool is_merc = false);
void AddMember(const std::string& new_member_name);
void SendUpdate(uint32 type,Mob* member);
void SendLeadershipAAUpdate();
void SendWorldGroup(uint32 zone_id,Mob* zoningmember);
@@ -76,7 +83,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 GetGroupLeaderName(GetID()).c_str(); };
std::string GetLeaderName();
void SendHPManaEndPacketsTo(Mob* newmember);
void SendHPPacketsFrom(Mob* member);
void SendManaPacketFrom(Mob* member);
@@ -145,6 +152,9 @@ public:
void SetDirtyAutoHaters();
inline XTargetAutoHaters *GetXTargetAutoMgr() { return &m_autohatermgr; }
void JoinRaidXTarget(Raid *raid, bool first = false);
void AddToGroup(AddToGroupRequest r);
void AddToGroup(Mob* m);
static void RemoveFromGroup(Mob* m);
void SetGroupMentor(int percent, char *name);
void ClearGroupMentor();
@@ -177,8 +187,6 @@ private:
int mentor_percent;
XTargetAutoHaters m_autohatermgr;
std::string GetGroupLeaderName(uint32 group_id);
};
#endif
+32 -30
View File
@@ -108,46 +108,48 @@ void HateList::SetHateAmountOnEnt(Mob* other, int64 in_hate, uint64 in_damage)
Mob* HateList::GetDamageTopOnHateList(Mob* hater)
{
Mob* current = nullptr;
Group* grp = nullptr;
Raid* r = nullptr;
uint64 dmg_amt = 0;
Mob* c = nullptr;
Mob* m = nullptr;
Group* g = nullptr;
Raid* r = nullptr;
auto iterator = list.begin();
while (iterator != list.end())
{
grp = nullptr;
uint64 damage = 0;
for (const auto& e : list) {
c = e->entity_on_hatelist;
if (!c) {
continue;
}
g = nullptr;
r = nullptr;
if ((*iterator)->entity_on_hatelist && (*iterator)->entity_on_hatelist->IsClient()){
r = entity_list.GetRaidByClient((*iterator)->entity_on_hatelist->CastToClient());
if (c->IsBot()) {
r = entity_list.GetRaidByBot(c->CastToBot());
} else if (c->IsClient()) {
r = entity_list.GetRaidByClient(c->CastToClient());
}
grp = entity_list.GetGroupByMob((*iterator)->entity_on_hatelist);
g = entity_list.GetGroupByMob(c);
if ((*iterator)->entity_on_hatelist && r){
if (r->GetTotalRaidDamage(hater) >= dmg_amt)
{
current = (*iterator)->entity_on_hatelist;
dmg_amt = r->GetTotalRaidDamage(hater);
if (r) {
if (r->GetTotalRaidDamage(hater) >= damage) {
m = c;
damage = r->GetTotalRaidDamage(hater);
}
}
else if ((*iterator)->entity_on_hatelist != nullptr && grp != nullptr)
{
if (grp->GetTotalGroupDamage(hater) >= dmg_amt)
{
current = (*iterator)->entity_on_hatelist;
dmg_amt = grp->GetTotalGroupDamage(hater);
} else if (g) {
if (g->GetTotalGroupDamage(hater) >= damage) {
m = c;
damage = g->GetTotalGroupDamage(hater);
}
} else if (static_cast<uint64>(e->hatelist_damage) >= damage) {
m = c;
damage = static_cast<uint64>(e->hatelist_damage);
}
else if ((*iterator)->entity_on_hatelist != nullptr && (uint64)(*iterator)->hatelist_damage >= dmg_amt)
{
current = (*iterator)->entity_on_hatelist;
dmg_amt = (*iterator)->hatelist_damage;
}
++iterator;
}
return current;
return m;
}
Mob* HateList::GetClosestEntOnHateList(Mob *hater, bool skip_mezzed, EntityFilterType filter_type) {
+29 -1
View File
@@ -601,7 +601,7 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2,
inst->SetOrnamentHeroModel(ornament_hero_model);
// check to see if item is usable in requested slot
if (enforce_usable && (to_slot >= EQ::invslot::EQUIPMENT_BEGIN && to_slot <= EQ::invslot::EQUIPMENT_END)) {
if (enforce_usable && to_slot != EQ::invslot::SLOT_QUEST && (to_slot >= EQ::invslot::EQUIPMENT_BEGIN && to_slot <= EQ::invslot::EQUIPMENT_END)) {
uint32 slottest = to_slot;
if(!(slots & ((uint32)1 << slottest))) {
Message(
@@ -648,6 +648,34 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2,
RecordPlayerEventLog(PlayerEvent::ITEM_CREATION, e);
}
//We're coming from a quest method.
if (to_slot == EQ::invslot::SLOT_QUEST) {
bool stacking = TryStacking(inst);
if (stacking) {
safe_delete(inst);
return true;
}
else {
bool bag = false;
if (inst->IsClassBag()) {
bag = true;
}
to_slot = m_inv.FindFreeSlot(bag, true, item->Size);
//make sure we are not completely full...
if (to_slot == EQ::invslot::slotCursor || to_slot == INVALID_INDEX) {
if (inst->GetItem()->NoDrop == 0) {
//If it's no drop, force it to the cursor. This carries the risk of deletion if the player already has this item on their cursor
// or if the cursor queue is full. But in this situation, we have little other recourse.
PushItemOnCursor(*inst);
LogInventory("{} has a full inventory and {} is a no drop item. Forcing to cursor", GetName(), inst->GetItem()->Name);
safe_delete(inst);
}
}
}
}
// put item into inventory
if (to_slot == EQ::invslot::slotCursor) {
+411 -5
View File
@@ -22,7 +22,7 @@ void NPC::AddLootTable(uint32 loottable_id, bool is_global)
if (!npctype_id) {
return;
}
if (!is_global) {
m_loot_copper = 0;
m_loot_silver = 0;
@@ -37,6 +37,14 @@ void NPC::AddLootTable(uint32 loottable_id, bool is_global)
return;
}
LogLootDetail(
"Attempting to load loot [{}] loottable [{}] ({}) is_global [{}]",
GetCleanName(),
loottable_id,
l->name,
is_global
);
auto content_flags = ContentFlags{
.min_expansion = l->min_expansion,
.max_expansion = l->max_expansion,
@@ -106,7 +114,12 @@ void NPC::AddLootTable(uint32 loottable_id, bool is_global)
}
}
LogLootDetail("Loaded [{}] Loot Table [{}]", GetCleanName(), loottable_id);
LogLootDetail(
"Loaded [{}] Loot Table [{}] is_global [{}]",
GetCleanName(),
loottable_id,
is_global
);
}
void NPC::AddLootDropTable(uint32 lootdrop_id, uint8 drop_limit, uint8 min_drop)
@@ -128,10 +141,28 @@ void NPC::AddLootDropTable(uint32 lootdrop_id, uint8 drop_limit, uint8 min_drop)
// if this lootdrop is droplimit=0 and mindrop 0, scan list once and return
if (drop_limit == 0 && min_drop == 0) {
for (const auto &e: le) {
LogLootDetail(
"-- NPC [{}] Lootdrop [{}] Item [{}] ({}_ Chance [{}] Multiplier [{}]",
GetCleanName(),
lootdrop_id,
database.GetItem(e.item_id)->Name,
e.item_id,
e.chance,
e.multiplier
);
for (int j = 0; j < e.multiplier; ++j) {
if (zone->random.Real(0.0, 100.0) <= e.chance && MeetsLootDropLevelRequirements(e, true)) {
const EQ::ItemData *database_item = database.GetItem(e.item_id);
AddLootDrop(database_item, e);
LogLootDetail(
"---- NPC (Rolled) [{}] Lootdrop [{}] Item [{}] ({}) Chance [{}] Multiplier [{}]",
GetCleanName(),
lootdrop_id,
database_item->Name,
e.item_id,
e.chance,
e.multiplier
);
}
}
}
@@ -254,6 +285,8 @@ void NPC::AddLootDrop(
const EQ::ItemData *item2,
LootdropEntriesRepository::LootdropEntries loot_drop,
bool wear_change,
bool quest,
bool pet,
uint32 augment_one,
uint32 augment_two,
uint32 augment_three,
@@ -266,6 +299,10 @@ void NPC::AddLootDrop(
return;
}
if (CountQuestItems() >= MAX_NPC_QUEST_INVENTORY) {
return;
}
auto item = new LootItem;
if (LogSys.log_settings[Logs::Loot].is_category_enabled == 1) {
@@ -287,6 +324,10 @@ void NPC::AddLootDrop(
);
}
if (quest || pet) {
LogLoot("Adding {} to npc: {}. Wearchange: {} Multiquest: {} Pet: {}", item2->Name, GetName(), wear_change, quest, pet);
}
EQApplicationPacket *outapp = nullptr;
WearChange_Struct *p_wear_change_struct = nullptr;
if (wear_change) {
@@ -308,6 +349,8 @@ void NPC::AddLootDrop(
item->trivial_min_level = loot_drop.trivial_min_level;
item->trivial_max_level = loot_drop.trivial_max_level;
item->equip_slot = EQ::invslot::SLOT_INVALID;
item->quest = quest;
item->pet = pet;
// unsure if required to equip, YOLO for now
if (item2->ItemType == EQ::item::ItemTypeBow) {
@@ -318,6 +361,11 @@ void NPC::AddLootDrop(
SetArrowEquipped(true);
}
if (pet && quest) {
LogLoot("Error: Item {} is being added to {} as both a pet and a quest.", item2->Name, GetName());
item->pet = 0;
}
bool found = false; // track if we found an empty slot we fit into
int found_slot = INVALID_INDEX; // for multi-slot items
@@ -510,20 +558,21 @@ void NPC::AddLootDrop(
safe_delete(inst);
}
void NPC::AddItem(const EQ::ItemData *item, uint16 charges, bool equip_item)
void NPC::AddItem(const EQ::ItemData *item, uint16 charges, bool equip_item, bool quest)
{
auto l = LootdropEntriesRepository::NewNpcEntity();
l.equip_item = static_cast<uint8>(equip_item ? 1 : 0);
l.item_charges = charges;
AddLootDrop(item, l, true);
AddLootDrop(item, l, equip_item, quest);
}
void NPC::AddItem(
uint32 item_id,
uint16 charges,
bool equip_item,
bool quest,
uint32 augment_one,
uint32 augment_two,
uint32 augment_three,
@@ -545,7 +594,8 @@ void NPC::AddItem(
AddLootDrop(
item,
l,
true,
equip_item,
quest,
augment_one,
augment_two,
augment_three,
@@ -664,6 +714,50 @@ LootItem *NPC::GetItem(int slot_id)
return (nullptr);
}
LootItem *NPC::GetItemByItemID(int16 item_id)
{
LootItems::iterator cur, end;
cur = m_loot_items.begin();
end = m_loot_items.end();
for (; cur != end; ++cur) {
LootItem *item = *cur;
if (item->item_id == item_id) {
return item;
}
}
return(nullptr);
}
void NPC::RemoveItem(LootItem *item_data, uint8 quantity) {
if (!item_data) {
return;
}
LootItems::iterator cur, end;
cur = m_loot_items.begin();
end = m_loot_items.end();
for (; cur != end; ++cur) {
LootItem *item = *cur;
if (item != item_data) { continue; }
if (!item) {
return;
}
if (item->charges <= quantity) {
m_loot_items.erase(cur);
UpdateEquipmentLight();
if (UpdateActiveLight()) { SendAppearancePacket(AppearanceType::Light, GetActiveLightType()); }
}
else {
item->charges -= quantity;
}
return;
}
}
void NPC::RemoveItem(uint32 item_id, uint16 quantity, uint16 slot)
{
LootItems::iterator cur, end;
@@ -910,3 +1004,315 @@ void NPC::RemoveLootCash()
m_loot_gold = 0;
m_loot_platinum = 0;
}
bool NPC::HasQuestLootItem(int16 itemid)
{
LootItems::iterator cur, end;
cur = m_loot_items.begin();
end = m_loot_items.end();
for (; cur != end; ++cur) {
LootItem *quest_item = *cur;
if (quest_item && quest_item->quest == 1 && quest_item->item_id == itemid) {
return true;
}
}
return false;
}
bool NPC::HasQuestLoot()
{
LootItems::iterator cur, end;
cur = m_loot_items.begin();
end = m_loot_items.end();
for (; cur != end; ++cur) {
LootItem *quest_loot = *cur;
if (quest_loot && quest_loot->quest == 1) {
return true;
}
}
return false;
}
bool NPC::RemoveQuestLootItems(int16 itemid)
{
LootItems::iterator cur, end;
cur = m_loot_items.begin();
end = m_loot_items.end();
for (; cur != end; ++cur) {
LootItem *quest_item = *cur;
if (quest_item && quest_item->quest == 1) {
if (itemid == 0 || itemid == quest_item->item_id) {
RemoveItem(quest_item);
return true;
}
}
}
return false;
}
bool NPC::HasRequiredQuestLoot(int16 itemid1, int16 itemid2, int16 itemid3, int16 itemid4)
{
if (itemid2 == 0 && itemid3 == 0 && itemid4 == 0) {
return true;
}
uint8 item2count = 0, item3count = 0, item4count = 0, item1npc = 0, item2npc = 0, item3npc = 0, item4npc = 0;
uint8 item1count = 1;
if (itemid2 > 0) {
item2count = 1;
}
if (itemid3 > 0) {
item3count = 1;
}
if (itemid4 > 0) {
item4count = 1;
}
if (itemid1 == itemid2 && itemid2 > 0) {
item2count = item1count;
++item1count;
++item2count;
}
if (itemid1 == itemid3 && itemid3 > 0) {
item3count = item1count;
++item1count;
++item3count;
}
if (itemid1 == itemid4 && itemid4 > 0) {
item4count = item1count;
++item1count;
++item4count;
}
if (itemid2 == itemid3 && itemid2 > 0 && itemid3 > 0) {
item3count = item2count;
++item2count;
++item3count;
}
if (itemid2 == itemid4 && itemid2 > 0 && itemid4 > 0) {
item4count = item2count;
++item2count;
++item4count;
}
if (itemid3 == itemid4 && itemid3 > 0 && itemid4 > 0) {
item4count = item3count;
++item3count;
++item4count;
}
LootItems::iterator cur, end;
cur = m_loot_items.begin();
end = m_loot_items.end();
for (; cur != end; ++cur) {
LootItem *sitem = *cur;
if (sitem && sitem->quest == 1) {
if (sitem->item_id == itemid1) {
++item1npc;
}
if (sitem->item_id == itemid2 && itemid2 > 0) {
++item2npc;
}
if (sitem->item_id == itemid3 && itemid3 > 0) {
++item3npc;
}
if (sitem->item_id == itemid4 && itemid4 > 0) {
++item4npc;
}
}
}
if (item1npc < item1count) {
return false;
}
if (itemid2 > 0 && item2npc < item2count) {
return false;
}
if (itemid3 > 0 && item3npc < item3count) {
return false;
}
if (itemid4 > 0 && item4npc < item4count) {
return false;
}
return true;
}
void NPC::CleanQuestLootItems()
{
//Removes nodrop or multiple quest loot items from a NPC before sending the corpse items to the client.
LootItems::iterator cur, end;
cur = m_loot_items.begin();
end = m_loot_items.end();
uint8 count = 0;
for (; cur != end; ++cur) {
LootItem *quest_item = *cur;
if (quest_item && (quest_item->quest == 1 || quest_item->pet == 1)) {
uint8 count = CountQuestItem(quest_item->item_id);
if (count > 1 && quest_item->pet != 1) {
RemoveItem(quest_item);
return;
}
else {
const EQ::ItemData *item = database.GetItem(quest_item->item_id);
if (item && item->NoDrop == 0) {
RemoveItem(quest_item);
return;
}
}
}
}
}
uint8 NPC::CountQuestItem(uint16 itemid)
{
LootItems::iterator cur, end;
cur = m_loot_items.begin();
end = m_loot_items.end();
uint8 count = 0;
for (; cur != end; ++cur) {
LootItem *quest_item = *cur;
if (quest_item && quest_item->item_id == itemid) {
++count;
}
}
return count;
}
uint8 NPC::CountQuestItems()
{
LootItems::iterator cur, end;
cur = m_loot_items.begin();
end = m_loot_items.end();
uint8 count = 0;
for (; cur != end; ++cur) {
LootItem *quest_item = *cur;
if (quest_item && quest_item->quest == 1) {
++count;
}
}
return count;
}
bool NPC::AddQuestLoot(int16 itemid, int8 charges) {
auto l = LootdropEntriesRepository::NewNpcEntity();
const EQ::ItemData *item = database.GetItem(itemid);
if (item) {
l.item_charges = charges;
l.equip_item = 0;
AddLootDrop(item, l, false, false, true);
LogLoot("Adding item {} to the NPC's loot marked as quest.", itemid);
if (itemid > 0 && HasPetLootItem(itemid)) {
LogLoot("Deleting quest item {} from NPC's pet loot.", itemid);
RemovePetLootItems(itemid);
}
}
else
return false;
return true;
}
void NPC::DeleteQuestLoot(int16 itemid1, int16 itemid2, int16 itemid3, int16 itemid4)
{
int16 items = m_loot_items.size();
for (int i = 0; i < items; ++i) {
if (itemid1 == 0) {
if (!RemoveQuestLootItems(itemid1))
break;
}
else {
if (itemid1 != 0) {
RemoveQuestLootItems(itemid1);
}
if (itemid2 != 0) {
RemoveQuestLootItems(itemid2);
}
if (itemid3 != 0) {
RemoveQuestLootItems(itemid3);
}
if (itemid4 != 0) {
RemoveQuestLootItems(itemid4);
}
}
}
}
void NPC::DeleteInvalidQuestLoot()
{
int16 items = m_loot_items.size();
for (int i = 0; i < items; ++i) {
CleanQuestLootItems();
}
}
bool NPC::AddPetLoot(int16 itemid, int8 charges, bool fromquest) {
auto l = LootdropEntriesRepository::NewNpcEntity();
const EQ::ItemData *item = database.GetItem(itemid);
bool IsCharmedPet = IsPet() && IsCharmed();
if (!item) {
return false;
}
bool valid = (item->NoDrop != 0 && ( !IsCharmedPet || (IsCharmedPet && CountQuestItem(item->ID) == 0)));
if (!fromquest || valid) {
if (item) {
l.item_charges = charges;
AddLootDrop(item, l, true, true, false, true);
LogLoot("Adding item {} to the NPC's loot marked as pet.", itemid);
return true;
}
}
else {
LogLoot("Item {} is a duplicate or no drop. Deleting...", itemid);
return false;
}
return false;
}
bool NPC::HasPetLootItem(int16 itemid)
{
LootItems::iterator cur, end;
cur = m_loot_items.begin();
end = m_loot_items.end();
for (; cur != end; ++cur) {
LootItem *quest_item = *cur;
if (quest_item && quest_item->pet == 1 && quest_item->item_id == itemid) {
return true;
}
}
return false;
}
bool NPC::RemovePetLootItems(int16 itemid)
{
LootItems::iterator cur, end;
cur = m_loot_items.begin();
end = m_loot_items.end();
for (; cur != end; ++cur) {
LootItem *quest_item = *cur;
if (quest_item && quest_item->pet == 1) {
if (itemid == 0 || itemid == quest_item->item_id) {
RemoveItem(quest_item);
return true;
}
}
}
return false;
}
+6
View File
@@ -659,6 +659,11 @@ void Lua_Bot::SetBucket(std::string bucket_name, std::string bucket_value, std::
self->SetBucket(bucket_name, bucket_value, expiration);
}
void Lua_Bot::DeleteBot() {
Lua_Safe_Call_Void();
self->DeleteBot();
}
luabind::scope lua_register_bot() {
return luabind::class_<Lua_Bot, Lua_Mob>("Bot")
.def(luabind::constructor<>())
@@ -698,6 +703,7 @@ luabind::scope lua_register_bot() {
.def("ClearSpellRecastTimer", (void(Lua_Bot::*)(uint16))&Lua_Bot::ClearSpellRecastTimer)
.def("CountBotItem", (uint32(Lua_Bot::*)(uint32))&Lua_Bot::CountBotItem)
.def("CountItemEquippedByID", (int(Lua_Bot::*)(uint32))&Lua_Bot::CountItemEquippedByID)
.def("DeleteBot", (void(Lua_Bot::*)(void))&Lua_Bot::DeleteBot)
.def("DeleteBucket", (void(Lua_Bot::*)(std::string))&Lua_Bot::DeleteBucket)
.def("Escape", (void(Lua_Bot::*)(void))&Lua_Bot::Escape)
.def("Fling", (void(Lua_Bot::*)(float,float,float))&Lua_Bot::Fling)
+1
View File
@@ -60,6 +60,7 @@ public:
uint32 GetBotID();
void Camp();
void Camp(bool save_to_database);
void DeleteBot();
Lua_ItemInst GetAugmentAt(int16 slot_id, uint8 augment_index);
int GetAugmentIDAt(int16 slot_id, uint8 augment_index);
luabind::object GetAugmentIDsBySlotID(lua_State* L, int16 slot_id) const;
+154
View File
@@ -0,0 +1,154 @@
#ifdef LUA_EQEMU
#include "lua.hpp"
#include <luabind/luabind.hpp>
#include <luabind/iterator_policy.hpp>
#include "lua_buff.h"
uint16 Lua_Buff::GetCasterID()
{
Lua_Safe_Call_Int();
return self->casterid;
}
uint8 Lua_Buff::GetCasterLevel()
{
Lua_Safe_Call_Int();
return self->casterlevel;
}
std::string Lua_Buff::GetCasterName()
{
Lua_Safe_Call_String();
return self->caster_name;
}
int Lua_Buff::GetCastOnX()
{
Lua_Safe_Call_Int();
return self->caston_x;
}
int Lua_Buff::GetCastOnY()
{
Lua_Safe_Call_Int();
return self->caston_y;
}
int Lua_Buff::GetCastOnZ()
{
Lua_Safe_Call_Int();
return self->caston_z;
}
uint32 Lua_Buff::GetCounters()
{
Lua_Safe_Call_Int();
return self->counters;
}
uint32 Lua_Buff::GetDOTRune()
{
Lua_Safe_Call_Int();
return self->dot_rune;
}
int Lua_Buff::GetExtraDIChance()
{
Lua_Safe_Call_Int();
return self->ExtraDIChance;
}
uint32 Lua_Buff::GetInstrumentModifier()
{
Lua_Safe_Call_Int();
return self->instrument_mod;
}
uint32 Lua_Buff::GetMagicRune()
{
Lua_Safe_Call_Int();
return self->magic_rune;
}
uint32 Lua_Buff::GetMeleeRune()
{
Lua_Safe_Call_Int();
return self->melee_rune;
}
uint32 Lua_Buff::GetNumberOfHits()
{
Lua_Safe_Call_Int();
return self->hit_number;
}
int16 Lua_Buff::GetRootBreakChance()
{
Lua_Safe_Call_Int();
return self->RootBreakChance;
}
uint16 Lua_Buff::GetSpellID()
{
Lua_Safe_Call_Int();
return self->spellid;
}
int Lua_Buff::GetTicsRemaining()
{
Lua_Safe_Call_Int();
return self->ticsremaining;
}
int Lua_Buff::GetVirusSpreadTime()
{
Lua_Safe_Call_Int();
return self->virus_spread_time;
}
bool Lua_Buff::IsCasterClient()
{
Lua_Safe_Call_Bool();
return self->client;
}
bool Lua_Buff::IsPersistentBuff()
{
Lua_Safe_Call_Bool();
return self->persistant_buff;
}
bool Lua_Buff::SendsClientUpdate()
{
Lua_Safe_Call_Bool();
return self->UpdateClient;
}
luabind::scope lua_register_buff() {
return luabind::class_<Lua_Buff>("Buff")
.def(luabind::constructor<>())
.def("GetCasterID", &Lua_Buff::GetCasterID)
.def("GetCasterLevel", &Lua_Buff::GetCasterLevel)
.def("GetCasterName", &Lua_Buff::GetCasterName)
.def("GetCastOnX", &Lua_Buff::GetCastOnX)
.def("GetCastOnY", &Lua_Buff::GetCastOnY)
.def("GetCastOnZ", &Lua_Buff::GetCastOnZ)
.def("GetCounters", &Lua_Buff::GetCounters)
.def("GetDOTRune", &Lua_Buff::GetDOTRune)
.def("GetExtraDIChance", &Lua_Buff::GetExtraDIChance)
.def("GetInstrumentModifier", &Lua_Buff::GetInstrumentModifier)
.def("GetMagicRune", &Lua_Buff::GetMagicRune)
.def("GetMeleeRune", &Lua_Buff::GetMeleeRune)
.def("GetNumberOfHits", &Lua_Buff::GetNumberOfHits)
.def("GetRootBreakChance", &Lua_Buff::GetRootBreakChance)
.def("GetSpellID", &Lua_Buff::GetSpellID)
.def("GetTicsRemaining", &Lua_Buff::GetTicsRemaining)
.def("GetVirusSpreadTime", &Lua_Buff::GetVirusSpreadTime)
.def("IsCasterClient", &Lua_Buff::IsCasterClient)
.def("IsPersistentBuff", &Lua_Buff::IsPersistentBuff)
.def("SendsClientUpdate", &Lua_Buff::SendsClientUpdate);
}
#endif

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