Compare commits

...

74 Commits

Author SHA1 Message Date
KimLS b894d7ee3a update base character data repo 2024-10-12 12:52:07 -07:00
KimLS eecfce6514 update allocations table base repo 2024-10-12 12:41:38 -07:00
KimLS 2d6950149a Fix for server side everything showing as MR 2024-10-12 12:12:57 -07:00
KimLS 3945a8c0c0 Add titanium support 2024-10-12 12:02:40 -07:00
KimLS f27209a812 Add sof support 2024-10-12 11:27:37 -07:00
KimLS d4982743bf Add sod support 2024-10-12 11:23:59 -07:00
KimLS e2dd8f5f60 Add support to rof 2024-10-12 11:13:24 -07:00
KimLS bfc0cceecc Basics work for rof2 2024-10-12 11:06:36 -07:00
KimLS c9902881b7 Generate repo; create will save resists to db 2024-10-12 10:47:03 -07:00
KimLS 62bb426847 working on loading resists from character create. 2024-10-11 23:33:23 -07:00
Alex King c873fe5a22 [Bug Fix] Fix Mercenary Encounter Crash (#4509) 2024-10-11 23:00:09 -04:00
Fryguy e06b0c4b0c [Bug Fix] Master of Disguise should apply to illusions casted by others. (#4506)
Many era comments outline how Master of Disguise would apply to Project Illusion spells on you:

https://thesafehouse.org/forums/forum/everquest-wing/main-lounge/14249-new-aa-master-of-disguise/page4

https://thesafehouse.org/forums/forum/everquest-wing/training-studios/18143-master-of-disguise-broken

```
Im not a big fan of wolf form, but having a 1200 min NDT is pretty nice  I also agree its great to shrink on a raid once and not have to worry about it. 7 aa is a little steep imho, but with a name change and some frog potions, I may reapply to my guild as the servers only froggy rogue /cackle.
```

```
share form of the great wolf gave a 1500min timer.
```
2024-10-11 13:39:36 -04:00
catapultam-habeo ed2130f649 [Bug Fix] Correctly limit max targets of PBAOE (#4507)
* fix pbaoe max targets incorrectly set

* fix scratch copy
2024-10-11 13:15:19 -04:00
Alex King 448a33a60c [Quest API] Add Scripting Support to Mercenaries (#4500)
* [Quest API] Add Scripting Support to Mercenaries

* Cleanup

* Cleanup

* Update lua_merc.h

* Update mob.cpp

* XYZH

* Final

* Update attack.cpp

* Update attack.cpp

* Simplify event invocation

* Inline example

* Nullptr init example

* EVENT_TIMER simplify add EventPlayerNpcBotMerc

* EVENT_TIMER_START

* Remove has_start_event

* EVENT_TIMER_START with settimerMS

* EVENT_POPUP_RESPONSE

* Consolidation

* Update attack.cpp

* Push

* Update quest_parser_collection.h

* Comments

* Cleanup per comments

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2024-10-10 21:29:29 -04:00
Fryguy 8f86cb353e [Bug Fix] Spells - Self Only (Yellow) cast when non group member is targeted (#4503)
* [Bug Fix] Spells - Self Only (Yellow) cast when non group member is targeted

When using a Yellow gem invis spell, it should cast on yourself regardless of the targetted entity.

* Update spells.cpp

---------

Co-authored-by: Alex King <89047260+Kinglykrab@users.noreply.github.com>
2024-10-10 21:27:49 -04:00
Alex 178129443f [Loginserver] Login Fatal Error Spamming (#4476)
Co-authored-by: KimLS <KimLS@peqtgc.com>
2024-10-09 02:15:49 -05:00
Alex King a7c3b41afc [Quest API] Add Buff Fade Methods to Perl/Lua (#4501)
* [Quest API] Add Buff Fade Methods to Perl/Lua

* BuffFadeSongs()
2024-10-09 02:12:33 -05:00
Alex King a5a568d548 [Bug Fix] Fix character_exp_modifiers Default Values (#4502) 2024-10-09 02:11:57 -05:00
Alex King e3198edb86 [Quest API] Add EVENT_READ_ITEM to Perl/Lua (#4497)
* [Quest API] Add EVENT_READ_ITEM to Perl/Lua

* Add item_id export

* Add item export.

* Update client.cpp
2024-10-08 18:25:14 -04:00
Alex King 8568cf7d49 [Bug Fix] Fix NPC::CanTalk() Crash (#4499)
* [Bug FIx] Fix NPC::CanTalk() Crash

* Update npc.cpp

* Update mob.cpp

* Update npc.cpp
2024-10-07 00:17:49 -05:00
Alex King 1fb7a860a1 [Bug Fix] Fix #set motd Crash (#4495) 2024-10-05 07:58:22 -05:00
nytmyr 7eaee2649e [Bots] Add "silent" option to ^spawn and mute raid spawn (#4494)
When zoning or forming a raid, bots would spam their spawn message. They will now be muted.

Adds an optional argument "silent" to the ^spawn command. This will bypass ^oo spawnmessage settings and not send a spawn message. Example: ^spawn Warbot silent
2024-10-04 20:20:52 -04:00
Alex King a17f467b98 [Quest API] Add NPC List Filter Methods to Perl/Lua (#4493)
* [Quest API] Add GetNPCsByNPCIDs to Perl/Lua

* Push

* Update entity.cpp

* Separate methods.
2024-10-03 20:28:57 -04:00
Alex King 3359839a9b [Bug Fix] Fix Targeted AOE Max Targets Rule (#4488) 2024-10-02 20:25:35 -05:00
Alex 7e51e629f9 [Loginserver] Larion loginserver support (#4492)
* Add larion version and opcode path

* WIP: getting server to work

* Identify server_id

* Add missing opcode, add opcodes file

---------

Co-authored-by: KimLS <KimLS@peqtgc.com>
2024-10-02 20:20:13 -05:00
Alex King dc6c28a52d [Cleanup] Remove Extra Skill in EQ::skills::GetExtraDamageSkills() (#4486) 2024-10-02 20:07:19 -05:00
Alex King 78aee0780a [Bug Fix] Fix Group ID 0 in Group::SaveGroupLeaderAA() (#4487) 2024-10-02 20:06:56 -05:00
Chris Miles bcd943a964 [Code Cleanup] Optimization Code Cleanup (#4489)
* Initial push

* More

* More

* Further simplify

* More cleanup

* More consolidation

* Fix

* Update

* Update npc.cpp

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2024-09-30 18:34:42 -04:00
nytmyr 56608e84bd [Bots] Add attack flag when told to attack (#4490)
This adds a flag to mobs that are told to attack by their owner to prevent unintended attacks.

Previously, if you were to send your bots to attack a target and then switch targets: before casters land their spell or if melee (especially anyone with pets)  hasn't engaged before the target switch, they could switch to your new target and attack.

This adds a flag upon attack and bots will only attack flagged targets.
2024-09-29 17:59:26 -04:00
regneq 8d23e710ce [Bug Fix] fixed a bug where it would use npc value instead of faction value in the database. (#4491) 2024-09-29 17:42:43 -04:00
Morzain 4d11077b21 [Bug Fix] Add character_instance_safereturns to tables_to_zero_id (#4485)
Co-authored-by: morzain <morzain@users.noreply.github.com>
2024-09-26 18:14:32 -04:00
Alex King 5c0bdfdc4c [Bug Fix] Fix issue with Client::SaveDisciplines() not specifying character ID (#4481) 2024-09-23 23:00:52 -05:00
Alex King 6130e10831 [Release] 22.56.2 (#4480) 2024-09-19 21:59:53 -05:00
Alex King c3e1c531d2 [Bug Fix] Fix Issue with Database::ReserveName (#4477) 2024-09-19 21:15:14 -05:00
Alex King b52719a535 [Quest API] Add GrantAllAAPoints() Overload To Perl/Lua (#4474) 2024-09-19 21:09:24 -05:00
Alex King 1af252466f [Bug Fix] Fix Untrained Disciplines in Client::SaveDisciplines() (#4472)
* [Bug Fix] Fix Untrained Disciplines in Client::SaveDisciplines()

* [Bug Fix] Fix Infinite Loop in Adventure::Finished() (#4473)

Fix infinite loop condition when bot encountered

* [Bug Fix] Fix Untrained Disciplines in Client::SaveDisciplines()

* Change to release

---------

Co-authored-by: oddx2k <103136558+oddx2k@users.noreply.github.com>
2024-09-19 21:09:09 -05:00
catapultam-habeo 699d22fc28 [Bug Fix] Fix 'Teleport Doors' from being blocked by GM flag (#4475)
* gm flag blocks teleport doors with keys from working instead of allowing them to work

* correct coniditional logic
2024-09-18 17:18:07 -04:00
Mitch Freeman 5d1fe68906 [Bug Fix] Parcel purchase of bazaar items with unlimited charges (#4479)
Fix for unlimited charges in bazaar
2024-09-18 09:36:00 -04:00
oddx2k 52dcf35425 [Bug Fix] Fix Infinite Loop in Adventure::Finished() (#4473)
Fix infinite loop condition when bot encountered
2024-09-13 13:20:55 -04:00
Alex King a7550fbd9e [Release] 22.56.0 (#4471)
* [Release] 22.55.2

### Code

* Add IsCloseToBanker method ([#4462](https://github.com/EQEmu/Server/pull/4462)) @Akkadius 2024-08-27

### Feature

* Add Rule to Limit Task Update Messages ([#4459](https://github.com/EQEmu/Server/pull/4459)) @Kinglykrab 2024-08-28
* Allow NPCs to cast Sacrifice ([#4470](https://github.com/EQEmu/Server/pull/4470)) @fuzzlecutter 2024-09-12
* Lazy Load Bank Contents ([#4453](https://github.com/EQEmu/Server/pull/4453)) @catapultam-habeo 2024-08-27

### Fixes

* Add RULE_STRING to RuleManager::ResetRules ([#4467](https://github.com/EQEmu/Server/pull/4467)) @Kinglykrab 2024-09-07
* Fix Bard Effect in Migration 9237 ([#4468](https://github.com/EQEmu/Server/pull/4468)) @Kinglykrab 2024-09-09
* ModernAAScalingEnabled() Calculation Error ([#4469](https://github.com/EQEmu/Server/pull/4469)) @carolus21rex 2024-09-11

### Performance

* Move Discipline Loading to Client::CompleteConnect() ([#4466](https://github.com/EQEmu/Server/pull/4466)) @Kinglykrab 2024-09-09

### Rules

* Add a Bandolier Swap Delay Rule ([#4465](https://github.com/EQEmu/Server/pull/4465)) @Kinglykrab 2024-09-08

* 22.56.0
2024-09-12 20:39:48 -05:00
fuzzlecutter cc0171dfe1 [Feature] Allow NPCs to cast Sacrifice (#4470)
* [Feature] Teach npcs how to cast sacrifice

* [Feature] Teach npcs how to cast sacrifice

- Remove the hardcoded limit preventing npcs from casting sacrifice. The
  npc will receive as loot an emerald essence as expected.

* Update client.cpp
* Update client_packet.cpp
* Update spell_effects.cpp

* rename Client::SacrificeCaster to Client::sacrifice_caster_id
2024-09-12 15:42:44 -04:00
carolus21rex 913c5da70f [Bug Fix] ModernAAScalingEnabled() Calculation Error (#4469)
Current version only looks at your unspent AAs, meaning if you have 2000 spent AAs and 1 unspent AA, your scaling will be based on the 1 unspent AA instead of the 2001 total AA.

Here's the original log which is custom code found in the ModernAAScalingEnabled function:

[Wed Sep 11 14:10:19 2024] [AA] [ScaleAAXPBasedOnCurrentAATotal] AA Experience Calculation: add_aaxp = 660796, Base Bonus = 256.000000, Half-Life = 64.000000, Minimum Bonus = 1.000000, Earned AA = 1, Calculated Bonus = 253.242371

Custom code looks like this:

uint64 totalWithExpMod = add_aaxp;
	if (RuleB(AA, EnableLogrithmicClasslessAABonus)) {
		float base_bonus = RuleR(AA, InitialLogrithmicClasslessAABonus);
		float half_life = RuleR(AA, HalfLifeLogrithmicClasslessAABonus);
		float min_bon = RuleR(AA, MinimumLogrithmicClasslessAABonus);
		float bonus_expon = earnedAA / half_life;

		float bonus = base_bonus * std::pow(0.5, bonus_expon);
		Log(Logs::General,
			Logs::AA,
			"AA Experience Calculation: add_aaxp = %d, Base Bonus = %f, Half-Life = %f, Minimum Bonus = %f, Earned AA = %d, Calculated Bonus = %f",
			add_aaxp, base_bonus, half_life, min_bon, earnedAA, bonus);

		if (bonus < min_bon) bonus = min_bon;

		totalWithExpMod = (uint64)(totalWithExpMod * bonus);
	}

After the fix, the log becomes:

[Wed Sep 11 14:10:19 2024] [AA] [ScaleAAXPBasedOnCurrentAATotal] AA Experience Calculation: add_aaxp = 660796, Base Bonus = 256.000000, Half-Life = 64.000000, Minimum Bonus = 1.000000, Earned AA = 1, Calculated Bonus = 253.242371

Which is much closer to the expected behavior
2024-09-11 17:06:48 -04:00
Alex King 40fecbfaf5 [Performance] Move Discipline Loading to Client::CompleteConnect() (#4466)
* [Performance] Move Character Discipline Loading

* Push

* Final
2024-09-09 18:20:12 -05:00
Alex King b1646381b0 [Bug Fix] Fix Bard Effect in Migration 9237 (#4468) 2024-09-09 18:02:58 -05:00
Alex King bb1578796b [Rule] Add a Bandolier Swap Delay Rule (#4465)
* [Rule] Add a Bandolier Swap Delay Rule

* Push

* Update exp.cpp
2024-09-07 22:05:44 -05:00
Alex King 0e5a38f072 [Bug Fix] Add RULE_STRING to RuleManager::ResetRules (#4467)
* [Bug Fix] Add RULE_STRING to RuleManager::ResetRules

* Update rulesys.cpp
2024-09-07 18:29:46 -05:00
Alex King 39876ab858 [Feature] Add Rule to Limit Task Update Messages (#4459)
* [Feature] Add Rule to Limit Task Update Messages

* Update task_client_state.cpp

* Update task_client_state.cpp

* Change rule
2024-08-27 21:49:07 -04:00
catapultam-habeo ff16a76481 [Feature] Lazy Load Bank Contents (#4453)
* initial work porting this to upstream

* more

* track complete connect

* it sucks to suck

* Few optimizations

* Move sent_inventory init

* Move var

* Adjustments

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2024-08-27 13:21:55 -05:00
Akkadius ffd68eb63d [Release] 22.55.1 2024-08-27 08:17:20 -05:00
Akkadius 76c1da1aad [Release] 22.55.1 2024-08-27 08:09:13 -05:00
Chris Miles a91e03fa43 [Code] Add IsCloseToBanker method (#4462)
* [Code] Add IsCloseToBanker method

* Update mob.cpp
2024-08-26 22:59:06 -05:00
Chris Miles 453106439f [Release] 22.55.0 (#4464) 2024-08-26 22:03:37 -05:00
Mitch Freeman 3da24fffa4 [Bug Fix] Fix client hotbar exchanging items when zoning (#4460)
* Add an exception process to assigning item serial numbers to correct a bug in the client hot bar clicky system.

* fixed missing guid in replace statement

* added snapshot support

* upate #show inventory command to protect against crash conditions
2024-08-26 21:58:07 -05:00
Kurt Gilpin 8d8ef6d480 [Bug Fix] Correct missed maxlevel reference in exp.cpp (#4463)
This was causing characters to de-level when gaining experience and was missed as part of https://github.com/EQEmu/Server/pull/4455
2024-08-26 22:34:22 -04:00
Chris Miles 1f9c4b3a22 [World] Fix slow world bootup bug (#4461)
* [World] Fix slow world bootup bug

* Update ip_util.cpp

* Add timeout

* Update ip_util.cpp

* Cross platform timeout
2024-08-26 20:59:43 -05:00
Alex King 7dfda95d86 [Bug Fix] Fix Bot Spell Entries IDs Capping at 32,767 (#4444)
* [Bug Fix] Fix Bot Spell Entries IDs Capping at 32,767

* Fix manifest
2024-08-26 20:29:50 -05:00
Alex King 40738b29e3 [Quest API] Add Area-Based Quest Methods to Perl/Lua (#4447)
* [Quest API] Add Area-Based Quest Methods to Perl/Lua

* Convert some to mob

* Fix
2024-08-26 20:29:07 -05:00
Alex King 080865faa2 [Feature] Add Optional Return to EVENT_DAMAGE_TAKEN (#4454)
* [Feature] Add Optional Return to EVENT_DAMAGE_TAKEN

# Description
- Allows operators to return a value from `EVENT_DAMAGE_TAKEN` to override the amount of damage taken based on any arbitrary criteria they'd like to apply.

* Update attack.cpp
2024-08-26 20:27:29 -05:00
Alex King e2b545991a [Quest API] Add AreTasksCompleted() to Perl/Lua. (#4456)
* [Quest API] Add AreTasksCompleted() to Perl/Lua.

* Bool
2024-08-22 20:21:14 -04:00
Alex King b7f8d0f179 [Feature] Extend Spell Buckets Functionality (#4441) 2024-08-22 18:49:52 -04:00
Alex King e3588781aa [Cleanup] Remove unused methods (#4449) 2024-08-22 11:48:02 -04:00
Alex King e9b84f4d11 [Bug Fix] Fix issue with killed mob coordinates (#4457) 2024-08-22 11:45:31 -04:00
Alex King 4f03970fd1 [Bug Fix] Fix Character ID of 0 being inserted into character_stats_record (#4458) 2024-08-22 11:45:19 -04:00
catapultam-habeo 4979da6932 [Bug Fix] Apply Race & Class restrictions to Auto-Combines (#4452) 2024-08-19 21:57:34 -04:00
Fryguy 9987029791 [Bug Fix] client_max_level allow leveling to end of level (#4455)
When using a method that leverages client_max_level (e.g. Max Level by bucket / qglobal / quest API) it would stop xp at 0% into the level rather than maximum xp for the level.

This could pose an issue where: If you had a max level of 65 via a databucket and a raid zone required level 65, one death would de-level them and potentially prevent them from entering the zone.

I reorganized the code to leverage the existing max_level logic which allows max xp in the max level.

I also cleaned up the overall functions formating (Mostly brackets and implied if statements).
2024-08-19 21:56:14 -04:00
Alex King eece0a92e3 [Quest API] Add Several Door Methods to Perl/Lua (#4451) 2024-08-16 15:52:49 -04:00
Alex King 057f96796a [Bug Fix] Fix Issue with Removed #setfaction Command (#4448) 2024-08-10 21:21:46 -04:00
Alex King f475cecdb1 [Bug Fix] Fix AddCrystals() in Perl/Lua (#4445) 2024-08-09 22:48:32 -04:00
Fryguy 6296ed6d41 [Bug Fix] Attune Augments when Equipped (#4446) 2024-08-09 22:27:04 -04:00
Alex King ac0f729aa2 [Feature] Add Character:DefaultGuildRank Rule (#4438)
* [Feature] Add Character:DefaultGuildRank Rule

* Update ruletypes.h

* Update ruletypes.h

* Update database.cpp
2024-08-03 22:48:30 -04:00
JJ 2937852cf9 [Bug Fix] Ensure close of Tribute Item search (#4439) 2024-08-03 20:25:07 -04:00
Alex King 2cf5bae571 [Bug Fix] Fix Lua Client FilteredMessage (#4437) 2024-07-31 19:39:57 -04:00
Fryguy 2feb05be18 [Improvement] Filtered Messages Extension (#4435)
* [Improvment] Filtered Messages Extension

Added:
ItemSpeech 25
Strikethrough 26
Stuns 27
BardSongsOnPets 28

I wired up Strikethrough and Stuns as they already had message entries.

ItemSpeech and BardSongsOnPets do not appear to be currently used in the source.

Note: There are still 5 unknown Filters in RoF2 that need to be investigated:

Achievments
Fellowships
Mercenary Messages
PVP Messages
Spam

* Spelling Error

* Missed some stun calls
2024-07-31 18:28:45 -04:00
Fryguy 421767e1e5 [Bug Fix] Imitate Death should also clear zone feign aggro (#4436) 2024-07-31 18:28:36 -04:00
122 changed files with 5444 additions and 2687 deletions
+91
View File
@@ -1,3 +1,94 @@
## [22.56.3] 9/23/2024
### Fixes
* Fix issue with Client::SaveDisciplines() not specifying character ID ([#4481](https://github.com/EQEmu/Server/pull/4477)) @Kinglykrab 2024-09-23
## [22.56.2] 9/20/2024
### Fixes
* Fix Issue with Database::ReserveName ([#4477](https://github.com/EQEmu/Server/pull/4477)) @Kinglykrab 2024-09-20
### Quest API
* Add GrantAllAAPoints() Overload To Perl/Lua ([#4474](https://github.com/EQEmu/Server/pull/4474)) @Kinglykrab 2024-09-20
## [22.56.1] 9/20/2024
### Fixes
* Fix Untrained Disciplines in Client::SaveDisciplines() ([#4472](https://github.com/EQEmu/Server/pull/4472)) @Kinglykrab 2024-09-13
* Fix Infinite Loop in Adventure::Finished() ([#4473](https://github.com/EQEmu/Server/pull/4473)) @oddx2k 2024-09-13
## [22.56.0] 9/12/2024
### Code
* Add IsCloseToBanker method ([#4462](https://github.com/EQEmu/Server/pull/4462)) @Akkadius 2024-08-27
### Feature
* Add Rule to Limit Task Update Messages ([#4459](https://github.com/EQEmu/Server/pull/4459)) @Kinglykrab 2024-08-28
* Allow NPCs to cast Sacrifice ([#4470](https://github.com/EQEmu/Server/pull/4470)) @fuzzlecutter 2024-09-12
* Lazy Load Bank Contents ([#4453](https://github.com/EQEmu/Server/pull/4453)) @catapultam-habeo 2024-08-27
### Fixes
* Add RULE_STRING to RuleManager::ResetRules ([#4467](https://github.com/EQEmu/Server/pull/4467)) @Kinglykrab 2024-09-07
* Fix Bard Effect in Migration 9237 ([#4468](https://github.com/EQEmu/Server/pull/4468)) @Kinglykrab 2024-09-09
* ModernAAScalingEnabled() Calculation Error ([#4469](https://github.com/EQEmu/Server/pull/4469)) @carolus21rex 2024-09-11
### Performance
* Move Discipline Loading to Client::CompleteConnect() ([#4466](https://github.com/EQEmu/Server/pull/4466)) @Kinglykrab 2024-09-09
### Rules
* Add a Bandolier Swap Delay Rule ([#4465](https://github.com/EQEmu/Server/pull/4465)) @Kinglykrab 2024-09-08
## [22.55.1] 8/26/2024
### Code
* Remove unused methods ([#4449](https://github.com/EQEmu/Server/pull/4449)) @Kinglykrab 2024-08-22
### Feature
* Add Character:DefaultGuildRank Rule ([#4438](https://github.com/EQEmu/Server/pull/4438)) @Kinglykrab 2024-08-04
* Add Optional Return to EVENT_DAMAGE_TAKEN ([#4454](https://github.com/EQEmu/Server/pull/4454)) @Kinglykrab 2024-08-27
* Extend Spell Buckets Functionality ([#4441](https://github.com/EQEmu/Server/pull/4441)) @Kinglykrab 2024-08-22
### Fixes
* Apply Race & Class restrictions to Auto-Combines ([#4452](https://github.com/EQEmu/Server/pull/4452)) @catapultam-habeo 2024-08-20
* Attune Augments when Equipped ([#4446](https://github.com/EQEmu/Server/pull/4446)) @fryguy503 2024-08-10
* Correct missed maxlevel reference in exp.cpp ([#4463](https://github.com/EQEmu/Server/pull/4463)) @N0ctrnl 2024-08-27
* Ensure close of Tribute Item search ([#4439](https://github.com/EQEmu/Server/pull/4439)) @joligario 2024-08-04
* Fix AddCrystals() in Perl/Lua ([#4445](https://github.com/EQEmu/Server/pull/4445)) @Kinglykrab 2024-08-10
* Fix Bot Spell Entries IDs Capping at 32,767 ([#4444](https://github.com/EQEmu/Server/pull/4444)) @Kinglykrab 2024-08-27
* Fix Character ID of 0 being inserted into character_stats_record ([#4458](https://github.com/EQEmu/Server/pull/4458)) @Kinglykrab 2024-08-22
* Fix Issue with Removed #setfaction Command ([#4448](https://github.com/EQEmu/Server/pull/4448)) @Kinglykrab 2024-08-11
* Fix Lua Client FilteredMessage ([#4437](https://github.com/EQEmu/Server/pull/4437)) @Kinglykrab 2024-07-31
* Fix client hotbar exchanging items when zoning ([#4460](https://github.com/EQEmu/Server/pull/4460)) @neckkola 2024-08-27
* Fix issue with killed mob coordinates ([#4457](https://github.com/EQEmu/Server/pull/4457)) @Kinglykrab 2024-08-22
* Imitate Death should also clear zone feign aggro ([#4436](https://github.com/EQEmu/Server/pull/4436)) @fryguy503 2024-07-31
* client_max_level allow leveling to end of level ([#4455](https://github.com/EQEmu/Server/pull/4455)) @fryguy503 2024-08-20
### Improvement
* Filtered Messages Extension ([#4435](https://github.com/EQEmu/Server/pull/4435)) @fryguy503 2024-07-31
### Quest API
* Add AreTasksCompleted() to Perl/Lua. ([#4456](https://github.com/EQEmu/Server/pull/4456)) @Kinglykrab 2024-08-23
* Add Area-Based Quest Methods to Perl/Lua ([#4447](https://github.com/EQEmu/Server/pull/4447)) @Kinglykrab 2024-08-27
* Add Several Door Methods to Perl/Lua ([#4451](https://github.com/EQEmu/Server/pull/4451)) @Kinglykrab 2024-08-16
### World
* Fix slow world bootup bug ([#4461](https://github.com/EQEmu/Server/pull/4461)) @Akkadius 2024-08-27
## [22.54.0] 7/30/2024 ## [22.54.0] 7/30/2024
### Code ### Code
+32 -8
View File
@@ -66,6 +66,7 @@
#endif #endif
#include "database.h" #include "database.h"
#include "data_verification.h"
#include "eq_packet_structs.h" #include "eq_packet_structs.h"
#include "extprofile.h" #include "extprofile.h"
#include "strings.h" #include "strings.h"
@@ -284,16 +285,31 @@ bool Database::SetAccountStatus(const std::string& account_name, int16 status)
bool Database::ReserveName(uint32 account_id, const std::string& name) bool Database::ReserveName(uint32 account_id, const std::string& name)
{ {
const auto& l = CharacterDataRepository::GetWhere( const std::string& where_filter = fmt::format(
*this,
fmt::format(
"`name` = '{}'", "`name` = '{}'",
Strings::Escape(name) Strings::Escape(name)
)
); );
if (!l.empty()) { if (RuleB(Bots, Enabled)) {
LogInfo("Account: [{}] tried to request name: [{}], but it is already taken", account_id, name); const auto& b = BotDataRepository::GetWhere(*this, where_filter);
if (!b.empty()) {
LogInfo("Account [{}] requested name [{}] but name is already taken by a bot", account_id, name);
return false;
}
}
const auto& c = CharacterDataRepository::GetWhere(*this, where_filter);
if (!c.empty()) {
LogInfo("Account [{}] requested name [{}] but name is already taken by a character", account_id, name);
return false;
}
const auto& n = NpcTypesRepository::GetWhere(*this, where_filter);
if (!n.empty()) {
LogInfo("Account [{}] requested name [{}] but name is already taken by an NPC", account_id, name);
return false; return false;
} }
@@ -308,13 +324,15 @@ bool Database::ReserveName(uint32 account_id, const std::string& name)
return false; return false;
} }
const int guild_id = RuleI(Character, DefaultGuild); const uint32 guild_id = RuleI(Character, DefaultGuild);
const uint8 guild_rank = EQ::Clamp(RuleI(Character, DefaultGuildRank), 0, 8);
if (guild_id != 0) { if (guild_id != 0) {
if (e.id) { if (e.id) {
auto g = GuildMembersRepository::NewEntity(); auto g = GuildMembersRepository::NewEntity();
g.char_id = e.id; g.char_id = e.id;
g.guild_id = guild_id; g.guild_id = guild_id;
g.rank_ = guild_rank;
GuildMembersRepository::InsertOne(*this, g); GuildMembersRepository::InsertOne(*this, g);
} }
@@ -496,6 +514,12 @@ bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, Playe
c.raid_auto_consent = pp->raidAutoconsent; c.raid_auto_consent = pp->raidAutoconsent;
c.guild_auto_consent = pp->guildAutoconsent; c.guild_auto_consent = pp->guildAutoconsent;
c.RestTimer = pp->RestTimer; c.RestTimer = pp->RestTimer;
c.cold_resist = pp->cold_resist;
c.fire_resist = pp->fire_resist;
c.magic_resist = pp->magic_resist;
c.disease_resist = pp->disease_resist;
c.poison_resist = pp->poison_resist;
c.corruption_resist = pp->corruption_resist;
CharacterDataRepository::ReplaceOne(*this, c); CharacterDataRepository::ReplaceOne(*this, c);
@@ -1842,7 +1866,7 @@ bool Database::CopyCharacter(
const int64 new_character_id = (CharacterDataRepository::GetMaxId(*this) + 1); const int64 new_character_id = (CharacterDataRepository::GetMaxId(*this) + 1);
std::vector<std::string> tables_to_zero_id = { "keyring", "data_buckets" }; std::vector<std::string> tables_to_zero_id = { "keyring", "data_buckets", "character_instance_safereturns" };
TransactionBegin(); TransactionBegin();
+42 -1
View File
@@ -4947,7 +4947,7 @@ UPDATE `aa_ability` SET `auto_grant_enabled` = 1 WHERE `grant_only` = 0 AND `cha
.version = 9237, .version = 9237,
.description = "2023_10_15_import_13th_floor.sql", .description = "2023_10_15_import_13th_floor.sql",
.check = "SHOW COLUMNS FROM `items` LIKE 'bardeffect';", .check = "SHOW COLUMNS FROM `items` LIKE 'bardeffect';",
.condition = "contains", .condition = "missing",
.match = "mediumint", .match = "mediumint",
.sql = R"( .sql = R"(
ALTER TABLE `items` ALTER TABLE `items`
@@ -5717,6 +5717,47 @@ CREATE TABLE `buyer_trade_items` (
COLLATE='latin1_swedish_ci' COLLATE='latin1_swedish_ci'
ENGINE=InnoDB ENGINE=InnoDB
AUTO_INCREMENT=1; AUTO_INCREMENT=1;
)"
},
ManifestEntry{
.version = 9282,
.description = "2024_08_02_spell_buckets_comparison.sql",
.check = "SHOW COLUMNS FROM `spell_buckets` LIKE 'bucket_comparison'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `spell_buckets`
CHANGE COLUMN `spellid` `spell_id` int UNSIGNED NOT NULL FIRST,
CHANGE COLUMN `key` `bucket_name` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `spell_id`,
CHANGE COLUMN `value` `bucket_value` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `bucket_name`,
ADD COLUMN `bucket_comparison` tinyint UNSIGNED NOT NULL DEFAULT 0 AFTER `bucket_value`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`spell_id`) USING BTREE;
)"
},
ManifestEntry{
.version = 9283,
.description = "2024_08_05_fix_client_hotbar",
.check = "SHOW COLUMNS FROM `inventory` LIKE 'guid'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `inventory`
ADD COLUMN `guid` BIGINT UNSIGNED NULL DEFAULT '0' AFTER `ornament_hero_model`;
ALTER TABLE `inventory_snapshots`
ADD COLUMN `guid` BIGINT UNSIGNED NULL DEFAULT '0' AFTER `ornament_hero_model`;
)"
},
ManifestEntry{
.version = 9284,
.description = "2024_10_08_character_exp_modifiers_default.sql",
.check = "SHOW CREATE TABLE `character_exp_modifiers`",
.condition = "contains",
.match = "`exp_modifier` float NOT NULL,",
.sql = R"(
ALTER TABLE `character_exp_modifiers`
MODIFY COLUMN `aa_modifier` float NOT NULL DEFAULT 1.0 AFTER `instance_version`,
MODIFY COLUMN `exp_modifier` float NOT NULL DEFAULT 1.0 AFTER `aa_modifier`;
)" )"
} }
// -- template; copy/paste this when you need to create a new entry // -- template; copy/paste this when you need to create a new entry
@@ -150,6 +150,17 @@ ADD COLUMN `augment_six` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_five
.sql = R"( .sql = R"(
ALTER TABLE `bot_data` ALTER TABLE `bot_data`
ADD COLUMN `extra_haste` mediumint(8) NOT NULL DEFAULT 0 AFTER `wis`; ADD COLUMN `extra_haste` mediumint(8) NOT NULL DEFAULT 0 AFTER `wis`;
)"
},
ManifestEntry{
.version = 9045,
.description = "2024_08_05_bot_spells_entries_unsigned_spell_id.sql",
.check = "SHOW COLUMNS FROM `bot_spells_entries` LIKE 'spell_id'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `bot_spells_entries`
CHANGE COLUMN `spellid` `spell_id` smallint(5) UNSIGNED NOT NULL DEFAULT 0 AFTER `npc_spells_id`;
)" )"
} }
// -- template; copy/paste this when you need to create a new entry // -- template; copy/paste this when you need to create a new entry
+11
View File
@@ -751,4 +751,15 @@ static std::map<uint32, std::string> stance_names = {
{ Stance::AEBurn, "AE Burn" } { Stance::AEBurn, "AE Burn" }
}; };
namespace PCNPCOnlyFlagType {
constexpr int PC = 1;
constexpr int NPC = 2;
}
namespace BookType {
constexpr uint8 Scroll = 0;
constexpr uint8 Book = 1;
constexpr uint8 ItemInfo = 2;
}
#endif /*COMMON_EMU_CONSTANTS_H*/ #endif /*COMMON_EMU_CONSTANTS_H*/
+1
View File
@@ -534,6 +534,7 @@ N(OP_Stamina),
N(OP_Stun), N(OP_Stun),
N(OP_Surname), N(OP_Surname),
N(OP_SwapSpell), N(OP_SwapSpell),
N(OP_SystemFingerprint),
N(OP_TargetBuffs), N(OP_TargetBuffs),
N(OP_TargetCommand), N(OP_TargetCommand),
N(OP_TargetHoTT), N(OP_TargetHoTT),
+4 -4
View File
@@ -758,10 +758,10 @@ typedef enum {
FilterFocusEffects = 22, //0=show, 1=hide FilterFocusEffects = 22, //0=show, 1=hide
FilterPetSpells = 23, //0=show, 1=hide FilterPetSpells = 23, //0=show, 1=hide
FilterHealOverTime = 24, //0=show, 1=mine only, 2=hide FilterHealOverTime = 24, //0=show, 1=mine only, 2=hide
FilterUnknown25 = 25, FilterItemSpeech = 25, //0=show, 1=hide // RoF2 Confirmed
FilterUnknown26 = 26, FilterStrikethrough = 26, //0=show, 1=hide // RoF2 Confirmed
FilterUnknown27 = 27, FilterStuns = 27, //0=show, 1=hide // RoF2 Confirmed
FilterUnknown28 = 28, FilterBardSongsOnPets = 28, //0=show, 1=hide // RoF2 Confirmed
_FilterCount _FilterCount
} eqFilterType; } eqFilterType;
+6
View File
@@ -1121,6 +1121,12 @@ struct PlayerProfile_Struct
/*19559*/ uint8 unknown19595[5]; // ***Placeholder (6/29/2005) /*19559*/ uint8 unknown19595[5]; // ***Placeholder (6/29/2005)
/*19564*/ uint32 RestTimer; /*19564*/ uint32 RestTimer;
/*19568*/ uint32 char_id; // Found as part of bazaar revamp (5/15/2024) /*19568*/ uint32 char_id; // Found as part of bazaar revamp (5/15/2024)
/*19572*/ uint32 cold_resist;
/*19576*/ uint32 fire_resist;
/*19580*/ uint32 magic_resist;
/*19584*/ uint32 disease_resist;
/*19588*/ uint32 poison_resist;
/*19592*/ uint32 corruption_resist;
// All player profile packets are translated and this overhead is ignored in out-bound packets // All player profile packets are translated and this overhead is ignored in out-bound packets
PlayerProfile_Struct() : m_player_profile_version(EQ::versions::MobVersion::Unknown) { } PlayerProfile_Struct() : m_player_profile_version(EQ::versions::MobVersion::Unknown) { }
+53 -19
View File
@@ -81,45 +81,79 @@ bool IpUtil::IsIpInPrivateRfc1918(const std::string &ip)
); );
} }
/**
* Gets local address - pings google to inspect what interface was used locally #ifdef _WIN32
* @return #include <winsock2.h>
*/ #include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#endif
#include <iostream>
#include <string>
#include <cstring>
std::string IpUtil::GetLocalIPAddress() std::string IpUtil::GetLocalIPAddress()
{ {
char my_ip_address[16]; #ifdef _WIN32
unsigned int my_port; WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
return "";
}
#endif
char my_ip_address[INET_ADDRSTRLEN];
struct sockaddr_in server_address{}; struct sockaddr_in server_address{};
struct sockaddr_in my_address{}; struct sockaddr_in my_address{};
int sockfd; int sockfd;
// Connect to server // Create a UDP socket
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { #ifdef _WIN32
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == INVALID_SOCKET) {
WSACleanup();
return ""; return "";
} }
#else
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
return "";
}
#endif
// Set server_addr // Set server_addr (dummy address)
memset(&server_address, 0, sizeof(server_address)); memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET; server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("172.217.160.99"); server_address.sin_addr.s_addr = inet_addr("8.8.8.8"); // Google DNS
server_address.sin_port = htons(80); server_address.sin_port = htons(53); // DNS port
// Connect to server // Perform a dummy connection to the server (UDP)
if (connect(sockfd, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) { connect(sockfd, (struct sockaddr *) &server_address, sizeof(server_address));
close(sockfd);
return "";
}
// Get my ip address and port // Get my IP address
memset(&my_address, 0, sizeof(my_address)); memset(&my_address, 0, sizeof(my_address));
socklen_t len = sizeof(my_address); socklen_t len = sizeof(my_address);
getsockname(sockfd, (struct sockaddr *) &my_address, &len); getsockname(sockfd, (struct sockaddr *) &my_address, &len);
inet_ntop(AF_INET, &my_address.sin_addr, my_ip_address, sizeof(my_ip_address)); inet_ntop(AF_INET, &my_address.sin_addr, my_ip_address, sizeof(my_ip_address));
my_port = ntohs(my_address.sin_port);
return fmt::format("{}", my_ip_address); #ifdef _WIN32
closesocket(sockfd);
WSACleanup();
#else
close(sockfd);
#endif
LogInfo("Local IP Address [{}]", my_ip_address);
return std::string(my_ip_address);
} }
/** /**
* Gets public address * Gets public address
* Uses various websites as options to return raw public IP back to the client * Uses various websites as options to return raw public IP back to the client
+24 -8
View File
@@ -32,10 +32,11 @@
//#include <iostream> //#include <iostream>
int32 NextItemInstSerialNumber = 1; int32 next_item_serial_number = 1;
std::unordered_set<uint64> guids{};
static inline int32 GetNextItemInstSerialNumber() {
static inline int32 GetNextItemInstSerialNumber()
{
// The Bazaar relies on each item a client has up for Trade having a unique // The Bazaar relies on each item a client has up for Trade having a unique
// identifier. This 'SerialNumber' is sent in Serialized item packets and // identifier. This 'SerialNumber' is sent in Serialized item packets and
// is used in Bazaar packets to identify the item a player is buying or inspecting. // is used in Bazaar packets to identify the item a player is buying or inspecting.
@@ -46,12 +47,18 @@ static inline int32 GetNextItemInstSerialNumber() {
// NextItemInstSerialNumber is the next one to hand out. // NextItemInstSerialNumber is the next one to hand out.
// //
// It is very unlikely to reach 2,147,483,647. Maybe we should call abort(), rather than wrapping back to 1. // It is very unlikely to reach 2,147,483,647. Maybe we should call abort(), rather than wrapping back to 1.
if(NextItemInstSerialNumber >= INT_MAX) if (next_item_serial_number >= INT32_MAX) {
NextItemInstSerialNumber = 1; next_item_serial_number = 1;
else }
NextItemInstSerialNumber++; else {
next_item_serial_number++;
}
return NextItemInstSerialNumber; while (guids.contains(next_item_serial_number)) {
next_item_serial_number++;
}
return next_item_serial_number;
} }
// //
@@ -1935,6 +1942,15 @@ int EQ::ItemInstance::GetItemSkillsStat(EQ::skills::SkillType skill, bool augmen
return stat; return stat;
} }
void EQ::ItemInstance::AddGUIDToMap(uint64 existing_serial_number)
{
guids.emplace(existing_serial_number);
}
void EQ::ItemInstance::ClearGUIDMap()
{
guids.clear();
}
// //
// class EvolveInfo // class EvolveInfo
// //
+2
View File
@@ -309,6 +309,8 @@ namespace EQ
int GetItemSkillsStat(EQ::skills::SkillType skill, bool augments = false) const; int GetItemSkillsStat(EQ::skills::SkillType skill, bool augments = false) const;
uint32 GetItemGuildFavor() const; uint32 GetItemGuildFavor() const;
std::vector<uint32> GetAugmentIDs() const; std::vector<uint32> GetAugmentIDs() const;
static void AddGUIDToMap(uint64 existing_serial_number);
static void ClearGUIDMap();
protected: protected:
////////////////////////// //////////////////////////
+6 -6
View File
@@ -2288,13 +2288,13 @@ namespace RoF
outapp->WriteSInt32(345); // Mana Total ? outapp->WriteSInt32(345); // Mana Total ?
// these are needed to fix display bugs // these are needed to fix display bugs
outapp->WriteUInt32(0x19); // base CR outapp->WriteUInt32(emu->cold_resist); // base CR
outapp->WriteUInt32(0x19); // base FR outapp->WriteUInt32(emu->fire_resist); // base FR
outapp->WriteUInt32(0x19); // base MR outapp->WriteUInt32(emu->magic_resist); // base MR
outapp->WriteUInt32(0xf); // base DR outapp->WriteUInt32(emu->disease_resist); // base DR
outapp->WriteUInt32(0xf); // base PR outapp->WriteUInt32(emu->poison_resist); // base PR
outapp->WriteUInt32(0xf); // base PhR? outapp->WriteUInt32(0xf); // base PhR?
outapp->WriteUInt32(0xf); // base Corrup outapp->WriteUInt32(emu->corruption_resist); // base Corrup
outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown
+6 -6
View File
@@ -2806,13 +2806,13 @@ namespace RoF2
outapp->WriteSInt32(345); // Mana Total ? outapp->WriteSInt32(345); // Mana Total ?
// these are needed to fix display bugs // these are needed to fix display bugs
outapp->WriteUInt32(0x19); // base CR outapp->WriteUInt32(emu->cold_resist); // base CR
outapp->WriteUInt32(0x19); // base FR outapp->WriteUInt32(emu->fire_resist); // base FR
outapp->WriteUInt32(0x19); // base MR outapp->WriteUInt32(emu->magic_resist); // base MR
outapp->WriteUInt32(0xf); // base DR outapp->WriteUInt32(emu->disease_resist); // base DR
outapp->WriteUInt32(0xf); // base PR outapp->WriteUInt32(emu->poison_resist); // base PR
outapp->WriteUInt32(0xf); // base PhR? outapp->WriteUInt32(0xf); // base PhR?
outapp->WriteUInt32(0xf); // base Corrup outapp->WriteUInt32(emu->corruption_resist); // base Corrup
outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown
+19 -7
View File
@@ -1669,15 +1669,27 @@ namespace SoD
// OUT(unknown19584[4]); // OUT(unknown19584[4]);
// OUT(unknown19588); // OUT(unknown19588);
const uint8 bytes[] = { const uint8 unknown12864_bytes[] = {
0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F,
0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}; };
memcpy(eq->unknown12864, bytes, sizeof(bytes)); memcpy(eq->unknown12864, unknown12864_bytes, sizeof(unknown12864_bytes));
eq->cold_resist = emu->cold_resist;
eq->fire_resist = emu->fire_resist;
eq->magic_resist = emu->magic_resist;
eq->disease_resist = emu->disease_resist;
eq->poison_resist = emu->poison_resist;
eq->physical_resist = 15;
eq->corruption_resist = emu->corruption_resist;
const uint8 unknown15112_bytes[] = {
0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
memcpy(eq->unknown15112, unknown15112_bytes, sizeof(unknown15112_bytes));
//set the checksum... //set the checksum...
CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4);
+9 -1
View File
@@ -944,7 +944,15 @@ struct PlayerProfile_Struct
/*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot /*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot
/*15060*/ uint8 unknown12852[8]; /*15060*/ uint8 unknown12852[8];
/*15068*/ uint32 available_slots; /*15068*/ uint32 available_slots;
/*15072*/ uint8 unknown12864[80]; //#### uint8 uint8 unknown12864[76]; in Titanium ####[80] /*15072*/ uint8 unknown12864[12]; //#### uint8 uint8 unknown12864[76]; in Titanium ####[80]
/*15084*/ uint32 cold_resist;
/*15088*/ uint32 fire_resist;
/*15092*/ uint32 magic_resist;
/*15096*/ uint32 disease_resist;
/*15100*/ uint32 poison_resist;
/*15104*/ uint32 physical_resist;
/*15108*/ uint32 corruption_resist;
/*15112*/ uint8 unknown15112[40];
//END SUB-STRUCT used for shrouding. //END SUB-STRUCT used for shrouding.
/*15152*/ char name[64]; // Name of player /*15152*/ char name[64]; // Name of player
/*15216*/ char last_name[32]; // Last name of player /*15216*/ char last_name[32]; // Last name of player
+19 -7
View File
@@ -1339,15 +1339,27 @@ namespace SoF
// OUT(unknown19584[4]); // OUT(unknown19584[4]);
// OUT(unknown19588); // OUT(unknown19588);
const uint8 bytes[] = { const uint8 unknown12864_bytes[] = {
0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F,
0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}; };
memcpy(eq->unknown12864, bytes, sizeof(bytes)); memcpy(eq->unknown12864, unknown12864_bytes, sizeof(unknown12864_bytes));
eq->cold_resist = emu->cold_resist;
eq->fire_resist = emu->fire_resist;
eq->magic_resist = emu->magic_resist;
eq->disease_resist = emu->disease_resist;
eq->poison_resist = emu->poison_resist;
eq->physical_resist = 15;
eq->corruption_resist = emu->corruption_resist;
const uint8 unknown15112_bytes[] = {
0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
memcpy(eq->unknown15112, unknown15112_bytes, sizeof(unknown15112_bytes));
//set the checksum... //set the checksum...
CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4);
+10 -1
View File
@@ -944,7 +944,16 @@ struct PlayerProfile_Struct //23576 Octets
/*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot /*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot
/*15060*/ uint8 unknown12852[8]; /*15060*/ uint8 unknown12852[8];
/*15068*/ uint32 available_slots; /*15068*/ uint32 available_slots;
/*15072*/ uint8 unknown12864[80]; //#### uint8 uint8 unknown12864[76]; in Titanium ####[80] /*15072*/ uint8 unknown12864[12]; //#### uint8 uint8 unknown12864[76]; in Titanium ####[80]
/*15084*/ uint32 cold_resist;
/*15088*/ uint32 fire_resist;
/*15092*/ uint32 magic_resist;
/*15096*/ uint32 disease_resist;
/*15100*/ uint32 poison_resist;
/*15104*/ uint32 physical_resist;
/*15108*/ uint32 corruption_resist;
/*15112*/ uint8 unknown15112[40];
//END SUB-STRUCT used for shrouding. //END SUB-STRUCT used for shrouding.
/*15120*/ char name[64]; // Name of player /*15120*/ char name[64]; // Name of player
/*15184*/ char last_name[32]; // Last name of player /*15184*/ char last_name[32]; // Last name of player
+16 -5
View File
@@ -1600,14 +1600,25 @@ namespace Titanium
// OUT(unknown19584[4]); // OUT(unknown19584[4]);
// OUT(unknown19588); // OUT(unknown19588);
const uint8 unknown12864_bytes[] = {
0x78, 0x03, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00
};
const uint8 bytes[] = { memcpy(eq->unknown12864, unknown12864_bytes, sizeof(unknown12864_bytes));
0x78, 0x03, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, eq->cold_resist = emu->cold_resist;
0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, 0x09, 0x00, 0x00, 0x00, eq->fire_resist = emu->fire_resist;
eq->magic_resist = emu->magic_resist;
eq->disease_resist = emu->disease_resist;
eq->poison_resist = emu->poison_resist;
eq->physical_resist = 15;
const uint8 unknown12900_bytes[] = {
0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, 0x09, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14
}; };
memcpy(eq->unknown12864, bytes, sizeof(bytes));
memcpy(eq->unknown12900, unknown12900_bytes, sizeof(unknown12900_bytes));
//set the checksum... //set the checksum...
CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4);
+8 -1
View File
@@ -879,7 +879,14 @@ struct PlayerProfile_Struct
/*12564*/ PotionBelt_Struct potionbelt; // potion belt /*12564*/ PotionBelt_Struct potionbelt; // potion belt
/*12852*/ uint8 unknown12852[8]; /*12852*/ uint8 unknown12852[8];
/*12860*/ uint32 available_slots; /*12860*/ uint32 available_slots;
/*12864*/ uint8 unknown12864[76]; /*12864*/ uint8 unknown12864[12];
/*12876*/ uint32 cold_resist;
/*12880*/ uint32 fire_resist;
/*12884*/ uint32 magic_resist;
/*12888*/ uint32 disease_resist;
/*12892*/ uint32 poison_resist;
/*12896*/ uint32 physical_resist;
/*12900*/ uint8 unknown12900[40];
/*12940*/ char name[64]; // Name of player /*12940*/ char name[64]; // Name of player
/*13004*/ char last_name[32]; // Last name of player /*13004*/ char last_name[32]; // Last name of player
/*13036*/ uint32 guild_id; // guildid /*13036*/ uint32 guild_id; // guildid
@@ -21,7 +21,7 @@ public:
struct BotSpellsEntries { struct BotSpellsEntries {
uint32_t id; uint32_t id;
int32_t npc_spells_id; int32_t npc_spells_id;
int16_t spellid; uint16_t spell_id;
uint32_t type; uint32_t type;
uint8_t minlevel; uint8_t minlevel;
uint8_t maxlevel; uint8_t maxlevel;
@@ -46,7 +46,7 @@ public:
return { return {
"id", "id",
"npc_spells_id", "npc_spells_id",
"spellid", "spell_id",
"type", "type",
"minlevel", "minlevel",
"maxlevel", "maxlevel",
@@ -67,7 +67,7 @@ public:
return { return {
"id", "id",
"npc_spells_id", "npc_spells_id",
"spellid", "spell_id",
"type", "type",
"minlevel", "minlevel",
"maxlevel", "maxlevel",
@@ -122,7 +122,7 @@ public:
e.id = 0; e.id = 0;
e.npc_spells_id = 0; e.npc_spells_id = 0;
e.spellid = 0; e.spell_id = 0;
e.type = 0; e.type = 0;
e.minlevel = 0; e.minlevel = 0;
e.maxlevel = 255; e.maxlevel = 255;
@@ -173,7 +173,7 @@ public:
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0; e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0; e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
e.spellid = row[2] ? static_cast<int16_t>(atoi(row[2])) : 0; e.spell_id = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 0;
e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0; e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0; e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255; e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255;
@@ -220,7 +220,7 @@ public:
auto columns = Columns(); auto columns = Columns();
v.push_back(columns[1] + " = " + std::to_string(e.npc_spells_id)); v.push_back(columns[1] + " = " + std::to_string(e.npc_spells_id));
v.push_back(columns[2] + " = " + std::to_string(e.spellid)); v.push_back(columns[2] + " = " + std::to_string(e.spell_id));
v.push_back(columns[3] + " = " + std::to_string(e.type)); v.push_back(columns[3] + " = " + std::to_string(e.type));
v.push_back(columns[4] + " = " + std::to_string(e.minlevel)); v.push_back(columns[4] + " = " + std::to_string(e.minlevel));
v.push_back(columns[5] + " = " + std::to_string(e.maxlevel)); v.push_back(columns[5] + " = " + std::to_string(e.maxlevel));
@@ -256,7 +256,7 @@ public:
v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.npc_spells_id)); v.push_back(std::to_string(e.npc_spells_id));
v.push_back(std::to_string(e.spellid)); v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.type)); v.push_back(std::to_string(e.type));
v.push_back(std::to_string(e.minlevel)); v.push_back(std::to_string(e.minlevel));
v.push_back(std::to_string(e.maxlevel)); v.push_back(std::to_string(e.maxlevel));
@@ -300,7 +300,7 @@ public:
v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.npc_spells_id)); v.push_back(std::to_string(e.npc_spells_id));
v.push_back(std::to_string(e.spellid)); v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.type)); v.push_back(std::to_string(e.type));
v.push_back(std::to_string(e.minlevel)); v.push_back(std::to_string(e.minlevel));
v.push_back(std::to_string(e.maxlevel)); v.push_back(std::to_string(e.maxlevel));
@@ -348,7 +348,7 @@ public:
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0; e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0; e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
e.spellid = row[2] ? static_cast<int16_t>(atoi(row[2])) : 0; e.spell_id = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 0;
e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0; e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0; e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255; e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255;
@@ -387,7 +387,7 @@ public:
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0; e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0; e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
e.spellid = row[2] ? static_cast<int16_t>(atoi(row[2])) : 0; e.spell_id = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 0;
e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0; e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0; e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255; e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255;
@@ -476,7 +476,7 @@ public:
v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.npc_spells_id)); v.push_back(std::to_string(e.npc_spells_id));
v.push_back(std::to_string(e.spellid)); v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.type)); v.push_back(std::to_string(e.type));
v.push_back(std::to_string(e.minlevel)); v.push_back(std::to_string(e.minlevel));
v.push_back(std::to_string(e.maxlevel)); v.push_back(std::to_string(e.maxlevel));
@@ -513,7 +513,7 @@ public:
v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.npc_spells_id)); v.push_back(std::to_string(e.npc_spells_id));
v.push_back(std::to_string(e.spellid)); v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.type)); v.push_back(std::to_string(e.type));
v.push_back(std::to_string(e.minlevel)); v.push_back(std::to_string(e.minlevel));
v.push_back(std::to_string(e.maxlevel)); v.push_back(std::to_string(e.maxlevel));
@@ -34,6 +34,12 @@ public:
uint32_t alloc_int; uint32_t alloc_int;
uint32_t alloc_wis; uint32_t alloc_wis;
uint32_t alloc_cha; uint32_t alloc_cha;
uint32_t base_cr;
uint32_t base_fr;
uint32_t base_mr;
uint32_t base_dr;
uint32_t base_pr;
uint32_t base_corrup;
}; };
static std::string PrimaryKey() static std::string PrimaryKey()
@@ -59,6 +65,12 @@ public:
"alloc_int", "alloc_int",
"alloc_wis", "alloc_wis",
"alloc_cha", "alloc_cha",
"base_cr",
"base_fr",
"base_mr",
"base_dr",
"base_pr",
"base_corrup",
}; };
} }
@@ -80,6 +92,12 @@ public:
"alloc_int", "alloc_int",
"alloc_wis", "alloc_wis",
"alloc_cha", "alloc_cha",
"base_cr",
"base_fr",
"base_mr",
"base_dr",
"base_pr",
"base_corrup",
}; };
} }
@@ -135,6 +153,12 @@ public:
e.alloc_int = 0; e.alloc_int = 0;
e.alloc_wis = 0; e.alloc_wis = 0;
e.alloc_cha = 0; e.alloc_cha = 0;
e.base_cr = 0;
e.base_fr = 0;
e.base_mr = 0;
e.base_dr = 0;
e.base_pr = 0;
e.base_corrup = 0;
return e; return e;
} }
@@ -186,6 +210,12 @@ public:
e.alloc_int = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0; e.alloc_int = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
e.alloc_wis = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0; e.alloc_wis = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.alloc_cha = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0; e.alloc_cha = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.base_cr = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
e.base_fr = row[16] ? static_cast<uint32_t>(strtoul(row[16], nullptr, 10)) : 0;
e.base_mr = row[17] ? static_cast<uint32_t>(strtoul(row[17], nullptr, 10)) : 0;
e.base_dr = row[18] ? static_cast<uint32_t>(strtoul(row[18], nullptr, 10)) : 0;
e.base_pr = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
e.base_corrup = row[20] ? static_cast<uint32_t>(strtoul(row[20], nullptr, 10)) : 0;
return e; return e;
} }
@@ -234,6 +264,12 @@ public:
v.push_back(columns[12] + " = " + std::to_string(e.alloc_int)); v.push_back(columns[12] + " = " + std::to_string(e.alloc_int));
v.push_back(columns[13] + " = " + std::to_string(e.alloc_wis)); v.push_back(columns[13] + " = " + std::to_string(e.alloc_wis));
v.push_back(columns[14] + " = " + std::to_string(e.alloc_cha)); v.push_back(columns[14] + " = " + std::to_string(e.alloc_cha));
v.push_back(columns[15] + " = " + std::to_string(e.base_cr));
v.push_back(columns[16] + " = " + std::to_string(e.base_fr));
v.push_back(columns[17] + " = " + std::to_string(e.base_mr));
v.push_back(columns[18] + " = " + std::to_string(e.base_dr));
v.push_back(columns[19] + " = " + std::to_string(e.base_pr));
v.push_back(columns[20] + " = " + std::to_string(e.base_corrup));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -270,6 +306,12 @@ public:
v.push_back(std::to_string(e.alloc_int)); v.push_back(std::to_string(e.alloc_int));
v.push_back(std::to_string(e.alloc_wis)); v.push_back(std::to_string(e.alloc_wis));
v.push_back(std::to_string(e.alloc_cha)); v.push_back(std::to_string(e.alloc_cha));
v.push_back(std::to_string(e.base_cr));
v.push_back(std::to_string(e.base_fr));
v.push_back(std::to_string(e.base_mr));
v.push_back(std::to_string(e.base_dr));
v.push_back(std::to_string(e.base_pr));
v.push_back(std::to_string(e.base_corrup));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -314,6 +356,12 @@ public:
v.push_back(std::to_string(e.alloc_int)); v.push_back(std::to_string(e.alloc_int));
v.push_back(std::to_string(e.alloc_wis)); v.push_back(std::to_string(e.alloc_wis));
v.push_back(std::to_string(e.alloc_cha)); v.push_back(std::to_string(e.alloc_cha));
v.push_back(std::to_string(e.base_cr));
v.push_back(std::to_string(e.base_fr));
v.push_back(std::to_string(e.base_mr));
v.push_back(std::to_string(e.base_dr));
v.push_back(std::to_string(e.base_pr));
v.push_back(std::to_string(e.base_corrup));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -362,6 +410,12 @@ public:
e.alloc_int = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0; e.alloc_int = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
e.alloc_wis = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0; e.alloc_wis = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.alloc_cha = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0; e.alloc_cha = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.base_cr = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
e.base_fr = row[16] ? static_cast<uint32_t>(strtoul(row[16], nullptr, 10)) : 0;
e.base_mr = row[17] ? static_cast<uint32_t>(strtoul(row[17], nullptr, 10)) : 0;
e.base_dr = row[18] ? static_cast<uint32_t>(strtoul(row[18], nullptr, 10)) : 0;
e.base_pr = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
e.base_corrup = row[20] ? static_cast<uint32_t>(strtoul(row[20], nullptr, 10)) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -401,6 +455,12 @@ public:
e.alloc_int = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0; e.alloc_int = row[12] ? static_cast<uint32_t>(strtoul(row[12], nullptr, 10)) : 0;
e.alloc_wis = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0; e.alloc_wis = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.alloc_cha = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0; e.alloc_cha = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.base_cr = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
e.base_fr = row[16] ? static_cast<uint32_t>(strtoul(row[16], nullptr, 10)) : 0;
e.base_mr = row[17] ? static_cast<uint32_t>(strtoul(row[17], nullptr, 10)) : 0;
e.base_dr = row[18] ? static_cast<uint32_t>(strtoul(row[18], nullptr, 10)) : 0;
e.base_pr = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
e.base_corrup = row[20] ? static_cast<uint32_t>(strtoul(row[20], nullptr, 10)) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -490,6 +550,12 @@ public:
v.push_back(std::to_string(e.alloc_int)); v.push_back(std::to_string(e.alloc_int));
v.push_back(std::to_string(e.alloc_wis)); v.push_back(std::to_string(e.alloc_wis));
v.push_back(std::to_string(e.alloc_cha)); v.push_back(std::to_string(e.alloc_cha));
v.push_back(std::to_string(e.base_cr));
v.push_back(std::to_string(e.base_fr));
v.push_back(std::to_string(e.base_mr));
v.push_back(std::to_string(e.base_dr));
v.push_back(std::to_string(e.base_pr));
v.push_back(std::to_string(e.base_corrup));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -527,6 +593,12 @@ public:
v.push_back(std::to_string(e.alloc_int)); v.push_back(std::to_string(e.alloc_int));
v.push_back(std::to_string(e.alloc_wis)); v.push_back(std::to_string(e.alloc_wis));
v.push_back(std::to_string(e.alloc_cha)); v.push_back(std::to_string(e.alloc_cha));
v.push_back(std::to_string(e.base_cr));
v.push_back(std::to_string(e.base_fr));
v.push_back(std::to_string(e.base_mr));
v.push_back(std::to_string(e.base_dr));
v.push_back(std::to_string(e.base_pr));
v.push_back(std::to_string(e.base_corrup));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -123,6 +123,12 @@ public:
uint32_t aa_points_old; uint32_t aa_points_old;
uint32_t e_last_invsnapshot; uint32_t e_last_invsnapshot;
time_t deleted_at; time_t deleted_at;
uint32_t cold_resist;
uint32_t fire_resist;
uint32_t magic_resist;
uint32_t disease_resist;
uint32_t poison_resist;
uint32_t corruption_resist;
}; };
static std::string PrimaryKey() static std::string PrimaryKey()
@@ -237,6 +243,12 @@ public:
"aa_points_old", "aa_points_old",
"e_last_invsnapshot", "e_last_invsnapshot",
"deleted_at", "deleted_at",
"cold_resist",
"fire_resist",
"magic_resist",
"disease_resist",
"poison_resist",
"corruption_resist",
}; };
} }
@@ -347,6 +359,12 @@ public:
"aa_points_old", "aa_points_old",
"e_last_invsnapshot", "e_last_invsnapshot",
"UNIX_TIMESTAMP(deleted_at)", "UNIX_TIMESTAMP(deleted_at)",
"cold_resist",
"fire_resist",
"magic_resist",
"disease_resist",
"poison_resist",
"corruption_resist",
}; };
} }
@@ -491,6 +509,12 @@ public:
e.aa_points_old = 0; e.aa_points_old = 0;
e.e_last_invsnapshot = 0; e.e_last_invsnapshot = 0;
e.deleted_at = 0; e.deleted_at = 0;
e.cold_resist = 0;
e.fire_resist = 0;
e.magic_resist = 0;
e.disease_resist = 0;
e.poison_resist = 0;
e.corruption_resist = 0;
return e; return e;
} }
@@ -631,6 +655,12 @@ public:
e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0; e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0;
e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0; e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0;
e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10); e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10);
e.cold_resist = row[104] ? static_cast<uint32_t>(strtoul(row[104], nullptr, 10)) : 0;
e.fire_resist = row[105] ? static_cast<uint32_t>(strtoul(row[105], nullptr, 10)) : 0;
e.magic_resist = row[106] ? static_cast<uint32_t>(strtoul(row[106], nullptr, 10)) : 0;
e.disease_resist = row[107] ? static_cast<uint32_t>(strtoul(row[107], nullptr, 10)) : 0;
e.poison_resist = row[108] ? static_cast<uint32_t>(strtoul(row[108], nullptr, 10)) : 0;
e.corruption_resist = row[109] ? static_cast<uint32_t>(strtoul(row[109], nullptr, 10)) : 0;
return e; return e;
} }
@@ -767,6 +797,12 @@ public:
v.push_back(columns[101] + " = " + std::to_string(e.aa_points_old)); v.push_back(columns[101] + " = " + std::to_string(e.aa_points_old));
v.push_back(columns[102] + " = " + std::to_string(e.e_last_invsnapshot)); v.push_back(columns[102] + " = " + std::to_string(e.e_last_invsnapshot));
v.push_back(columns[103] + " = FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); v.push_back(columns[103] + " = FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
v.push_back(columns[104] + " = " + std::to_string(e.cold_resist));
v.push_back(columns[105] + " = " + std::to_string(e.fire_resist));
v.push_back(columns[106] + " = " + std::to_string(e.magic_resist));
v.push_back(columns[107] + " = " + std::to_string(e.disease_resist));
v.push_back(columns[108] + " = " + std::to_string(e.poison_resist));
v.push_back(columns[109] + " = " + std::to_string(e.corruption_resist));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -892,6 +928,12 @@ public:
v.push_back(std::to_string(e.aa_points_old)); v.push_back(std::to_string(e.aa_points_old));
v.push_back(std::to_string(e.e_last_invsnapshot)); v.push_back(std::to_string(e.e_last_invsnapshot));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
v.push_back(std::to_string(e.cold_resist));
v.push_back(std::to_string(e.fire_resist));
v.push_back(std::to_string(e.magic_resist));
v.push_back(std::to_string(e.disease_resist));
v.push_back(std::to_string(e.poison_resist));
v.push_back(std::to_string(e.corruption_resist));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -1025,6 +1067,12 @@ public:
v.push_back(std::to_string(e.aa_points_old)); v.push_back(std::to_string(e.aa_points_old));
v.push_back(std::to_string(e.e_last_invsnapshot)); v.push_back(std::to_string(e.e_last_invsnapshot));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
v.push_back(std::to_string(e.cold_resist));
v.push_back(std::to_string(e.fire_resist));
v.push_back(std::to_string(e.magic_resist));
v.push_back(std::to_string(e.disease_resist));
v.push_back(std::to_string(e.poison_resist));
v.push_back(std::to_string(e.corruption_resist));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -1162,6 +1210,12 @@ public:
e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0; e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0;
e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0; e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0;
e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10); e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10);
e.cold_resist = row[104] ? static_cast<uint32_t>(strtoul(row[104], nullptr, 10)) : 0;
e.fire_resist = row[105] ? static_cast<uint32_t>(strtoul(row[105], nullptr, 10)) : 0;
e.magic_resist = row[106] ? static_cast<uint32_t>(strtoul(row[106], nullptr, 10)) : 0;
e.disease_resist = row[107] ? static_cast<uint32_t>(strtoul(row[107], nullptr, 10)) : 0;
e.poison_resist = row[108] ? static_cast<uint32_t>(strtoul(row[108], nullptr, 10)) : 0;
e.corruption_resist = row[109] ? static_cast<uint32_t>(strtoul(row[109], nullptr, 10)) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -1290,6 +1344,12 @@ public:
e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0; e.aa_points_old = row[101] ? static_cast<uint32_t>(strtoul(row[101], nullptr, 10)) : 0;
e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0; e.e_last_invsnapshot = row[102] ? static_cast<uint32_t>(strtoul(row[102], nullptr, 10)) : 0;
e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10); e.deleted_at = strtoll(row[103] ? row[103] : "-1", nullptr, 10);
e.cold_resist = row[104] ? static_cast<uint32_t>(strtoul(row[104], nullptr, 10)) : 0;
e.fire_resist = row[105] ? static_cast<uint32_t>(strtoul(row[105], nullptr, 10)) : 0;
e.magic_resist = row[106] ? static_cast<uint32_t>(strtoul(row[106], nullptr, 10)) : 0;
e.disease_resist = row[107] ? static_cast<uint32_t>(strtoul(row[107], nullptr, 10)) : 0;
e.poison_resist = row[108] ? static_cast<uint32_t>(strtoul(row[108], nullptr, 10)) : 0;
e.corruption_resist = row[109] ? static_cast<uint32_t>(strtoul(row[109], nullptr, 10)) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -1468,6 +1528,12 @@ public:
v.push_back(std::to_string(e.aa_points_old)); v.push_back(std::to_string(e.aa_points_old));
v.push_back(std::to_string(e.e_last_invsnapshot)); v.push_back(std::to_string(e.e_last_invsnapshot));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
v.push_back(std::to_string(e.cold_resist));
v.push_back(std::to_string(e.fire_resist));
v.push_back(std::to_string(e.magic_resist));
v.push_back(std::to_string(e.disease_resist));
v.push_back(std::to_string(e.poison_resist));
v.push_back(std::to_string(e.corruption_resist));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -1594,6 +1660,12 @@ public:
v.push_back(std::to_string(e.aa_points_old)); v.push_back(std::to_string(e.aa_points_old));
v.push_back(std::to_string(e.e_last_invsnapshot)); v.push_back(std::to_string(e.e_last_invsnapshot));
v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")");
v.push_back(std::to_string(e.cold_resist));
v.push_back(std::to_string(e.fire_resist));
v.push_back(std::to_string(e.magic_resist));
v.push_back(std::to_string(e.disease_resist));
v.push_back(std::to_string(e.poison_resist));
v.push_back(std::to_string(e.corruption_resist));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -35,6 +35,7 @@ public:
uint32_t ornamenticon; uint32_t ornamenticon;
uint32_t ornamentidfile; uint32_t ornamentidfile;
int32_t ornament_hero_model; int32_t ornament_hero_model;
uint64_t guid;
}; };
static std::string PrimaryKey() static std::string PrimaryKey()
@@ -61,6 +62,7 @@ public:
"ornamenticon", "ornamenticon",
"ornamentidfile", "ornamentidfile",
"ornament_hero_model", "ornament_hero_model",
"guid",
}; };
} }
@@ -83,6 +85,7 @@ public:
"ornamenticon", "ornamenticon",
"ornamentidfile", "ornamentidfile",
"ornament_hero_model", "ornament_hero_model",
"guid",
}; };
} }
@@ -139,6 +142,7 @@ public:
e.ornamenticon = 0; e.ornamenticon = 0;
e.ornamentidfile = 0; e.ornamentidfile = 0;
e.ornament_hero_model = 0; e.ornament_hero_model = 0;
e.guid = 0;
return e; return e;
} }
@@ -191,6 +195,7 @@ public:
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0; e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0; e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0; e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
return e; return e;
} }
@@ -240,6 +245,7 @@ public:
v.push_back(columns[13] + " = " + std::to_string(e.ornamenticon)); v.push_back(columns[13] + " = " + std::to_string(e.ornamenticon));
v.push_back(columns[14] + " = " + std::to_string(e.ornamentidfile)); v.push_back(columns[14] + " = " + std::to_string(e.ornamentidfile));
v.push_back(columns[15] + " = " + std::to_string(e.ornament_hero_model)); v.push_back(columns[15] + " = " + std::to_string(e.ornament_hero_model));
v.push_back(columns[16] + " = " + std::to_string(e.guid));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -277,6 +283,7 @@ public:
v.push_back(std::to_string(e.ornamenticon)); v.push_back(std::to_string(e.ornamenticon));
v.push_back(std::to_string(e.ornamentidfile)); v.push_back(std::to_string(e.ornamentidfile));
v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.ornament_hero_model));
v.push_back(std::to_string(e.guid));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -322,6 +329,7 @@ public:
v.push_back(std::to_string(e.ornamenticon)); v.push_back(std::to_string(e.ornamenticon));
v.push_back(std::to_string(e.ornamentidfile)); v.push_back(std::to_string(e.ornamentidfile));
v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.ornament_hero_model));
v.push_back(std::to_string(e.guid));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -371,6 +379,7 @@ public:
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0; e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0; e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0; e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -411,6 +420,7 @@ public:
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0; e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0; e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0; e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -501,6 +511,7 @@ public:
v.push_back(std::to_string(e.ornamenticon)); v.push_back(std::to_string(e.ornamenticon));
v.push_back(std::to_string(e.ornamentidfile)); v.push_back(std::to_string(e.ornamentidfile));
v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.ornament_hero_model));
v.push_back(std::to_string(e.guid));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -539,6 +550,7 @@ public:
v.push_back(std::to_string(e.ornamenticon)); v.push_back(std::to_string(e.ornamenticon));
v.push_back(std::to_string(e.ornamentidfile)); v.push_back(std::to_string(e.ornamentidfile));
v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.ornament_hero_model));
v.push_back(std::to_string(e.guid));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -36,6 +36,7 @@ public:
uint32_t ornamenticon; uint32_t ornamenticon;
uint32_t ornamentidfile; uint32_t ornamentidfile;
int32_t ornament_hero_model; int32_t ornament_hero_model;
uint64_t guid;
}; };
static std::string PrimaryKey() static std::string PrimaryKey()
@@ -63,6 +64,7 @@ public:
"ornamenticon", "ornamenticon",
"ornamentidfile", "ornamentidfile",
"ornament_hero_model", "ornament_hero_model",
"guid",
}; };
} }
@@ -86,6 +88,7 @@ public:
"ornamenticon", "ornamenticon",
"ornamentidfile", "ornamentidfile",
"ornament_hero_model", "ornament_hero_model",
"guid",
}; };
} }
@@ -143,6 +146,7 @@ public:
e.ornamenticon = 0; e.ornamenticon = 0;
e.ornamentidfile = 0; e.ornamentidfile = 0;
e.ornament_hero_model = 0; e.ornament_hero_model = 0;
e.guid = 0;
return e; return e;
} }
@@ -196,6 +200,7 @@ public:
e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0; e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0; e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0; e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
e.guid = row[17] ? strtoull(row[17], nullptr, 10) : 0;
return e; return e;
} }
@@ -246,6 +251,7 @@ public:
v.push_back(columns[14] + " = " + std::to_string(e.ornamenticon)); v.push_back(columns[14] + " = " + std::to_string(e.ornamenticon));
v.push_back(columns[15] + " = " + std::to_string(e.ornamentidfile)); v.push_back(columns[15] + " = " + std::to_string(e.ornamentidfile));
v.push_back(columns[16] + " = " + std::to_string(e.ornament_hero_model)); v.push_back(columns[16] + " = " + std::to_string(e.ornament_hero_model));
v.push_back(columns[17] + " = " + std::to_string(e.guid));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -284,6 +290,7 @@ public:
v.push_back(std::to_string(e.ornamenticon)); v.push_back(std::to_string(e.ornamenticon));
v.push_back(std::to_string(e.ornamentidfile)); v.push_back(std::to_string(e.ornamentidfile));
v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.ornament_hero_model));
v.push_back(std::to_string(e.guid));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -330,6 +337,7 @@ public:
v.push_back(std::to_string(e.ornamenticon)); v.push_back(std::to_string(e.ornamenticon));
v.push_back(std::to_string(e.ornamentidfile)); v.push_back(std::to_string(e.ornamentidfile));
v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.ornament_hero_model));
v.push_back(std::to_string(e.guid));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -380,6 +388,7 @@ public:
e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0; e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0; e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0; e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
e.guid = row[17] ? strtoull(row[17], nullptr, 10) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -421,6 +430,7 @@ public:
e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0; e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0; e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0; e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
e.guid = row[17] ? strtoull(row[17], nullptr, 10) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -512,6 +522,7 @@ public:
v.push_back(std::to_string(e.ornamenticon)); v.push_back(std::to_string(e.ornamenticon));
v.push_back(std::to_string(e.ornamentidfile)); v.push_back(std::to_string(e.ornamentidfile));
v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.ornament_hero_model));
v.push_back(std::to_string(e.guid));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -551,6 +562,7 @@ public:
v.push_back(std::to_string(e.ornamenticon)); v.push_back(std::to_string(e.ornamenticon));
v.push_back(std::to_string(e.ornamentidfile)); v.push_back(std::to_string(e.ornamentidfile));
v.push_back(std::to_string(e.ornament_hero_model)); v.push_back(std::to_string(e.ornament_hero_model));
v.push_back(std::to_string(e.guid));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -19,31 +19,34 @@
class BaseSpellBucketsRepository { class BaseSpellBucketsRepository {
public: public:
struct SpellBuckets { struct SpellBuckets {
uint64_t spellid; uint32_t spell_id;
std::string key_; std::string bucket_name;
std::string value; std::string bucket_value;
uint8_t bucket_comparison;
}; };
static std::string PrimaryKey() static std::string PrimaryKey()
{ {
return std::string("spellid"); return std::string("spell_id");
} }
static std::vector<std::string> Columns() static std::vector<std::string> Columns()
{ {
return { return {
"spellid", "spell_id",
"`key`", "bucket_name",
"value", "bucket_value",
"bucket_comparison",
}; };
} }
static std::vector<std::string> SelectColumns() static std::vector<std::string> SelectColumns()
{ {
return { return {
"spellid", "spell_id",
"`key`", "bucket_name",
"value", "bucket_value",
"bucket_comparison",
}; };
} }
@@ -84,9 +87,10 @@ public:
{ {
SpellBuckets e{}; SpellBuckets e{};
e.spellid = 0; e.spell_id = 0;
e.key_ = ""; e.bucket_name = "";
e.value = ""; e.bucket_value = "";
e.bucket_comparison = 0;
return e; return e;
} }
@@ -97,7 +101,7 @@ public:
) )
{ {
for (auto &spell_buckets : spell_bucketss) { for (auto &spell_buckets : spell_bucketss) {
if (spell_buckets.spellid == spell_buckets_id) { if (spell_buckets.spell_id == spell_buckets_id) {
return spell_buckets; return spell_buckets;
} }
} }
@@ -123,9 +127,10 @@ public:
if (results.RowCount() == 1) { if (results.RowCount() == 1) {
SpellBuckets e{}; SpellBuckets e{};
e.spellid = row[0] ? strtoull(row[0], nullptr, 10) : 0; e.spell_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.key_ = row[1] ? row[1] : ""; e.bucket_name = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : ""; e.bucket_value = row[2] ? row[2] : "";
e.bucket_comparison = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
return e; return e;
} }
@@ -159,9 +164,10 @@ public:
auto columns = Columns(); auto columns = Columns();
v.push_back(columns[0] + " = " + std::to_string(e.spellid)); v.push_back(columns[0] + " = " + std::to_string(e.spell_id));
v.push_back(columns[1] + " = '" + Strings::Escape(e.key_) + "'"); v.push_back(columns[1] + " = '" + Strings::Escape(e.bucket_name) + "'");
v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'"); v.push_back(columns[2] + " = '" + Strings::Escape(e.bucket_value) + "'");
v.push_back(columns[3] + " = " + std::to_string(e.bucket_comparison));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -169,7 +175,7 @@ public:
TableName(), TableName(),
Strings::Implode(", ", v), Strings::Implode(", ", v),
PrimaryKey(), PrimaryKey(),
e.spellid e.spell_id
) )
); );
@@ -183,9 +189,10 @@ public:
{ {
std::vector<std::string> v; std::vector<std::string> v;
v.push_back(std::to_string(e.spellid)); v.push_back(std::to_string(e.spell_id));
v.push_back("'" + Strings::Escape(e.key_) + "'"); v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'"); v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
v.push_back(std::to_string(e.bucket_comparison));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -196,7 +203,7 @@ public:
); );
if (results.Success()) { if (results.Success()) {
e.spellid = results.LastInsertedID(); e.spell_id = results.LastInsertedID();
return e; return e;
} }
@@ -215,9 +222,10 @@ public:
for (auto &e: entries) { for (auto &e: entries) {
std::vector<std::string> v; std::vector<std::string> v;
v.push_back(std::to_string(e.spellid)); v.push_back(std::to_string(e.spell_id));
v.push_back("'" + Strings::Escape(e.key_) + "'"); v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'"); v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
v.push_back(std::to_string(e.bucket_comparison));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -251,9 +259,10 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
SpellBuckets e{}; SpellBuckets e{};
e.spellid = row[0] ? strtoull(row[0], nullptr, 10) : 0; e.spell_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.key_ = row[1] ? row[1] : ""; e.bucket_name = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : ""; e.bucket_value = row[2] ? row[2] : "";
e.bucket_comparison = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -278,9 +287,10 @@ public:
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
SpellBuckets e{}; SpellBuckets e{};
e.spellid = row[0] ? strtoull(row[0], nullptr, 10) : 0; e.spell_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.key_ = row[1] ? row[1] : ""; e.bucket_name = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : ""; e.bucket_value = row[2] ? row[2] : "";
e.bucket_comparison = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
all_entries.push_back(e); all_entries.push_back(e);
} }
@@ -355,9 +365,10 @@ public:
{ {
std::vector<std::string> v; std::vector<std::string> v;
v.push_back(std::to_string(e.spellid)); v.push_back(std::to_string(e.spell_id));
v.push_back("'" + Strings::Escape(e.key_) + "'"); v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'"); v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
v.push_back(std::to_string(e.bucket_comparison));
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
@@ -380,9 +391,10 @@ public:
for (auto &e: entries) { for (auto &e: entries) {
std::vector<std::string> v; std::vector<std::string> v;
v.push_back(std::to_string(e.spellid)); v.push_back(std::to_string(e.spell_id));
v.push_back("'" + Strings::Escape(e.key_) + "'"); v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'"); v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
v.push_back(std::to_string(e.bucket_comparison));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
} }
@@ -85,7 +85,6 @@ public:
{.parent_command = "set", .sub_command = "endurance", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setendurance"}, {.parent_command = "set", .sub_command = "endurance", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setendurance"},
{.parent_command = "set", .sub_command = "endurance_full", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "endurance"}, {.parent_command = "set", .sub_command = "endurance_full", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "endurance"},
{.parent_command = "set", .sub_command = "exp", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setxp|setexp"}, {.parent_command = "set", .sub_command = "exp", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setxp|setexp"},
{.parent_command = "set", .sub_command = "faction", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setfaction"},
{.parent_command = "set", .sub_command = "flymode", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "flymode"}, {.parent_command = "set", .sub_command = "flymode", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "flymode"},
{.parent_command = "set", .sub_command = "freeze", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "freeze|unfreeze"}, {.parent_command = "set", .sub_command = "freeze", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "freeze|unfreeze"},
{.parent_command = "set", .sub_command = "gender", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "gender"}, {.parent_command = "set", .sub_command = "gender", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "gender"},
+2
View File
@@ -189,6 +189,8 @@ void RuleManager::ResetRules(bool reload) {
m_RuleRealValues[ Real__##rule_name ] = default_value; m_RuleRealValues[ Real__##rule_name ] = default_value;
#define RULE_BOOL(category_name, rule_name, default_value, notes) \ #define RULE_BOOL(category_name, rule_name, default_value, notes) \
m_RuleBoolValues[ Bool__##rule_name ] = default_value; m_RuleBoolValues[ Bool__##rule_name ] = default_value;
#define RULE_STRING(category_name, rule_name, default_value, notes) \
m_RuleStringValues[ String__##rule_name ] = default_value;
#include "ruletypes.h" #include "ruletypes.h"
// restore these rules to their pre-reset values // restore these rules to their pre-reset values
+8 -1
View File
@@ -178,6 +178,7 @@ RULE_BOOL(Character, NoSkillsOnHorse, false, "Enabling this will prevent Bind Wo
RULE_BOOL(Character, UseNoJunkFishing, false, "Disregards junk items when fishing") RULE_BOOL(Character, UseNoJunkFishing, false, "Disregards junk items when fishing")
RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted") RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted")
RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated") RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated")
RULE_INT(Character, DefaultGuildRank, 8, "Default guild rank when Character:DefaultGuild is a non-0 value, default is 8 (Recruit)")
RULE_BOOL(Character, ProcessFearedProximity, false, "Processes proximity checks when feared") RULE_BOOL(Character, ProcessFearedProximity, false, "Processes proximity checks when feared")
RULE_BOOL(Character, EnableCharacterEXPMods, false, "Enables character zone-based experience modifiers.") RULE_BOOL(Character, EnableCharacterEXPMods, false, "Enables character zone-based experience modifiers.")
RULE_BOOL(Character, PVPEnableGuardFactionAssist, true, "Enables faction based assisting against the aggresor in pvp.") RULE_BOOL(Character, PVPEnableGuardFactionAssist, true, "Enables faction based assisting against the aggresor in pvp.")
@@ -228,6 +229,7 @@ RULE_BOOL(Character, GroupInvitesRequireTarget, false, "Enable to require player
RULE_BOOL(Character, PlayerTradingLoreFeedback, true, "If enabled, during a player to player trade, if lore items exist, it will output which items.") RULE_BOOL(Character, PlayerTradingLoreFeedback, true, "If enabled, during a player to player trade, if lore items exist, it will output which items.")
RULE_INT(Character, MendAlwaysSucceedValue, 199, "Value at which mend will always succeed its skill check. Default: 199") RULE_INT(Character, MendAlwaysSucceedValue, 199, "Value at which mend will always succeed its skill check. Default: 199")
RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false") RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false")
RULE_INT(Character, BandolierSwapDelay, 0, "Bandolier swap delay in milliseconds, default is 0")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Mercs) RULE_CATEGORY(Mercs)
@@ -473,7 +475,6 @@ RULE_BOOL(Spells, OldRainTargets, false, "Use old incorrectly implemented maximu
RULE_REAL(Spells, CallOfTheHeroAggroClearDist, 250.0, "Distance at which CoTH will wipe aggro. To disable and always enable aggro wipe on any distance of CoTH, set to 0.") RULE_REAL(Spells, CallOfTheHeroAggroClearDist, 250.0, "Distance at which CoTH will wipe aggro. To disable and always enable aggro wipe on any distance of CoTH, set to 0.")
RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs") RULE_BOOL(Spells, NPCSpellPush, false, "Enable spell push on NPCs")
RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002") RULE_BOOL(Spells, July242002PetResists, true, "Enable Pets using PCs resist change from July 24 2002")
RULE_INT(Spells, AOEMaxTargets, 0, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.")
RULE_BOOL(Spells, CazicTouchTargetsPetOwner, true, "If True, causes Cazic Touch to swap targets from pet to pet owner if a pet is tanking.") RULE_BOOL(Spells, CazicTouchTargetsPetOwner, true, "If True, causes Cazic Touch to swap targets from pet to pet owner if a pet is tanking.")
RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts and dot removal on charm break to prevent faction wars.") RULE_BOOL(Spells, PreventFactionWarOnCharmBreak, false, "Enable spell interupts and dot removal on charm break to prevent faction wars.")
RULE_BOOL(Spells, AllowDoubleInvis, true, "Allows you to cast invisibility spells on a player that is already invisible, live like behavior.") RULE_BOOL(Spells, AllowDoubleInvis, true, "Allows you to cast invisibility spells on a player that is already invisible, live like behavior.")
@@ -513,6 +514,9 @@ RULE_BOOL(Spells, UseClassicSpellFocus, false, "Enabling will tell the server to
RULE_BOOL(Spells, ManaTapsOnAnyClass, false, "Enabling this will allow you to cast mana taps on any class, this will bypass ManaTapsRequireNPCMana rule.") RULE_BOOL(Spells, ManaTapsOnAnyClass, false, "Enabling this will allow you to cast mana taps on any class, this will bypass ManaTapsRequireNPCMana rule.")
RULE_INT(Spells, HealAmountMessageFilterThreshold, 100, "Lifetaps below this threshold will not have a message sent to the client (Heal will still process) 0 to Disable.") RULE_INT(Spells, HealAmountMessageFilterThreshold, 100, "Lifetaps below this threshold will not have a message sent to the client (Heal will still process) 0 to Disable.")
RULE_BOOL(Spells, SnareOverridesSpeedBonuses, false, "Enabling will allow snares to override any speed bonuses the entity may have. Default: False") RULE_BOOL(Spells, SnareOverridesSpeedBonuses, false, "Enabling will allow snares to override any speed bonuses the entity may have. Default: False")
RULE_INT(Spells, TargetedAOEMaxTargets, 4, "Max number of targets a Targeted AOE spell can cast on. Set to 0 for no limit.")
RULE_INT(Spells, PointBlankAOEMaxTargets, 0, "Max number of targets a Point-Blank AOE spell can cast on. Set to 0 for no limit.")
RULE_INT(Spells, DefaultAOEMaxTargets, 4, "Max number of targets that an AOE spell which does not meet other descriptions can cast on. Set to 0 for no limit.")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Combat) RULE_CATEGORY(Combat)
@@ -714,6 +718,7 @@ RULE_BOOL(TaskSystem, ExpRewardsIgnoreLevelBasedEXPMods, false, "Rewarding Level
RULE_INT(TaskSystem, SharedTasksWorldProcessRate, 6000, "Timer interval (milliseconds) that shared tasks are processed in world") RULE_INT(TaskSystem, SharedTasksWorldProcessRate, 6000, "Timer interval (milliseconds) that shared tasks are processed in world")
RULE_INT(TaskSystem, SharedTasksTerminateTimerMS, 120000, "Delay (milliseconds) until a shared task is terminated if requirements are no longer met after member removal (default: 2 minutes)") RULE_INT(TaskSystem, SharedTasksTerminateTimerMS, 120000, "Delay (milliseconds) until a shared task is terminated if requirements are no longer met after member removal (default: 2 minutes)")
RULE_BOOL(TaskSystem, UpdateOneElementPerTask, true, "If true (live-like) task updates only increment the first matching activity. If false all matching elements will be incremented.") RULE_BOOL(TaskSystem, UpdateOneElementPerTask, true, "If true (live-like) task updates only increment the first matching activity. If false all matching elements will be incremented.")
RULE_INT(TaskSystem, MaxUpdateMessages, 50, "Maximum update messages for non-GiveCash activity types in IncrementDoneCount")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Range) RULE_CATEGORY(Range)
@@ -725,6 +730,7 @@ RULE_INT(Range, SpellParticles, 135, "The packet range in which spell particles
RULE_INT(Range, DamageMessages, 50, "The packet range in which damage messages are sent (non-crit)") RULE_INT(Range, DamageMessages, 50, "The packet range in which damage messages are sent (non-crit)")
RULE_INT(Range, SpellMessages, 75, "The packet range in which spell damage messages are sent") RULE_INT(Range, SpellMessages, 75, "The packet range in which spell damage messages are sent")
RULE_INT(Range, SongMessages, 75, "The packet range in which song messages are sent") RULE_INT(Range, SongMessages, 75, "The packet range in which song messages are sent")
RULE_INT(Range, StunMessages, 75, "The packet range in which stun messages are sent")
RULE_INT(Range, ClientPositionUpdates, 300, "Distance in which the own changed position is communicated to other clients") RULE_INT(Range, ClientPositionUpdates, 300, "Distance in which the own changed position is communicated to other clients")
RULE_INT(Range, CriticalDamage, 80, "The packet range in which critical hit messages are sent") RULE_INT(Range, CriticalDamage, 80, "The packet range in which critical hit messages are sent")
RULE_INT(Range, MobCloseScanDistance, 600, "Close scan distance") RULE_INT(Range, MobCloseScanDistance, 600, "Close scan distance")
@@ -907,6 +913,7 @@ RULE_BOOL(Inventory, AllowAnyWeaponTransformation, false, "Weapons can use any w
RULE_BOOL(Inventory, TransformSummonedBags, false, "Transforms summoned bags into disenchanted ones instead of deleting") RULE_BOOL(Inventory, TransformSummonedBags, false, "Transforms summoned bags into disenchanted ones instead of deleting")
RULE_BOOL(Inventory, AllowMultipleOfSameAugment, false, "Allows multiple of the same augment to be placed in an item via #augmentitem or MQ2, set to true to allow") RULE_BOOL(Inventory, AllowMultipleOfSameAugment, false, "Allows multiple of the same augment to be placed in an item via #augmentitem or MQ2, set to true to allow")
RULE_INT(Inventory, AlternateAugmentationSealer, 53, "Allows RoF+ clients to augment items from a special container type") RULE_INT(Inventory, AlternateAugmentationSealer, 53, "Allows RoF+ clients to augment items from a special container type")
RULE_BOOL(Inventory, LazyLoadBank, true, "Don't load bank during zoning, only when in proximinity to a banker. May increase zone speed and stability")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Client) RULE_CATEGORY(Client)
+114 -83
View File
@@ -46,6 +46,8 @@
#include "repositories/character_item_recast_repository.h" #include "repositories/character_item_recast_repository.h"
#include "repositories/character_corpses_repository.h" #include "repositories/character_corpses_repository.h"
#include "repositories/skill_caps_repository.h" #include "repositories/skill_caps_repository.h"
#include "repositories/inventory_repository.h"
#include "repositories/books_repository.h"
namespace ItemField namespace ItemField
{ {
@@ -300,15 +302,15 @@ bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const EQ::ItemInstance*
// Update/Insert item // Update/Insert item
const std::string query = StringFormat("REPLACE INTO inventory " const std::string query = StringFormat("REPLACE INTO inventory "
"(charid, slotid, itemid, charges, instnodrop, custom_data, color, " "(charid, slotid, itemid, charges, instnodrop, custom_data, color, "
"augslot1, augslot2, augslot3, augslot4, augslot5, augslot6, ornamenticon, ornamentidfile, ornament_hero_model) " "augslot1, augslot2, augslot3, augslot4, augslot5, augslot6, ornamenticon, ornamentidfile, ornament_hero_model, guid) "
"VALUES( %lu, %lu, %lu, %lu, %lu, '%s', %lu, " "VALUES( %lu, %lu, %lu, %lu, %lu, '%s', %lu, "
"%lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu)", "%lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu)",
static_cast<unsigned long>(char_id), static_cast<unsigned long>(slot_id), static_cast<unsigned long>(inst->GetItem()->ID), static_cast<unsigned long>(char_id), static_cast<unsigned long>(slot_id), static_cast<unsigned long>(inst->GetItem()->ID),
static_cast<unsigned long>(charges), static_cast<unsigned long>(inst->IsAttuned() ? 1 : 0), static_cast<unsigned long>(charges), static_cast<unsigned long>(inst->IsAttuned() ? 1 : 0),
inst->GetCustomDataString().c_str(), static_cast<unsigned long>(inst->GetColor()), inst->GetCustomDataString().c_str(), static_cast<unsigned long>(inst->GetColor()),
static_cast<unsigned long>(augslot[0]), static_cast<unsigned long>(augslot[1]), static_cast<unsigned long>(augslot[2]), static_cast<unsigned long>(augslot[0]), static_cast<unsigned long>(augslot[1]), static_cast<unsigned long>(augslot[2]),
static_cast<unsigned long>(augslot[3]), static_cast<unsigned long>(augslot[4]), static_cast<unsigned long>(augslot[5]), static_cast<unsigned long>(inst->GetOrnamentationIcon()), static_cast<unsigned long>(augslot[3]), static_cast<unsigned long>(augslot[4]), static_cast<unsigned long>(augslot[5]), static_cast<unsigned long>(inst->GetOrnamentationIcon()),
static_cast<unsigned long>(inst->GetOrnamentationIDFile()), static_cast<unsigned long>(inst->GetOrnamentHeroModel())); static_cast<unsigned long>(inst->GetOrnamentationIDFile()), static_cast<unsigned long>(inst->GetOrnamentHeroModel()), inst->GetSerialNumber());
const auto results = QueryDatabase(query); const auto results = QueryDatabase(query);
// Save bag contents, if slot supports bag contents // Save bag contents, if slot supports bag contents
@@ -651,48 +653,67 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
return false; return false;
// Retrieve character inventory // Retrieve character inventory
const std::string query = auto results = InventoryRepository::GetWhere(*this, fmt::format("`charid` = '{}' ORDER BY `slotid`;", char_id));
StringFormat("SELECT slotid, itemid, charges, color, augslot1, augslot2, augslot3, augslot4, augslot5, " if (results.empty()) {
"augslot6, instnodrop, custom_data, ornamenticon, ornamentidfile, ornament_hero_model FROM " LogError("Error loading inventory for char_id {} from the database.", char_id);
"inventory WHERE charid = %i ORDER BY slotid",
char_id);
auto results = QueryDatabase(query);
if (!results.Success()) {
LogError("If you got an error related to the 'instnodrop' field, run the "
"following SQL Queries:\nalter table inventory add instnodrop "
"tinyint(1) unsigned default 0 not null;\n");
return false; return false;
} }
const auto timestamps = GetItemRecastTimestamps(char_id); for (auto const &row: results) {
if (row.guid != 0) {
EQ::ItemInstance::AddGUIDToMap(row.guid);
}
}
const auto timestamps = GetItemRecastTimestamps(char_id);
auto cv_conflict = false; auto cv_conflict = false;
const auto pmask = inv->GetLookup()->PossessionsBitmask; const auto pmask = inv->GetLookup()->PossessionsBitmask;
const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank; const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank;
for (auto& row = results.begin(); row != results.end(); ++row) { std::vector<InventoryRepository::Inventory> queue{};
int16 slot_id = Strings::ToInt(row[0]); for (auto &row: results) {
const int16 slot_id = row.slotid;
const uint32 item_id = row.itemid;
const uint16 charges = row.charges;
const uint32 color = row.color;
const bool instnodrop = row.instnodrop;
const uint32 ornament_icon = row.ornamenticon;
const uint32 ornament_idfile = row.ornamentidfile;
const uint32 ornament_hero_model = row.ornament_hero_model;
if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) { // Titanium thru UF check uint32 aug[EQ::invaug::SOCKET_COUNT];
aug[0] = row.augslot1;
aug[1] = row.augslot2;
aug[2] = row.augslot3;
aug[3] = row.augslot4;
aug[4] = row.augslot5;
aug[5] = row.augslot6;
if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) {
// Titanium thru UF check
if (((static_cast<uint64>(1) << slot_id) & pmask) == 0) { if (((static_cast<uint64>(1) << slot_id) & pmask) == 0) {
cv_conflict = true; cv_conflict = true;
continue; continue;
} }
} }
else if (slot_id <= EQ::invbag::GENERAL_BAGS_END && slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN) { // Titanium thru UF check else if (slot_id <= EQ::invbag::GENERAL_BAGS_END && slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN) {
const auto parent_slot = EQ::invslot::GENERAL_BEGIN + ((slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); // Titanium thru UF check
const auto parent_slot = EQ::invslot::GENERAL_BEGIN + (
(slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT);
if (((static_cast<uint64>(1) << parent_slot) & pmask) == 0) { if (((static_cast<uint64>(1) << parent_slot) & pmask) == 0) {
cv_conflict = true; cv_conflict = true;
continue; continue;
} }
} }
else if (slot_id <= EQ::invslot::BANK_END && slot_id >= EQ::invslot::BANK_BEGIN) { // Titanium check else if (slot_id <= EQ::invslot::BANK_END && slot_id >= EQ::invslot::BANK_BEGIN) {
// Titanium check
if ((slot_id - EQ::invslot::BANK_BEGIN) >= bank_size) { if ((slot_id - EQ::invslot::BANK_BEGIN) >= bank_size) {
cv_conflict = true; cv_conflict = true;
continue; continue;
} }
} }
else if (slot_id <= EQ::invbag::BANK_BAGS_END && slot_id >= EQ::invbag::BANK_BAGS_BEGIN) { // Titanium check else if (slot_id <= EQ::invbag::BANK_BAGS_END && slot_id >= EQ::invbag::BANK_BAGS_BEGIN) {
// Titanium check
const auto parent_index = ((slot_id - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT); const auto parent_index = ((slot_id - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT);
if (parent_index >= bank_size) { if (parent_index >= bank_size) {
cv_conflict = true; cv_conflict = true;
@@ -700,64 +721,55 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
} }
} }
uint32 item_id = Strings::ToUnsignedInt(row[1]); auto *item = GetItem(item_id);
const uint16 charges = Strings::ToUnsignedInt(row[2]);
const uint32 color = Strings::ToUnsignedInt(row[3]);
uint32 aug[EQ::invaug::SOCKET_COUNT];
aug[0] = Strings::ToUnsignedInt(row[4]);
aug[1] = Strings::ToUnsignedInt(row[5]);
aug[2] = Strings::ToUnsignedInt(row[6]);
aug[3] = Strings::ToUnsignedInt(row[7]);
aug[4] = Strings::ToUnsignedInt(row[8]);
aug[5] = Strings::ToUnsignedInt(row[9]);
const bool instnodrop = (row[10] && static_cast<uint16>(Strings::ToUnsignedInt(row[10])));
const uint32 ornament_icon = Strings::ToUnsignedInt(row[12]);
const uint32 ornament_idfile = Strings::ToUnsignedInt(row[13]);
uint32 ornament_hero_model = Strings::ToUnsignedInt(row[14]);
const EQ::ItemData *item = GetItem(item_id);
if (!item) { if (!item) {
LogError("Warning: charid [{}] has an invalid item_id [{}] in inventory slot [{}]", char_id, item_id, LogError(
slot_id); "Warning: charid [{}] has an invalid item_id [{}] in inventory slot [{}]",
char_id,
item_id,
slot_id
);
continue; continue;
} }
EQ::ItemInstance *inst = CreateBaseItem(item, charges); auto *inst = CreateBaseItem(item, charges);
if (!inst) {
if (inst == nullptr)
continue; continue;
}
if (row[11]) { if (!row.custom_data.empty()) {
std::string data_str(row[11]); inst->SetCustomDataString(row.custom_data);
inst->SetCustomDataString(data_str);
} }
inst->SetOrnamentIcon(ornament_icon); inst->SetOrnamentIcon(ornament_icon);
inst->SetOrnamentationIDFile(ornament_idfile); inst->SetOrnamentationIDFile(ornament_idfile);
inst->SetOrnamentHeroModel(item->HerosForgeModel); inst->SetOrnamentHeroModel(item->HerosForgeModel);
if (instnodrop || (inst->GetItem()->Attuneable && slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <= EQ::invslot::EQUIPMENT_END)) if (instnodrop || (inst->GetItem()->Attuneable && slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <=
EQ::invslot::EQUIPMENT_END)) {
inst->SetAttuned(true); inst->SetAttuned(true);
}
if (color > 0) if (color > 0) {
inst->SetColor(color); inst->SetColor(color);
}
if (charges == 0x7FFF) if (charges == 0x7FFF) {
inst->SetCharges(-1); inst->SetCharges(-1);
else if (charges == 0 && inst->IsStackable()) // Stackable items need a minimum charge of 1 remain moveable. }
else if (charges == 0 && inst->IsStackable()) {
// Stackable items need a minimum charge of 1 remain moveable.
inst->SetCharges(1); inst->SetCharges(1);
else }
else {
inst->SetCharges(charges); inst->SetCharges(charges);
}
if (item->RecastDelay) { if (item->RecastDelay) {
if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->RecastType)) { if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->RecastType)) {
inst->SetRecastTimestamp(timestamps.at(item->RecastType)); inst->SetRecastTimestamp(timestamps.at(item->RecastType));
} else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->ID)) { }
else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->ID)) {
inst->SetRecastTimestamp(timestamps.at(item->ID)); inst->SetRecastTimestamp(timestamps.at(item->ID));
} }
else { else {
@@ -767,35 +779,50 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
if (item->IsClassCommon()) { if (item->IsClassCommon()) {
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
if (aug[i]) if (aug[i]) {
inst->PutAugment(this, i, aug[i]); inst->PutAugment(this, i, aug[i]);
} }
} }
}
int16 put_slot_id; int16 put_slot_id;
if (slot_id >= 8000 && slot_id <= 8999) { if (slot_id >= 8000 && slot_id <= 8999) {
put_slot_id = inv->PushCursor(*inst); put_slot_id = inv->PushCursor(*inst);
} else if (slot_id >= 3111 && slot_id <= 3179) { }
else if (slot_id >= 3111 && slot_id <= 3179) {
// Admins: please report any occurrences of this error // Admins: please report any occurrences of this error
LogError("Warning: Defunct location for item in inventory: charid={}, item_id={}, slot_id={} .. pushing to cursor...", LogError(
char_id, item_id, slot_id); "Warning: Defunct location for item in inventory: charid={}, item_id={}, slot_id={} .. pushing to cursor...",
char_id,
item_id,
slot_id
);
put_slot_id = inv->PushCursor(*inst); put_slot_id = inv->PushCursor(*inst);
} else { }
else {
put_slot_id = inv->PutItem(slot_id, *inst); put_slot_id = inv->PutItem(slot_id, *inst);
} }
row.guid = inst->GetSerialNumber();
queue.push_back(row);
safe_delete(inst); safe_delete(inst);
// Save ptr to item in inventory // Save ptr to item in inventory
if (put_slot_id == INVALID_INDEX) { if (put_slot_id == INVALID_INDEX) {
LogError("Warning: Invalid slot_id for item in inventory: charid=[{}], item_id=[{}], slot_id=[{}]", LogError(
char_id, item_id, slot_id); "Warning: Invalid slot_id for item in inventory: charid=[{}], item_id=[{}], slot_id=[{}]",
char_id,
item_id,
slot_id
);
} }
} }
if (cv_conflict) { if (cv_conflict) {
const std::string& char_name = GetCharName(char_id); const std::string &char_name = GetCharName(char_id);
LogError("ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])", LogError(
"ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])",
char_name, char_name,
char_id, char_id,
EQ::versions::MobVersionName(inv->InventoryVersion()), EQ::versions::MobVersionName(inv->InventoryVersion()),
@@ -803,6 +830,12 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
); );
} }
if (!queue.empty()) {
InventoryRepository::ReplaceMany(*this, queue);
}
EQ::ItemInstance::ClearGUIDMap();
// Retrieve shared inventory // Retrieve shared inventory
return GetSharedBank(char_id, inv, true); return GetSharedBank(char_id, inv, true);
} }
@@ -1359,30 +1392,28 @@ const EQ::ItemData* SharedDatabase::IterateItems(uint32* id) const
return nullptr; return nullptr;
} }
std::string SharedDatabase::GetBook(const char *txtfile, int16 *language) Book_Struct SharedDatabase::GetBook(const std::string& text_file)
{ {
char txtfile2[20]; const auto& l = BooksRepository::GetWhere(
std::string txtout; *this,
strcpy(txtfile2, txtfile); fmt::format(
"`name` = '{}'",
Strings::Escape(text_file)
)
);
const std::string query = StringFormat("SELECT txtfile, language FROM books WHERE name = '%s'", txtfile2); Book_Struct b;
auto results = QueryDatabase(query);
if (!results.Success()) { if (l.empty()) {
txtout.assign(" ",1); return b;
return txtout;
} }
if (results.RowCount() == 0) { const auto& e = l.front();
LogError("No book to send, ({})", txtfile);
txtout.assign(" ",1);
return txtout;
}
auto& row = results.begin(); b.language = e.language;
txtout.assign(row[0],strlen(row[0])); b.text = e.txtfile;
*language = static_cast<int16>(Strings::ToInt(row[1]));
return txtout; return b;
} }
// Create appropriate EQ::ItemInstance class // Create appropriate EQ::ItemInstance class
+8 -3
View File
@@ -41,8 +41,7 @@ struct NPCFactionList;
struct FactionAssociations; struct FactionAssociations;
namespace EQ namespace EQ {
{
struct ItemData; struct ItemData;
class ItemInstance; class ItemInstance;
@@ -50,6 +49,12 @@ namespace EQ
class MemoryMappedFile; class MemoryMappedFile;
} }
struct Book_Struct
{
uint8 language;
std::string text;
};
/* /*
This object is inherited by world and zone's DB object, This object is inherited by world and zone's DB object,
and is mainly here to facilitate shared memory, and other and is mainly here to facilitate shared memory, and other
@@ -114,7 +119,7 @@ public:
int admin int admin
); );
std::string GetBook(const char *txtfile, int16 *language); Book_Struct GetBook(const std::string& text_file);
/** /**
* items * items
-1
View File
@@ -249,7 +249,6 @@ const std::vector<EQ::skills::SkillType>& EQ::skills::GetExtraDamageSkills()
EQ::skills::SkillFlyingKick, EQ::skills::SkillFlyingKick,
EQ::skills::SkillKick, EQ::skills::SkillKick,
EQ::skills::SkillRoundKick, EQ::skills::SkillRoundKick,
EQ::skills::SkillRoundKick,
EQ::skills::SkillTigerClaw, EQ::skills::SkillTigerClaw,
EQ::skills::SkillFrenzy EQ::skills::SkillFrenzy
}; };
+3 -3
View File
@@ -25,7 +25,7 @@
// Build variables // Build variables
// these get injected during the build pipeline // these get injected during the build pipeline
#define CURRENT_VERSION "22.54.0-dev" // always append -dev to the current version for custom-builds #define CURRENT_VERSION "22.56.3-dev" // always append -dev to the current version for custom-builds
#define LOGIN_VERSION "0.8.0" #define LOGIN_VERSION "0.8.0"
#define COMPILE_DATE __DATE__ #define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__ #define COMPILE_TIME __TIME__
@@ -42,8 +42,8 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/ */
#define CURRENT_BINARY_DATABASE_VERSION 9281 #define CURRENT_BINARY_DATABASE_VERSION 9284
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9044 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045
#endif #endif
+17
View File
@@ -224,6 +224,23 @@ void Client::Handle_Login(const char *data, unsigned int size)
if (server.db->GetLoginDataFromAccountInfo(user, db_loginserver, db_account_password_hash, db_account_id)) { if (server.db->GetLoginDataFromAccountInfo(user, db_loginserver, db_account_password_hash, db_account_id)) {
result = VerifyLoginHash(user, db_loginserver, cred, db_account_password_hash); result = VerifyLoginHash(user, db_loginserver, cred, db_account_password_hash);
#ifdef LSPX
// if user updated their password on the login server, update it here by validating their credentials with the login server
if (!result && db_loginserver == "eqemu") {
uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials(user, cred);
if (account_id > 0) {
auto encryption_mode = server.options.GetEncryptionMode();
server.db->UpdateLoginserverAccountPasswordHash(
user,
db_loginserver,
eqcrypt_hash(user, cred, encryption_mode)
);
LogInfo("Updating eqemu account [{}] password hash", account_id);
result = true;
}
}
#endif
LogDebug("Success [{0}]", (result ? "true" : "false")); LogDebug("Success [{0}]", (result ? "true" : "false"));
} }
else { else {
+7
View File
@@ -138,6 +138,13 @@ public:
*/ */
unsigned int GetPlaySequence() const { return m_play_sequence_id; } unsigned int GetPlaySequence() const { return m_play_sequence_id; }
/**
* Gets the client version
*
* @return
*/
LSClientVersion GetClientVersion() const { return m_client_version; }
/** /**
* Gets the connection for this client * Gets the connection for this client
* *
+40
View File
@@ -88,6 +88,46 @@ ClientManager::ClientManager()
clients.push_back(c); clients.push_back(c);
} }
); );
int larion_port = server.config.GetVariableInt("client_configuration", "larion_port", 15900);
EQStreamManagerInterfaceOptions larion_opts(larion_port, false, false);
larion_stream = new EQ::Net::EQStreamManager(larion_opts);
larion_ops = new RegularOpcodeManager;
opcodes_path = fmt::format(
"{}/{}",
path.GetServerPath(),
server.config.GetVariableString(
"client_configuration",
"larion_opcodes",
"login_opcodes.conf"
)
);
if (!larion_ops->LoadOpcodes(opcodes_path.c_str())) {
LogError(
"ClientManager fatal error: couldn't load opcodes for Larion file [{0}]",
server.config.GetVariableString("client_configuration", "larion_opcodes", "login_opcodes.conf")
);
run_server = false;
}
larion_stream->OnNewConnection(
[this](std::shared_ptr<EQ::Net::EQStream> stream) {
LogInfo(
"New Larion client connection from [{0}:{1}]",
long2ip(stream->GetRemoteIP()),
stream->GetRemotePort()
);
stream->SetOpcodeManager(&larion_ops);
Client* c = new Client(stream, cv_larion);
clients.push_back(c);
}
);
} }
ClientManager::~ClientManager() ClientManager::~ClientManager()
+2
View File
@@ -55,6 +55,8 @@ private:
EQ::Net::EQStreamManager *titanium_stream; EQ::Net::EQStreamManager *titanium_stream;
OpcodeManager *sod_ops; OpcodeManager *sod_ops;
EQ::Net::EQStreamManager *sod_stream; EQ::Net::EQStreamManager *sod_stream;
OpcodeManager *larion_ops;
EQ::Net::EQStreamManager* larion_stream;
}; };
#endif #endif
+2 -1
View File
@@ -83,7 +83,8 @@ struct PlayEverquestResponse_Struct {
enum LSClientVersion { enum LSClientVersion {
cv_titanium, cv_titanium,
cv_sod cv_sod,
cv_larion
}; };
enum LSClientStatus { enum LSClientStatus {
@@ -0,0 +1,14 @@
#EQEmu Public Login Server OPCodes
OP_SessionReady=0x0001
OP_Login=0x0002
OP_ServerListRequest=0x0004
OP_PlayEverquestRequest=0x000d
OP_PlayEverquestResponse=0x0022
OP_ChatMessage=0x0017
OP_LoginAccepted=0x0018
OP_ServerListResponse=0x0019
OP_Poll=0x0029
OP_EnterChat=0x000f
OP_PollResponse=0x0011
OP_SystemFingerprint=0x0016
OP_ExpansionList=0x0030
+1 -1
View File
@@ -137,7 +137,7 @@ std::unique_ptr<EQApplicationPacket> ServerManager::CreateServerListPacket(Clien
use_local_ip ? "Local" : "Remote" use_local_ip ? "Local" : "Remote"
); );
world_server->SerializeForClientServerList(buf, use_local_ip); world_server->SerializeForClientServerList(buf, use_local_ip, client->GetClientVersion());
} }
return std::make_unique<EQApplicationPacket>(OP_ServerListResponse, buf); return std::make_unique<EQApplicationPacket>(OP_ServerListResponse, buf);
+14 -2
View File
@@ -977,7 +977,7 @@ bool WorldServer::ValidateWorldServerAdminLogin(
return false; return false;
} }
void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_local_ip) const void WorldServer::SerializeForClientServerList(SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const
{ {
// see LoginClientServerData_Struct // see LoginClientServerData_Struct
if (use_local_ip) { if (use_local_ip) {
@@ -987,6 +987,10 @@ void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_lo
out.WriteString(GetRemoteIP()); out.WriteString(GetRemoteIP());
} }
if (version == cv_larion) {
out.WriteUInt32(9000);
}
switch (GetServerListID()) { switch (GetServerListID()) {
case LS::ServerType::Legends: case LS::ServerType::Legends:
out.WriteInt32(LS::ServerTypeFlags::Legends); out.WriteInt32(LS::ServerTypeFlags::Legends);
@@ -998,8 +1002,16 @@ void WorldServer::SerializeForClientServerList(SerializeBuffer &out, bool use_lo
out.WriteInt32(LS::ServerTypeFlags::Standard); out.WriteInt32(LS::ServerTypeFlags::Standard);
break; break;
} }
if (version == cv_larion) {
auto server_id = GetServerId();
//if this is 0, the client will not show the server in the list
out.WriteUInt32(1);
out.WriteUInt32(server_id);
}
else {
out.WriteUInt32(GetServerId()); out.WriteUInt32(GetServerId());
}
out.WriteString(GetServerLongName()); out.WriteString(GetServerLongName());
out.WriteString("us"); // country code out.WriteString("us"); // country code
out.WriteString("en"); // language code out.WriteString("en"); // language code
+2 -1
View File
@@ -7,6 +7,7 @@
#include "../common/packet_dump.h" #include "../common/packet_dump.h"
#include "database.h" #include "database.h"
#include "../common/event/timer.h" #include "../common/event/timer.h"
#include "login_types.h"
#include <string> #include <string>
#include <memory> #include <memory>
@@ -149,7 +150,7 @@ public:
bool HandleNewLoginserverRegisteredOnly(Database::DbWorldRegistration &world_registration); bool HandleNewLoginserverRegisteredOnly(Database::DbWorldRegistration &world_registration);
bool HandleNewLoginserverInfoUnregisteredAllowed(Database::DbWorldRegistration &world_registration); bool HandleNewLoginserverInfoUnregisteredAllowed(Database::DbWorldRegistration &world_registration);
void SerializeForClientServerList(class SerializeBuffer& out, bool use_local_ip) const; void SerializeForClientServerList(class SerializeBuffer& out, bool use_local_ip, LSClientVersion version) const;
private: private:
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "eqemu-server", "name": "eqemu-server",
"version": "22.54.0", "version": "22.56.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/EQEmu/Server.git" "url": "https://github.com/EQEmu/Server.git"
+1
View File
@@ -56,6 +56,7 @@ SET(world_headers
login_server.h login_server.h
login_server_list.h login_server_list.h
queryserv.h queryserv.h
race_combos.h
shared_task_manager.h shared_task_manager.h
shared_task_world_messaging.h shared_task_world_messaging.h
sof_char_create_data.h sof_char_create_data.h
+1
View File
@@ -287,6 +287,7 @@ void Adventure::Finished(AdventureWinStatus ws)
auto character_id = database.GetCharacterID(*iter); auto character_id = database.GetCharacterID(*iter);
if (character_id == 0) { if (character_id == 0) {
++iter;
continue; continue;
} }
+354 -269
View File
@@ -47,6 +47,7 @@
#include "clientlist.h" #include "clientlist.h"
#include "wguild_mgr.h" #include "wguild_mgr.h"
#include "sof_char_create_data.h" #include "sof_char_create_data.h"
#include "race_combos.h"
#include "../common/zone_store.h" #include "../common/zone_store.h"
#include "../common/repositories/account_repository.h" #include "../common/repositories/account_repository.h"
#include "../common/repositories/player_event_logs_repository.h" #include "../common/repositories/player_event_logs_repository.h"
@@ -85,8 +86,8 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
std::vector<RaceClassAllocation> character_create_allocations; std::vector<CharCreatePointAllocation> character_create_allocations;
std::vector<RaceClassCombos> character_create_race_class_combos; std::vector<CharCreateCombination> character_create_race_class_combos;
extern ZSList zoneserver_list; extern ZSList zoneserver_list;
extern LoginServerList loginserverlist; extern LoginServerList loginserverlist;
@@ -1642,6 +1643,348 @@ void Client::SendApproveWorld()
safe_delete(outapp); safe_delete(outapp);
} }
// returns true if the request is ok, false if there's an error
bool CheckCharCreateInfoSoF(CharCreate_Struct* cc)
{
if (!cc)
return false;
LogInfo("Validating char creation info");
CharCreateCombination class_combo;
bool found = false;
int combos = character_create_race_class_combos.size();
for (int i = 0; i < combos; ++i) {
if (character_create_race_class_combos[i].Class == cc->class_ &&
character_create_race_class_combos[i].Race == cc->race &&
character_create_race_class_combos[i].Deity == cc->deity &&
character_create_race_class_combos[i].Zone == cc->start_zone) {
class_combo = character_create_race_class_combos[i];
found = true;
break;
}
}
if (!found) {
LogInfo("Could not find class/race/deity/start_zone combination");
return false;
}
uint32 allocs = character_create_allocations.size();
CharCreatePointAllocation allocation = { 0 };
found = false;
for (int i = 0; i < allocs; ++i) {
if (character_create_allocations[i].Index == class_combo.AllocationIndex) {
allocation = character_create_allocations[i];
found = true;
break;
}
}
if (!found) {
LogInfo("Could not find starting stats for selected character combo, cannot verify stats");
return false;
}
uint32 max_stats = allocation.DefaultPointAllocation[0] +
allocation.DefaultPointAllocation[1] +
allocation.DefaultPointAllocation[2] +
allocation.DefaultPointAllocation[3] +
allocation.DefaultPointAllocation[4] +
allocation.DefaultPointAllocation[5] +
allocation.DefaultPointAllocation[6];
if (cc->STR > allocation.BaseStats[0] + max_stats || cc->STR < allocation.BaseStats[0]) {
LogInfo("Strength out of range");
return false;
}
if (cc->DEX > allocation.BaseStats[1] + max_stats || cc->DEX < allocation.BaseStats[1]) {
LogInfo("Dexterity out of range");
return false;
}
if (cc->AGI > allocation.BaseStats[2] + max_stats || cc->AGI < allocation.BaseStats[2]) {
LogInfo("Agility out of range");
return false;
}
if (cc->STA > allocation.BaseStats[3] + max_stats || cc->STA < allocation.BaseStats[3]) {
LogInfo("Stamina out of range");
return false;
}
if (cc->INT > allocation.BaseStats[4] + max_stats || cc->INT < allocation.BaseStats[4]) {
LogInfo("Intelligence out of range");
return false;
}
if (cc->WIS > allocation.BaseStats[5] + max_stats || cc->WIS < allocation.BaseStats[5]) {
LogInfo("Wisdom out of range");
return false;
}
if (cc->CHA > allocation.BaseStats[6] + max_stats || cc->CHA < allocation.BaseStats[6]) {
LogInfo("Charisma out of range");
return false;
}
uint32 current_stats = 0;
current_stats += cc->STR - allocation.BaseStats[0];
current_stats += cc->DEX - allocation.BaseStats[1];
current_stats += cc->AGI - allocation.BaseStats[2];
current_stats += cc->STA - allocation.BaseStats[3];
current_stats += cc->INT - allocation.BaseStats[4];
current_stats += cc->WIS - allocation.BaseStats[5];
current_stats += cc->CHA - allocation.BaseStats[6];
if (current_stats > max_stats) {
LogInfo("Current Stats > Maximum Stats");
return false;
}
return true;
}
bool CheckCharCreateInfoTitanium(CharCreate_Struct* cc)
{
uint32 bSTR, bSTA, bAGI, bDEX, bWIS, bINT, bCHA, bTOTAL, cTOTAL, stat_points; //these are all uint32 in CharCreate_Struct, so we'll make them uint32 here to make the compiler shut up
int classtemp, racetemp;
int Charerrors = 0;
// if this is increased you'll have to add a column to the classrace
// table below
#define _TABLE_RACES 16
static const int BaseRace[_TABLE_RACES][7] =
{ /* STR STA AGI DEX WIS INT CHR */
{ /*Human*/ 75, 75, 75, 75, 75, 75, 75},
{ /*Barbarian*/ 103, 95, 82, 70, 70, 60, 55},
{ /*Erudite*/ 60, 70, 70, 70, 83, 107, 70},
{ /*Wood Elf*/ 65, 65, 95, 80, 80, 75, 75},
{ /*High Elf*/ 55, 65, 85, 70, 95, 92, 80},
{ /*Dark Elf*/ 60, 65, 90, 75, 83, 99, 60},
{ /*Half Elf*/ 70, 70, 90, 85, 60, 75, 75},
{ /*Dwarf*/ 90, 90, 70, 90, 83, 60, 45},
{ /*Troll*/ 108, 109, 83, 75, 60, 52, 40},
{ /*Ogre*/ 130, 122, 70, 70, 67, 60, 37},
{ /*Halfling*/ 70, 75, 95, 90, 80, 67, 50},
{ /*Gnome*/ 60, 70, 85, 85, 67, 98, 60},
{ /*Iksar*/ 70, 70, 90, 85, 80, 75, 55},
{ /*Vah Shir*/ 90, 75, 90, 70, 70, 65, 65},
{ /*Froglok*/ 70, 80, 100, 100, 75, 75, 50},
{ /*Drakkin*/ 70, 80, 85, 75, 80, 85, 75}
};
static const int BaseClass[Class::PLAYER_CLASS_COUNT][8] =
{ /* STR STA AGI DEX WIS INT CHR ADD*/
{ /*Warrior*/ 10, 10, 5, 0, 0, 0, 0, 25},
{ /*Cleric*/ 5, 5, 0, 0, 10, 0, 0, 30},
{ /*Paladin*/ 10, 5, 0, 0, 5, 0, 10, 20},
{ /*Ranger*/ 5, 10, 10, 0, 5, 0, 0, 20},
{ /*ShadowKnight*/ 10, 5, 0, 0, 0, 10, 5, 20},
{ /*Druid*/ 0, 10, 0, 0, 10, 0, 0, 30},
{ /*Monk*/ 5, 5, 10, 10, 0, 0, 0, 20},
{ /*Bard*/ 5, 0, 0, 10, 0, 0, 10, 25},
{ /*Rouge*/ 0, 0, 10, 10, 0, 0, 0, 30},
{ /*Shaman*/ 0, 5, 0, 0, 10, 0, 5, 30},
{ /*Necromancer*/ 0, 0, 0, 10, 0, 10, 0, 30},
{ /*Wizard*/ 0, 10, 0, 0, 0, 10, 0, 30},
{ /*Magician*/ 0, 10, 0, 0, 0, 10, 0, 30},
{ /*Enchanter*/ 0, 0, 0, 0, 0, 10, 10, 30},
{ /*Beastlord*/ 0, 10, 5, 0, 10, 0, 5, 20},
{ /*Berserker*/ 10, 5, 0, 10, 0, 0, 0, 25}
};
static const bool ClassRaceLookupTable[Class::PLAYER_CLASS_COUNT][_TABLE_RACES] =
{ /*Human Barbarian Erudite Woodelf Highelf Darkelf Halfelf Dwarf Troll Ogre Halfling Gnome Iksar Vahshir Froglok Drakkin*/
{ /*Warrior*/ true, true, false, true, false, true, true, true, true, true, true, true, true, true, true, true},
{ /*Cleric*/ true, false, true, false, true, true, true, true, false, false, true, true, false, false, true, true},
{ /*Paladin*/ true, false, true, false, true, false, true, true, false, false, true, true, false, false, true, true},
{ /*Ranger*/ true, false, false, true, false, false, true, false, false, false, true, false, false, false, false, true},
{ /*ShadowKnight*/ true, false, true, false, false, true, false, false, true, true, false, true, true, false, true, true},
{ /*Druid*/ true, false, false, true, false, false, true, false, false, false, true, false, false, false, false, true},
{ /*Monk*/ true, false, false, false, false, false, false, false, false, false, false, false, true, false, false, true},
{ /*Bard*/ true, false, false, true, false, false, true, false, false, false, false, false, false, true, false, true},
{ /*Rogue*/ true, true, false, true, false, true, true, true, false, false, true, true, false, true, true, true},
{ /*Shaman*/ false, true, false, false, false, false, false, false, true, true, false, false, true, true, true, false},
{ /*Necromancer*/ true, false, true, false, false, true, false, false, false, false, false, true, true, false, true, true},
{ /*Wizard*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, true, true},
{ /*Magician*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, false, true},
{ /*Enchanter*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, false, true},
{ /*Beastlord*/ false, true, false, false, false, false, false, false, true, true, false, false, true, true, false, false},
{ /*Berserker*/ false, true, false, false, false, false, false, true, true, true, false, false, false, true, false, false}
};
if (!cc)
return false;
LogInfo("Validating char creation info");
classtemp = cc->class_ - 1;
racetemp = cc->race - 1;
// these have non sequential race numbers so they need to be mapped
if (cc->race == FROGLOK) racetemp = 14;
if (cc->race == VAHSHIR) racetemp = 13;
if (cc->race == IKSAR) racetemp = 12;
if (cc->race == DRAKKIN) racetemp = 15;
// if out of range looking it up in the table would crash stuff
// so we return from these
if (classtemp >= Class::PLAYER_CLASS_COUNT) {
LogInfo(" class is out of range");
return false;
}
if (racetemp >= _TABLE_RACES) {
LogInfo(" race is out of range");
return false;
}
if (!ClassRaceLookupTable[classtemp][racetemp]) { //Lookup table better than a bunch of ifs?
LogInfo(" invalid race/class combination");
// we return from this one, since if it's an invalid combination our table
// doesn't have meaningful values for the stats
return false;
}
// add up the base values for this class/race
// this is what they start with, and they have stat_points more
// that can distributed
bSTR = BaseClass[classtemp][0] + BaseRace[racetemp][0];
bSTA = BaseClass[classtemp][1] + BaseRace[racetemp][1];
bAGI = BaseClass[classtemp][2] + BaseRace[racetemp][2];
bDEX = BaseClass[classtemp][3] + BaseRace[racetemp][3];
bWIS = BaseClass[classtemp][4] + BaseRace[racetemp][4];
bINT = BaseClass[classtemp][5] + BaseRace[racetemp][5];
bCHA = BaseClass[classtemp][6] + BaseRace[racetemp][6];
stat_points = BaseClass[classtemp][7];
bTOTAL = bSTR + bSTA + bAGI + bDEX + bWIS + bINT + bCHA;
cTOTAL = cc->STR + cc->STA + cc->AGI + cc->DEX + cc->WIS + cc->INT + cc->CHA;
// the first check makes sure the total is exactly what was expected.
// this will catch all the stat cheating, but there's still the issue
// of reducing CHA or INT or something, to use for STR, so we check
// that none are lower than the base or higher than base + stat_points
// NOTE: these could just be else if, but i want to see all the stats
// that are messed up not just the first hit
if (bTOTAL + stat_points != cTOTAL) {
LogInfo(" stat points total doesn't match expected value: expecting [{}] got [{}]", bTOTAL + stat_points, cTOTAL);
Charerrors++;
}
if (cc->STR > bSTR + stat_points || cc->STR < bSTR) {
LogInfo(" stat STR is out of range");
Charerrors++;
}
if (cc->STA > bSTA + stat_points || cc->STA < bSTA) {
LogInfo(" stat STA is out of range");
Charerrors++;
}
if (cc->AGI > bAGI + stat_points || cc->AGI < bAGI) {
LogInfo(" stat AGI is out of range");
Charerrors++;
}
if (cc->DEX > bDEX + stat_points || cc->DEX < bDEX) {
LogInfo(" stat DEX is out of range");
Charerrors++;
}
if (cc->WIS > bWIS + stat_points || cc->WIS < bWIS) {
LogInfo(" stat WIS is out of range");
Charerrors++;
}
if (cc->INT > bINT + stat_points || cc->INT < bINT) {
LogInfo(" stat INT is out of range");
Charerrors++;
}
if (cc->CHA > bCHA + stat_points || cc->CHA < bCHA) {
LogInfo(" stat CHA is out of range");
Charerrors++;
}
/*TODO: Check for deity/class/race.. it'd be nice, but probably of any real use to hack(faction, deity based items are all I can think of)
I am NOT writing those tables - kathgar*/
LogInfo("Found [{}] errors in character creation request", Charerrors);
return Charerrors == 0;
}
//TODO: these hard coded values should be settable somewhere somehow.
//Also they're not 100% accurate so if I don't make them settable somehow
//we need to go back and match them to the logic that was ripped out of zone
void GetResistsForCharacterCreate(CharCreate_Struct* cc,
bool sofAndLater,
uint32 &cold_resist,
uint32& fire_resist,
uint32& magic_resist,
uint32& disease_resist,
uint32& poison_resist,
uint32& corruption_resist)
{
if (!sofAndLater) {
cold_resist = 25;
fire_resist = 25;
magic_resist = 25;
disease_resist = 15;
poison_resist = 15;
corruption_resist = 15;
return;
}
CharCreateCombination class_combo;
bool found = false;
int combos = character_create_race_class_combos.size();
for (int i = 0; i < combos; ++i) {
if (character_create_race_class_combos[i].Class == cc->class_ &&
character_create_race_class_combos[i].Race == cc->race &&
character_create_race_class_combos[i].Deity == cc->deity &&
character_create_race_class_combos[i].Zone == cc->start_zone) {
class_combo = character_create_race_class_combos[i];
found = true;
break;
}
}
if (!found) {
cold_resist = 25;
fire_resist = 25;
magic_resist = 25;
disease_resist = 15;
poison_resist = 15;
corruption_resist = 15;
return;
}
CharCreatePointAllocation allocation;
found = false;
combos = character_create_allocations.size();
for (int i = 0; i < combos; ++i) {
if (character_create_allocations[i].Index == class_combo.AllocationIndex) {
allocation = character_create_allocations[i];
found = true;
break;
}
}
if (!found) {
cold_resist = 25;
fire_resist = 25;
magic_resist = 25;
disease_resist = 15;
poison_resist = 15;
corruption_resist = 15;
return;
}
cold_resist = allocation.BaseResists[0];
fire_resist = allocation.BaseResists[1];
magic_resist = allocation.BaseResists[2];
disease_resist = allocation.BaseResists[3];
poison_resist = allocation.BaseResists[4];
corruption_resist = allocation.BaseResists[5];
}
bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
{ {
PlayerProfile_Struct pp; PlayerProfile_Struct pp;
@@ -1745,6 +2088,15 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
pp.hunger_level = 6000; pp.hunger_level = 6000;
pp.thirst_level = 6000; pp.thirst_level = 6000;
GetResistsForCharacterCreate(cc,
m_ClientVersionBit & EQ::versions::maskSoFAndLater,
pp.cold_resist,
pp.fire_resist,
pp.magic_resist,
pp.disease_resist,
pp.poison_resist,
pp.corruption_resist);
/* Set default skills for everybody */ /* Set default skills for everybody */
pp.skills[EQ::skills::SkillSwimming] = RuleI(Skills, SwimmingStartValue); pp.skills[EQ::skills::SkillSwimming] = RuleI(Skills, SwimmingStartValue);
pp.skills[EQ::skills::SkillSenseHeading] = RuleI(Skills, SenseHeadingStartValue); pp.skills[EQ::skills::SkillSenseHeading] = RuleI(Skills, SenseHeadingStartValue);
@@ -1865,273 +2217,6 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
return success; return success;
} }
// returns true if the request is ok, false if there's an error
bool CheckCharCreateInfoSoF(CharCreate_Struct *cc)
{
if (!cc)
return false;
LogInfo("Validating char creation info");
RaceClassCombos class_combo;
bool found = false;
int combos = character_create_race_class_combos.size();
for (int i = 0; i < combos; ++i) {
if (character_create_race_class_combos[i].Class == cc->class_ &&
character_create_race_class_combos[i].Race == cc->race &&
character_create_race_class_combos[i].Deity == cc->deity &&
character_create_race_class_combos[i].Zone == cc->start_zone) {
class_combo = character_create_race_class_combos[i];
found = true;
break;
}
}
if (!found) {
LogInfo("Could not find class/race/deity/start_zone combination");
return false;
}
uint32 allocs = character_create_allocations.size();
RaceClassAllocation allocation = {0};
found = false;
for (int i = 0; i < allocs; ++i) {
if (character_create_allocations[i].Index == class_combo.AllocationIndex) {
allocation = character_create_allocations[i];
found = true;
break;
}
}
if (!found) {
LogInfo("Could not find starting stats for selected character combo, cannot verify stats");
return false;
}
uint32 max_stats = allocation.DefaultPointAllocation[0] +
allocation.DefaultPointAllocation[1] +
allocation.DefaultPointAllocation[2] +
allocation.DefaultPointAllocation[3] +
allocation.DefaultPointAllocation[4] +
allocation.DefaultPointAllocation[5] +
allocation.DefaultPointAllocation[6];
if (cc->STR > allocation.BaseStats[0] + max_stats || cc->STR < allocation.BaseStats[0]) {
LogInfo("Strength out of range");
return false;
}
if (cc->DEX > allocation.BaseStats[1] + max_stats || cc->DEX < allocation.BaseStats[1]) {
LogInfo("Dexterity out of range");
return false;
}
if (cc->AGI > allocation.BaseStats[2] + max_stats || cc->AGI < allocation.BaseStats[2]) {
LogInfo("Agility out of range");
return false;
}
if (cc->STA > allocation.BaseStats[3] + max_stats || cc->STA < allocation.BaseStats[3]) {
LogInfo("Stamina out of range");
return false;
}
if (cc->INT > allocation.BaseStats[4] + max_stats || cc->INT < allocation.BaseStats[4]) {
LogInfo("Intelligence out of range");
return false;
}
if (cc->WIS > allocation.BaseStats[5] + max_stats || cc->WIS < allocation.BaseStats[5]) {
LogInfo("Wisdom out of range");
return false;
}
if (cc->CHA > allocation.BaseStats[6] + max_stats || cc->CHA < allocation.BaseStats[6]) {
LogInfo("Charisma out of range");
return false;
}
uint32 current_stats = 0;
current_stats += cc->STR - allocation.BaseStats[0];
current_stats += cc->DEX - allocation.BaseStats[1];
current_stats += cc->AGI - allocation.BaseStats[2];
current_stats += cc->STA - allocation.BaseStats[3];
current_stats += cc->INT - allocation.BaseStats[4];
current_stats += cc->WIS - allocation.BaseStats[5];
current_stats += cc->CHA - allocation.BaseStats[6];
if (current_stats > max_stats) {
LogInfo("Current Stats > Maximum Stats");
return false;
}
return true;
}
bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc)
{
uint32 bSTR, bSTA, bAGI, bDEX, bWIS, bINT, bCHA, bTOTAL, cTOTAL, stat_points; //these are all uint32 in CharCreate_Struct, so we'll make them uint32 here to make the compiler shut up
int classtemp, racetemp;
int Charerrors = 0;
// if this is increased you'll have to add a column to the classrace
// table below
#define _TABLE_RACES 16
static const int BaseRace[_TABLE_RACES][7] =
{ /* STR STA AGI DEX WIS INT CHR */
{ /*Human*/ 75, 75, 75, 75, 75, 75, 75},
{ /*Barbarian*/ 103, 95, 82, 70, 70, 60, 55},
{ /*Erudite*/ 60, 70, 70, 70, 83, 107, 70},
{ /*Wood Elf*/ 65, 65, 95, 80, 80, 75, 75},
{ /*High Elf*/ 55, 65, 85, 70, 95, 92, 80},
{ /*Dark Elf*/ 60, 65, 90, 75, 83, 99, 60},
{ /*Half Elf*/ 70, 70, 90, 85, 60, 75, 75},
{ /*Dwarf*/ 90, 90, 70, 90, 83, 60, 45},
{ /*Troll*/ 108, 109, 83, 75, 60, 52, 40},
{ /*Ogre*/ 130, 122, 70, 70, 67, 60, 37},
{ /*Halfling*/ 70, 75, 95, 90, 80, 67, 50},
{ /*Gnome*/ 60, 70, 85, 85, 67, 98, 60},
{ /*Iksar*/ 70, 70, 90, 85, 80, 75, 55},
{ /*Vah Shir*/ 90, 75, 90, 70, 70, 65, 65},
{ /*Froglok*/ 70, 80, 100, 100, 75, 75, 50},
{ /*Drakkin*/ 70, 80, 85, 75, 80, 85, 75}
};
static const int BaseClass[Class::PLAYER_CLASS_COUNT][8] =
{ /* STR STA AGI DEX WIS INT CHR ADD*/
{ /*Warrior*/ 10, 10, 5, 0, 0, 0, 0, 25},
{ /*Cleric*/ 5, 5, 0, 0, 10, 0, 0, 30},
{ /*Paladin*/ 10, 5, 0, 0, 5, 0, 10, 20},
{ /*Ranger*/ 5, 10, 10, 0, 5, 0, 0, 20},
{ /*ShadowKnight*/ 10, 5, 0, 0, 0, 10, 5, 20},
{ /*Druid*/ 0, 10, 0, 0, 10, 0, 0, 30},
{ /*Monk*/ 5, 5, 10, 10, 0, 0, 0, 20},
{ /*Bard*/ 5, 0, 0, 10, 0, 0, 10, 25},
{ /*Rouge*/ 0, 0, 10, 10, 0, 0, 0, 30},
{ /*Shaman*/ 0, 5, 0, 0, 10, 0, 5, 30},
{ /*Necromancer*/ 0, 0, 0, 10, 0, 10, 0, 30},
{ /*Wizard*/ 0, 10, 0, 0, 0, 10, 0, 30},
{ /*Magician*/ 0, 10, 0, 0, 0, 10, 0, 30},
{ /*Enchanter*/ 0, 0, 0, 0, 0, 10, 10, 30},
{ /*Beastlord*/ 0, 10, 5, 0, 10, 0, 5, 20},
{ /*Berserker*/ 10, 5, 0, 10, 0, 0, 0, 25}
};
static const bool ClassRaceLookupTable[Class::PLAYER_CLASS_COUNT][_TABLE_RACES]=
{ /*Human Barbarian Erudite Woodelf Highelf Darkelf Halfelf Dwarf Troll Ogre Halfling Gnome Iksar Vahshir Froglok Drakkin*/
{ /*Warrior*/ true, true, false, true, false, true, true, true, true, true, true, true, true, true, true, true},
{ /*Cleric*/ true, false, true, false, true, true, true, true, false, false, true, true, false, false, true, true},
{ /*Paladin*/ true, false, true, false, true, false, true, true, false, false, true, true, false, false, true, true},
{ /*Ranger*/ true, false, false, true, false, false, true, false, false, false, true, false, false, false, false, true},
{ /*ShadowKnight*/ true, false, true, false, false, true, false, false, true, true, false, true, true, false, true, true},
{ /*Druid*/ true, false, false, true, false, false, true, false, false, false, true, false, false, false, false, true},
{ /*Monk*/ true, false, false, false, false, false, false, false, false, false, false, false, true, false, false, true},
{ /*Bard*/ true, false, false, true, false, false, true, false, false, false, false, false, false, true, false, true},
{ /*Rogue*/ true, true, false, true, false, true, true, true, false, false, true, true, false, true, true, true},
{ /*Shaman*/ false, true, false, false, false, false, false, false, true, true, false, false, true, true, true, false},
{ /*Necromancer*/ true, false, true, false, false, true, false, false, false, false, false, true, true, false, true, true},
{ /*Wizard*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, true, true},
{ /*Magician*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, false, true},
{ /*Enchanter*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, false, true},
{ /*Beastlord*/ false, true, false, false, false, false, false, false, true, true, false, false, true, true, false, false},
{ /*Berserker*/ false, true, false, false, false, false, false, true, true, true, false, false, false, true, false, false}
};
if (!cc)
return false;
LogInfo("Validating char creation info");
classtemp = cc->class_ - 1;
racetemp = cc->race - 1;
// these have non sequential race numbers so they need to be mapped
if (cc->race == FROGLOK) racetemp = 14;
if (cc->race == VAHSHIR) racetemp = 13;
if (cc->race == IKSAR) racetemp = 12;
if (cc->race == DRAKKIN) racetemp = 15;
// if out of range looking it up in the table would crash stuff
// so we return from these
if (classtemp >= Class::PLAYER_CLASS_COUNT) {
LogInfo(" class is out of range");
return false;
}
if (racetemp >= _TABLE_RACES) {
LogInfo(" race is out of range");
return false;
}
if (!ClassRaceLookupTable[classtemp][racetemp]) { //Lookup table better than a bunch of ifs?
LogInfo(" invalid race/class combination");
// we return from this one, since if it's an invalid combination our table
// doesn't have meaningful values for the stats
return false;
}
// add up the base values for this class/race
// this is what they start with, and they have stat_points more
// that can distributed
bSTR = BaseClass[classtemp][0] + BaseRace[racetemp][0];
bSTA = BaseClass[classtemp][1] + BaseRace[racetemp][1];
bAGI = BaseClass[classtemp][2] + BaseRace[racetemp][2];
bDEX = BaseClass[classtemp][3] + BaseRace[racetemp][3];
bWIS = BaseClass[classtemp][4] + BaseRace[racetemp][4];
bINT = BaseClass[classtemp][5] + BaseRace[racetemp][5];
bCHA = BaseClass[classtemp][6] + BaseRace[racetemp][6];
stat_points = BaseClass[classtemp][7];
bTOTAL = bSTR + bSTA + bAGI + bDEX + bWIS + bINT + bCHA;
cTOTAL = cc->STR + cc->STA + cc->AGI + cc->DEX + cc->WIS + cc->INT + cc->CHA;
// the first check makes sure the total is exactly what was expected.
// this will catch all the stat cheating, but there's still the issue
// of reducing CHA or INT or something, to use for STR, so we check
// that none are lower than the base or higher than base + stat_points
// NOTE: these could just be else if, but i want to see all the stats
// that are messed up not just the first hit
if (bTOTAL + stat_points != cTOTAL) {
LogInfo(" stat points total doesn't match expected value: expecting [{}] got [{}]", bTOTAL + stat_points, cTOTAL);
Charerrors++;
}
if (cc->STR > bSTR + stat_points || cc->STR < bSTR) {
LogInfo(" stat STR is out of range");
Charerrors++;
}
if (cc->STA > bSTA + stat_points || cc->STA < bSTA) {
LogInfo(" stat STA is out of range");
Charerrors++;
}
if (cc->AGI > bAGI + stat_points || cc->AGI < bAGI) {
LogInfo(" stat AGI is out of range");
Charerrors++;
}
if (cc->DEX > bDEX + stat_points || cc->DEX < bDEX) {
LogInfo(" stat DEX is out of range");
Charerrors++;
}
if (cc->WIS > bWIS + stat_points || cc->WIS < bWIS) {
LogInfo(" stat WIS is out of range");
Charerrors++;
}
if (cc->INT > bINT + stat_points || cc->INT < bINT) {
LogInfo(" stat INT is out of range");
Charerrors++;
}
if (cc->CHA > bCHA + stat_points || cc->CHA < bCHA) {
LogInfo(" stat CHA is out of range");
Charerrors++;
}
/*TODO: Check for deity/class/race.. it'd be nice, but probably of any real use to hack(faction, deity based items are all I can think of)
I am NOT writing those tables - kathgar*/
LogInfo("Found [{}] errors in character creation request", Charerrors);
return Charerrors == 0;
}
void Client::SetClassStartingSkills(PlayerProfile_Struct *pp) void Client::SetClassStartingSkills(PlayerProfile_Struct *pp)
{ {
for (uint32 i = 0; i <= EQ::skills::HIGHEST_SKILL; ++i) { for (uint32 i = 0; i <= EQ::skills::HIGHEST_SKILL; ++i) {
-3
View File
@@ -122,7 +122,4 @@ private:
void RecordPossibleHack(const std::string& message); void RecordPossibleHack(const std::string& message);
}; };
bool CheckCharCreateInfoSoF(CharCreate_Struct *cc);
bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc);
#endif #endif
+23 -5
View File
@@ -315,6 +315,13 @@ void LoginServer::ProcessLSFatalError(uint16_t opcode, EQ::Net::Packet &p)
error, error,
reason reason
); );
if (m_legacy_client) {
m_legacy_client.release();
}
else if (m_client) {
m_client.release();
}
} }
void LoginServer::ProcessSystemwideMessage(uint16_t opcode, EQ::Net::Packet &p) void LoginServer::ProcessSystemwideMessage(uint16_t opcode, EQ::Net::Packet &p)
@@ -598,6 +605,11 @@ bool LoginServer::Connect()
void LoginServer::SendInfo() void LoginServer::SendInfo()
{ {
if (m_client == nullptr && m_legacy_client == nullptr) {
LogDebug("No client to send info to loginserver");
return;
}
const WorldConfig *Config = WorldConfig::get(); const WorldConfig *Config = WorldConfig::get();
auto pack = new ServerPacket; auto pack = new ServerPacket;
@@ -643,6 +655,11 @@ void LoginServer::SendInfo()
void LoginServer::SendStatus() void LoginServer::SendStatus()
{ {
if (m_client == nullptr && m_legacy_client == nullptr) {
LogDebug("No client to send status to loginserver");
return;
}
auto pack = new ServerPacket; auto pack = new ServerPacket;
pack->opcode = ServerOP_LSStatus; pack->opcode = ServerOP_LSStatus;
pack->size = sizeof(ServerLSStatus_Struct); pack->size = sizeof(ServerLSStatus_Struct);
@@ -671,20 +688,21 @@ void LoginServer::SendStatus()
*/ */
void LoginServer::SendPacket(ServerPacket *pack) void LoginServer::SendPacket(ServerPacket *pack)
{ {
if (m_is_legacy) {
if (m_legacy_client) { if (m_legacy_client) {
m_legacy_client->SendPacket(pack); m_legacy_client->SendPacket(pack);
} }
} else if (m_client) {
else {
if (m_client) {
m_client->SendPacket(pack); m_client->SendPacket(pack);
} }
}
} }
void LoginServer::SendAccountUpdate(ServerPacket *pack) void LoginServer::SendAccountUpdate(ServerPacket *pack)
{ {
if (m_client == nullptr && m_legacy_client == nullptr) {
LogDebug("No client to send account update to loginserver");
return;
}
auto *ls_account_update = (ServerLSAccountUpdate_Struct *) pack->pBuffer; auto *ls_account_update = (ServerLSAccountUpdate_Struct *) pack->pBuffer;
if (CanUpdate()) { if (CanUpdate()) {
LogInfo( LogInfo(
+18
View File
@@ -0,0 +1,18 @@
#pragma once
struct CharCreatePointAllocation
{
unsigned int Index;
unsigned int BaseStats[7];
unsigned int DefaultPointAllocation[7];
unsigned int BaseResists[7];
};
struct CharCreateCombination {
unsigned int ExpansionRequired;
unsigned int Race;
unsigned int Class;
unsigned int Deity;
unsigned int AllocationIndex;
unsigned int Zone;
};
+11 -4
View File
@@ -25,14 +25,15 @@
#include <cstdlib> #include <cstdlib>
#include <vector> #include <vector>
#include "sof_char_create_data.h" #include "sof_char_create_data.h"
#include "race_combos.h"
#include "../common/repositories/character_instance_safereturns_repository.h" #include "../common/repositories/character_instance_safereturns_repository.h"
#include "../common/repositories/criteria/content_filter_criteria.h" #include "../common/repositories/criteria/content_filter_criteria.h"
#include "../common/zone_store.h" #include "../common/zone_store.h"
WorldDatabase database; WorldDatabase database;
WorldDatabase content_db; WorldDatabase content_db;
extern std::vector<RaceClassAllocation> character_create_allocations; extern std::vector<CharCreatePointAllocation> character_create_allocations;
extern std::vector<RaceClassCombos> character_create_race_class_combos; extern std::vector<CharCreateCombination> character_create_race_class_combos;
/** /**
@@ -807,7 +808,7 @@ bool WorldDatabase::LoadCharacterCreateAllocations()
return false; return false;
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
RaceClassAllocation allocate; CharCreatePointAllocation allocate;
allocate.Index = Strings::ToInt(row[0]); allocate.Index = Strings::ToInt(row[0]);
allocate.BaseStats[0] = Strings::ToInt(row[1]); allocate.BaseStats[0] = Strings::ToInt(row[1]);
allocate.BaseStats[3] = Strings::ToInt(row[2]); allocate.BaseStats[3] = Strings::ToInt(row[2]);
@@ -823,6 +824,12 @@ bool WorldDatabase::LoadCharacterCreateAllocations()
allocate.DefaultPointAllocation[4] = Strings::ToInt(row[12]); allocate.DefaultPointAllocation[4] = Strings::ToInt(row[12]);
allocate.DefaultPointAllocation[5] = Strings::ToInt(row[13]); allocate.DefaultPointAllocation[5] = Strings::ToInt(row[13]);
allocate.DefaultPointAllocation[6] = Strings::ToInt(row[14]); allocate.DefaultPointAllocation[6] = Strings::ToInt(row[14]);
allocate.BaseResists[0] = Strings::ToInt(row[15]);
allocate.BaseResists[1] = Strings::ToInt(row[16]);
allocate.BaseResists[2] = Strings::ToInt(row[17]);
allocate.BaseResists[3] = Strings::ToInt(row[18]);
allocate.BaseResists[4] = Strings::ToInt(row[19]);
allocate.BaseResists[5] = Strings::ToInt(row[20]);
character_create_allocations.push_back(allocate); character_create_allocations.push_back(allocate);
} }
@@ -840,7 +847,7 @@ bool WorldDatabase::LoadCharacterCreateCombos()
return false; return false;
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
RaceClassCombos combo; CharCreateCombination combo;
combo.AllocationIndex = Strings::ToInt(row[0]); combo.AllocationIndex = Strings::ToInt(row[0]);
combo.Race = Strings::ToInt(row[1]); combo.Race = Strings::ToInt(row[1]);
combo.Class = Strings::ToInt(row[2]); combo.Class = Strings::ToInt(row[2]);
+3
View File
@@ -65,6 +65,7 @@ SET(zone_sources
lua_inventory.cpp lua_inventory.cpp
lua_item.cpp lua_item.cpp
lua_iteminst.cpp lua_iteminst.cpp
lua_merc.cpp
lua_mob.cpp lua_mob.cpp
lua_mod.cpp lua_mod.cpp
lua_npc.cpp lua_npc.cpp
@@ -115,6 +116,7 @@ SET(zone_sources
perl_groups.cpp perl_groups.cpp
perl_hateentry.cpp perl_hateentry.cpp
perl_inventory.cpp perl_inventory.cpp
perl_merc.cpp
perl_mob.cpp perl_mob.cpp
perl_npc.cpp perl_npc.cpp
perl_object.cpp perl_object.cpp
@@ -224,6 +226,7 @@ SET(zone_headers
lua_inventory.h lua_inventory.h
lua_item.h lua_item.h
lua_iteminst.h lua_iteminst.h
lua_merc.h
lua_mob.h lua_mob.h
lua_mod.h lua_mod.h
lua_npc.h lua_npc.h
+5 -1
View File
@@ -2185,7 +2185,7 @@ void Client::AutoGrantAAPoints() {
SendAlternateAdvancementStats(); SendAlternateAdvancementStats();
} }
void Client::GrantAllAAPoints(uint8 unlock_level) void Client::GrantAllAAPoints(uint8 unlock_level, bool skip_grant_only)
{ {
//iterate through every AA //iterate through every AA
for (auto& aa : zone->aa_abilities) { for (auto& aa : zone->aa_abilities) {
@@ -2195,6 +2195,10 @@ void Client::GrantAllAAPoints(uint8 unlock_level)
continue; continue;
} }
if (ability->grant_only && skip_grant_only) {
continue;
}
const uint8 level = unlock_level ? unlock_level : GetLevel(); const uint8 level = unlock_level ? unlock_level : GetLevel();
AA::Rank* rank = ability->first; AA::Rank* rank = ability->first;
+144 -159
View File
@@ -1478,8 +1478,10 @@ int64 Mob::DoDamageCaps(int64 base_damage)
//SYNC WITH: tune.cpp, mob.h TuneDoAttack //SYNC WITH: tune.cpp, mob.h TuneDoAttack
void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, bool FromRiposte) void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, bool FromRiposte)
{ {
if (!other) if (!other) {
return; return;
}
LogCombat("[{}]::DoAttack vs [{}] base [{}] min [{}] offense [{}] tohit [{}] skill [{}]", GetName(), LogCombat("[{}]::DoAttack vs [{}] base [{}] min [{}] offense [{}] tohit [{}] skill [{}]", GetName(),
other->GetName(), hit.base_damage, hit.min_damage, hit.offense, hit.tohit, hit.skill); other->GetName(), hit.base_damage, hit.min_damage, hit.offense, hit.tohit, hit.skill);
@@ -1491,14 +1493,22 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
if (!FromRiposte && other->AvoidDamage(this, hit)) { if (!FromRiposte && other->AvoidDamage(this, hit)) {
if (int strike_through = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough; if (int strike_through = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough;
strike_through && zone->random.Roll(strike_through)) { strike_through && zone->random.Roll(strike_through)) {
MessageString(Chat::StrikeThrough,
STRIKETHROUGH_STRING); // You strike through your opponents defenses! FilteredMessageString(
this, /* Sender */
Chat::StrikeThrough, /* Type: 339 */
FilterStrikethrough, /* FilterType: 12 */
STRIKETHROUGH_STRING /* You strike through your opponent's defenses! */
);
hit.damage_done = 1; // set to one, we will check this to continue hit.damage_done = 1; // set to one, we will check this to continue
} }
if (hit.damage_done == DMG_RIPOSTED) { if (hit.damage_done == DMG_RIPOSTED) {
DoRiposte(other); DoRiposte(other);
return; return;
} }
LogCombat("Avoided/strikethrough damage with code [{}]", hit.damage_done); LogCombat("Avoided/strikethrough damage with code [{}]", hit.damage_done);
} }
@@ -1510,9 +1520,19 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
int stun_resist2 = other->spellbonuses.FrontalStunResist + other->itembonuses.FrontalStunResist + other->aabonuses.FrontalStunResist; int stun_resist2 = other->spellbonuses.FrontalStunResist + other->itembonuses.FrontalStunResist + other->aabonuses.FrontalStunResist;
int stun_resist = other->spellbonuses.StunResist + other->itembonuses.StunResist + other->aabonuses.StunResist; int stun_resist = other->spellbonuses.StunResist + other->itembonuses.StunResist + other->aabonuses.StunResist;
if (zone->random.Roll(stun_resist2)) { if (zone->random.Roll(stun_resist2)) {
other->MessageString(Chat::Stun, AVOID_STUNNING_BLOW); other->FilteredMessageString(
this,
Chat::Stun,
FilterStuns,
AVOID_STUNNING_BLOW
);
} else if (zone->random.Roll(stun_resist)) { } else if (zone->random.Roll(stun_resist)) {
other->MessageString(Chat::Stun, SHAKE_OFF_STUN); other->FilteredMessageString(
this,
Chat::Stun,
FilterStuns,
SHAKE_OFF_STUN
);
} else { } else {
other->Stun(3000); // yuck -- 3 seconds other->Stun(3000); // yuck -- 3 seconds
} }
@@ -1530,17 +1550,18 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
hit.damage_done = 0; hit.damage_done = 0;
} }
if (IsBot()) { parse->EventBotMerc(
if (parse->BotHasQuestSub(EVENT_USE_SKILL)) { EVENT_USE_SKILL,
const auto& export_string = fmt::format( this,
nullptr,
[&]() {
return fmt::format(
"{} {}", "{} {}",
hit.skill, hit.skill,
GetSkill(hit.skill) GetSkill(hit.skill)
); );
parse->EventBot(EVENT_USE_SKILL, CastToBot(), nullptr, export_string, 0);
}
} }
);
} }
} }
@@ -1719,7 +1740,7 @@ bool Mob::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
(HasOwner() && GetOwner()->IsClient() && other->IsClient()) (HasOwner() && GetOwner()->IsClient() && other->IsClient())
) )
) { ) {
for (auto const& [id, mob] : entity_list.GetCloseMobList(other)) { for (auto const& [id, mob] : other->GetCloseMobList()) {
if (!mob) { if (!mob) {
continue; continue;
} }
@@ -1902,11 +1923,9 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil
} }
if (killer_mob) { if (killer_mob) {
if (killer_mob->IsNPC()) { parse->EventBotMercNPC(EVENT_SLAY, killer_mob, this);
if (parse->HasQuestSub(killer_mob->GetNPCTypeID(), EVENT_SLAY)) {
parse->EventNPC(EVENT_SLAY, killer_mob->CastToNPC(), this, "", 0);
}
if (killer_mob->IsNPC()) {
killed_by = KilledByTypes::Killed_NPC; killed_by = KilledByTypes::Killed_NPC;
auto emote_id = killer_mob->GetEmoteID(); auto emote_id = killer_mob->GetEmoteID();
@@ -1914,12 +1933,6 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil
killer_mob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this); killer_mob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this);
} }
killer_mob->TrySpellOnKill(killed_level, spell);
} else if (killer_mob->IsBot()) {
if (parse->BotHasQuestSub(EVENT_SLAY)) {
parse->EventBot(EVENT_SLAY, killer_mob->CastToBot(), this, "", 0);
}
killer_mob->TrySpellOnKill(killed_level, spell); killer_mob->TrySpellOnKill(killed_level, spell);
} }
@@ -2299,8 +2312,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
//Guard Assist Code //Guard Assist Code
if (RuleB(Character, PVPEnableGuardFactionAssist)) { if (RuleB(Character, PVPEnableGuardFactionAssist)) {
if (IsClient() && other->IsClient() || (HasOwner() && GetOwner()->IsClient() && other->IsClient())) { if (IsClient() && other->IsClient() || (HasOwner() && GetOwner()->IsClient() && other->IsClient())) {
auto& mob_list = entity_list.GetCloseMobList(other); for (auto& e : other->GetCloseMobList()) {
for (auto& e : mob_list) {
auto mob = e.second; auto mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -2439,14 +2451,10 @@ void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillTyp
spell_id = SPELL_UNKNOWN; spell_id = SPELL_UNKNOWN;
//handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds
if (attacked_timer.Check()) if (attacked_timer.Check()) {
{ parse->EventMercNPC(EVENT_ATTACK, this, other);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_ATTACK)) { }
LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", other ? other->GetName() : "nullptr");
parse->EventNPC(EVENT_ATTACK, this, other, "", 0);
}
}
attacked_timer.Start(CombatEventTimer_expire); attacked_timer.Start(CombatEventTimer_expire);
if (!IsEngaged()) if (!IsEngaged())
@@ -2487,42 +2495,23 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
Mob* owner_or_self = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr; Mob* owner_or_self = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr;
if (IsNPC()) { auto exports = [&]() {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH)) { return fmt::format(
const auto& export_string = fmt::format(
"{} {} {} {}", "{} {} {} {}",
killer_mob ? killer_mob->GetID() : 0, killer_mob ? killer_mob->GetID() : 0,
damage, damage,
spell, spell,
static_cast<int>(attack_skill) static_cast<int>(attack_skill)
); );
};
if (parse->EventNPC(EVENT_DEATH, this, owner_or_self, export_string, 0) != 0) { if (parse->EventBotMercNPC(EVENT_DEATH, this, owner_or_self, exports) != 0) {
if (GetHP() < 0) { if (GetHP() < 0) {
SetHP(0); SetHP(0);
} }
return false; return false;
} }
}
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_DEATH)) {
const auto& export_string = fmt::format(
"{} {} {} {}",
killer_mob ? killer_mob->GetID() : 0,
damage,
spell,
static_cast<int>(attack_skill)
);
if (parse->EventBot(EVENT_DEATH, CastToBot(), owner_or_self, export_string, 0) != 0) {
if (GetHP() < 0) {
SetHP(0);
}
return false;
}
}
}
if (killer_mob && killer_mob->IsOfClientBot() && IsValidSpell(spell) && damage > 0) { if (killer_mob && killer_mob->IsOfClientBot() && IsValidSpell(spell) && damage > 0) {
char val1[20] = { 0 }; char val1[20] = { 0 };
@@ -2953,8 +2942,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
entity_list.UnMarkNPC(GetID()); entity_list.UnMarkNPC(GetID());
entity_list.RemoveNPC(GetID()); entity_list.RemoveNPC(GetID());
// entity_list.RemoveMobFromCloseLists(this); m_close_mobs.clear();
close_mobs.clear();
SetID(0); SetID(0);
ApplyIllusionToCorpse(illusion_spell_id, corpse); ApplyIllusionToCorpse(illusion_spell_id, corpse);
@@ -3059,10 +3047,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
} }
} }
if (killer_mob && killer_mob->IsBot()) { if (killer_mob) {
if (parse->BotHasQuestSub(EVENT_NPC_SLAY)) { parse->EventBotMerc(EVENT_NPC_SLAY, killer_mob, this);
parse->EventBot(EVENT_NPC_SLAY, killer_mob->CastToBot(), this, "", 0);
}
killer_mob->TrySpellOnKill(killed_level, spell); killer_mob->TrySpellOnKill(killed_level, spell);
} }
@@ -3090,8 +3076,14 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
} }
} }
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH_COMPLETE)) { std::vector<std::any> args = { corpse };
const auto& export_string = fmt::format(
parse->EventMercNPC(
EVENT_DEATH_COMPLETE,
this,
owner_or_self,
[&]() {
return fmt::format(
"{} {} {} {} {} {} {} {} {}", "{} {} {} {} {} {} {} {} {}",
killer_mob ? killer_mob->GetID() : 0, killer_mob ? killer_mob->GetID() : 0,
damage, damage,
@@ -3103,11 +3095,10 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
m_combat_record.GetDamageReceived(), m_combat_record.GetDamageReceived(),
m_combat_record.GetHealingReceived() m_combat_record.GetHealingReceived()
); );
},
std::vector<std::any> args = { corpse }; 0,
&args
parse->EventNPC(EVENT_DEATH_COMPLETE, this, owner_or_self, export_string, 0, &args); );
}
// Zone controller process EVENT_DEATH_ZONE (Death events) // Zone controller process EVENT_DEATH_ZONE (Death events)
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DEATH_ZONE)) { if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DEATH_ZONE)) {
@@ -4267,80 +4258,20 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
//final damage has been determined. //final damage has been determined.
int old_hp_ratio = (int)GetHPRatio(); int old_hp_ratio = (int)GetHPRatio();
SetHP(int64(GetHP() - damage));
const auto has_bot_given_event = parse->BotHasQuestSub(EVENT_DAMAGE_GIVEN);
const auto has_bot_taken_event = parse->BotHasQuestSub(EVENT_DAMAGE_TAKEN);
const auto has_npc_given_event = (
(
IsNPC() &&
parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_GIVEN)
) ||
(
attacker &&
attacker->IsNPC() &&
parse->HasQuestSub(attacker->CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_GIVEN)
)
);
const auto has_npc_taken_event = (
(
IsNPC() &&
parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_TAKEN)
) ||
(
attacker &&
attacker->IsNPC() &&
parse->HasQuestSub(attacker->CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_TAKEN)
)
);
const auto has_player_given_event = parse->PlayerHasQuestSub(EVENT_DAMAGE_GIVEN);
const auto has_player_taken_event = parse->PlayerHasQuestSub(EVENT_DAMAGE_TAKEN);
const auto has_given_event = (
has_bot_given_event ||
has_npc_given_event ||
has_player_given_event
);
const auto has_taken_event = (
has_bot_taken_event ||
has_npc_taken_event ||
has_player_taken_event
);
std::vector<std::any> args; std::vector<std::any> args;
if (has_taken_event) { int64 damage_override = 0;
const auto export_string = fmt::format(
"{} {} {} {} {} {} {} {} {}",
attacker ? attacker->GetID() : 0,
damage,
spell_id,
static_cast<int>(skill_used),
FromDamageShield ? 1 : 0,
avoidable ? 1 : 0,
buffslot,
iBuffTic ? 1 : 0,
static_cast<int>(special)
);
if (IsBot() && has_bot_taken_event) { if (attacker) {
parse->EventBot(EVENT_DAMAGE_TAKEN, CastToBot(), attacker ? attacker : nullptr, export_string, 0); args = { this };
} else if (IsClient() && has_player_taken_event) {
args.push_back(attacker ? attacker : nullptr);
parse->EventPlayer(EVENT_DAMAGE_TAKEN, CastToClient(), export_string, 0, &args);
} else if (IsNPC() && has_npc_taken_event) {
parse->EventNPC(EVENT_DAMAGE_TAKEN, CastToNPC(), attacker ? attacker : nullptr, export_string, 0);
}
}
if (has_given_event && attacker) { parse->EventMob(
const auto export_string = fmt::format( EVENT_DAMAGE_GIVEN,
attacker,
this,
[&]() {
return fmt::format(
"{} {} {} {} {} {} {} {} {}", "{} {} {} {} {} {} {} {} {}",
GetID(), GetID(),
damage, damage,
@@ -4352,17 +4283,44 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
iBuffTic ? 1 : 0, iBuffTic ? 1 : 0,
static_cast<int>(special) static_cast<int>(special)
); );
},
0,
&args
);
}
if (attacker->IsBot() && has_bot_given_event) { args = { attacker };
parse->EventBot(EVENT_DAMAGE_GIVEN, attacker->CastToBot(), this, export_string, 0);
} else if (attacker->IsClient() && has_player_given_event) { damage_override = parse->EventMob(
args.push_back(this); EVENT_DAMAGE_TAKEN,
parse->EventPlayer(EVENT_DAMAGE_GIVEN, attacker->CastToClient(), export_string, 0, &args); this,
} else if (attacker->IsNPC() && has_npc_given_event) { attacker,
parse->EventNPC(EVENT_DAMAGE_GIVEN, attacker->CastToNPC(), this, export_string, 0); [&]() {
} return fmt::format(
"{} {} {} {} {} {} {} {} {}",
attacker ? attacker->GetID() : 0,
damage,
spell_id,
static_cast<int>(skill_used),
FromDamageShield ? 1 : 0,
avoidable ? 1 : 0,
buffslot,
iBuffTic ? 1 : 0,
static_cast<int>(special)
);
},
0,
&args
);
if (damage_override > 0) {
damage = damage_override;
} else if (damage_override < 0) {
damage = 0;
} }
SetHP(int64(GetHP() - damage));
if (HasDied()) { if (HasDied()) {
bool IsSaved = false; bool IsSaved = false;
@@ -4506,34 +4464,61 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
Stun(RuleI(Combat, StunDuration)); Stun(RuleI(Combat, StunDuration));
if (RuleB(Combat, ClientStunMessage) && attacker->IsClient()) { if (RuleB(Combat, ClientStunMessage) && attacker->IsClient()) {
if (attacker) { if (attacker) {
entity_list.MessageClose(this, true, 500, Chat::Emote, "%s is stunned after being bashed by %s.", GetCleanName(), attacker->GetCleanName()); entity_list.FilteredMessageClose(
} this,
else { true,
entity_list.MessageClose(this, true, 500, Chat::Emote, "%s is stunned by a bash to the head.", GetCleanName()); RuleI(Range, StunMessages),
Chat::Stun,
FilterStuns,
"%s is stunned after being bashed by %s.",
GetCleanName(),
attacker->GetCleanName()
);
} else {
entity_list.FilteredMessageClose(
this,
true,
RuleI(Range, StunMessages),
Chat::Stun,
FilterStuns,
"%s is stunned by a bash to the head.",
GetCleanName()
);
} }
} }
} } else {
else {
// stun resist passed! // stun resist passed!
if (IsClient()) if (IsClient()) {
MessageString(Chat::Stun, SHAKE_OFF_STUN); FilteredMessageString(
this,
Chat::Stun,
FilterStuns,
SHAKE_OFF_STUN
);
} }
} }
else { } else {
// stun resist 2 passed! // stun resist 2 passed!
if (IsClient()) if (IsClient()) {
MessageString(Chat::Stun, AVOID_STUNNING_BLOW); FilteredMessageString(
this,
Chat::Stun,
FilterStuns,
AVOID_STUNNING_BLOW
);
} }
} }
else { } else {
// main stun failed -- extra interrupt roll // main stun failed -- extra interrupt roll
if (IsCasting() && // these spells are excluded
!EQ::ValueWithin(casting_spell_id, 859, 1023)) // these spells are excluded
// 90% chance >< -- stun immune won't reach this branch though :( // 90% chance >< -- stun immune won't reach this branch though :(
if (zone->random.Int(0, 9) > 1) if (IsCasting() && !EQ::ValueWithin(casting_spell_id, 859, 1023)) {
if (zone->random.Int(0, 9) > 1) {
InterruptSpell(); InterruptSpell();
} }
} }
}
}
if (IsValidSpell(spell_id) && !iBuffTic) { if (IsValidSpell(spell_id) && !iBuffTic) {
//see if root will break //see if root will break
+7 -12
View File
@@ -72,7 +72,7 @@ Mob *Aura::GetOwner()
// not 100% sure how this one should work and PVP affects ... // not 100% sure how this one should work and PVP affects ...
void Aura::ProcessOnAllFriendlies(Mob *owner) void Aura::ProcessOnAllFriendlies(Mob *owner)
{ {
auto &mob_list = entity_list.GetCloseMobList(this, distance); auto &mob_list = GetCloseMobList(distance);
std::set<int> delayed_remove; std::set<int> delayed_remove;
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
@@ -127,7 +127,7 @@ void Aura::ProcessOnAllFriendlies(Mob *owner)
void Aura::ProcessOnAllGroupMembers(Mob *owner) void Aura::ProcessOnAllGroupMembers(Mob *owner)
{ {
auto &mob_list = entity_list.GetCloseMobList(this, distance); auto &mob_list = GetCloseMobList(distance);
std::set<int> delayed_remove; std::set<int> delayed_remove;
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
@@ -369,7 +369,7 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner)
void Aura::ProcessOnGroupMembersPets(Mob *owner) void Aura::ProcessOnGroupMembersPets(Mob *owner)
{ {
auto &mob_list = entity_list.GetCloseMobList(this,distance); auto &mob_list = GetCloseMobList(distance);
std::set<int> delayed_remove; std::set<int> delayed_remove;
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
// This type can either live on the pet (level 55/70 MAG aura) or on the pet owner (level 85 MAG aura) // This type can either live on the pet (level 55/70 MAG aura) or on the pet owner (level 85 MAG aura)
@@ -576,7 +576,7 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner)
void Aura::ProcessTotem(Mob *owner) void Aura::ProcessTotem(Mob *owner)
{ {
auto &mob_list = entity_list.GetCloseMobList(this, distance); auto &mob_list = GetCloseMobList(distance);
std::set<int> delayed_remove; std::set<int> delayed_remove;
bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
@@ -634,9 +634,7 @@ void Aura::ProcessTotem(Mob *owner)
void Aura::ProcessEnterTrap(Mob *owner) void Aura::ProcessEnterTrap(Mob *owner)
{ {
auto &mob_list = entity_list.GetCloseMobList(this, distance); for (auto &e : GetCloseMobList(distance)) {
for (auto &e : mob_list) {
auto mob = e.second; auto mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -656,9 +654,7 @@ void Aura::ProcessEnterTrap(Mob *owner)
void Aura::ProcessExitTrap(Mob *owner) void Aura::ProcessExitTrap(Mob *owner)
{ {
auto &mob_list = entity_list.GetCloseMobList(this, distance); for (auto &e : GetCloseMobList(distance)) {
for (auto &e : mob_list) {
auto mob = e.second; auto mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -689,8 +685,7 @@ void Aura::ProcessExitTrap(Mob *owner)
// and hard to reason about // and hard to reason about
void Aura::ProcessSpawns() void Aura::ProcessSpawns()
{ {
const auto &clients = entity_list.GetCloseMobList(this, distance); for (auto &e: GetCloseMobList(distance)) {
for (auto &e : clients) {
if (!e.second) { if (!e.second) {
continue; continue;
} }
+2 -12
View File
@@ -1578,17 +1578,7 @@ bool Bot::Process()
return false; return false;
} }
if (mob_close_scan_timer.Check()) { ScanCloseMobProcess();
LogAIScanCloseDetail(
"is_moving [{}] bot [{}] timer [{}]",
moving ? "true" : "false",
GetCleanName(),
mob_close_scan_timer.GetDuration()
);
entity_list.ScanCloseMobs(close_mobs, this, IsMoving());
}
SpellProcess(); SpellProcess();
if (tic_timer.Check()) { if (tic_timer.Check()) {
@@ -2978,7 +2968,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) {
if (NOT_HOLDING && NOT_PASSIVE) { if (NOT_HOLDING && NOT_PASSIVE) {
auto attack_target = bot_owner->GetTarget(); auto attack_target = bot_owner->GetTarget();
if (attack_target) { if (attack_target && HasBotAttackFlag(attack_target)) {
InterruptSpell(); InterruptSpell();
WipeHateList(); WipeHateList();
+5
View File
@@ -35,6 +35,11 @@ void bot_command_attack(Client *c, const Seperator *sep)
return; return;
} }
if (!c->HasBotAttackFlag(target_mob)) {
target_mob->SetBotAttackFlag(c->CharacterID());
target_mob->bot_attack_flag_timer.Start(10000);
}
size_t attacker_count = 0; size_t attacker_count = 0;
Bot *first_attacker = nullptr; Bot *first_attacker = nullptr;
sbl.remove(nullptr); sbl.remove(nullptr);
+11 -3
View File
@@ -857,7 +857,7 @@ void bot_command_spawn(Client *c, const Seperator *sep)
c->Message( c->Message(
Chat::White, Chat::White,
fmt::format( fmt::format(
"Usage: {} [bot_name]", "Usage: {} [bot_name] [optional: silent]",
sep->arg[0] sep->arg[0]
).c_str() ).c_str()
); );
@@ -1045,9 +1045,17 @@ void bot_command_spawn(Client *c, const Seperator *sep)
message_index = VALIDATECLASSID(my_bot->GetClass()); message_index = VALIDATECLASSID(my_bot->GetClass());
} }
if (c->GetBotOption(Client::booSpawnMessageSay)) { std::string silent_confirm = sep->arg[2];
bool silentTell = false;
if (!silent_confirm.compare("silent")) {
silentTell = true;
}
if (!silentTell && c->GetBotOption(Client::booSpawnMessageSay)) {
Bot::BotGroupSay(my_bot, bot_spawn_message[message_index].c_str()); Bot::BotGroupSay(my_bot, bot_spawn_message[message_index].c_str());
} else if (c->GetBotOption(Client::booSpawnMessageTell)) { }
else if (!silentTell && c->GetBotOption(Client::booSpawnMessageTell)) {
my_bot->OwnerMessage(bot_spawn_message[message_index]); my_bot->OwnerMessage(bot_spawn_message[message_index]);
} }
} }
+3 -1
View File
@@ -312,10 +312,12 @@ void Client::SpawnRaidBotsOnConnect(Raid* raid) {
for (const auto& m: r_members) { for (const auto& m: r_members) {
if (strlen(m.member_name) != 0) { if (strlen(m.member_name) != 0) {
for (const auto& b: bots_list) { for (const auto& b : bots_list) {
if (strcmp(m.member_name, b.bot_name) == 0) { if (strcmp(m.member_name, b.bot_name) == 0) {
std::string buffer = "^spawn "; std::string buffer = "^spawn ";
buffer.append(m.member_name); buffer.append(m.member_name);
std::string silent = " silent";
buffer.append(silent);
bot_command_real_dispatch(this, buffer.c_str()); bot_command_real_dispatch(this, buffer.c_str());
auto bot = entity_list.GetBotByBotName(m.member_name); auto bot = entity_list.GetBotByBotName(m.member_name);
+4 -3
View File
@@ -3324,7 +3324,8 @@ DBbotspells_Struct* ZoneDatabase::GetBotSpells(uint32 bot_spell_id)
if (!bse.empty()) { if (!bse.empty()) {
for (const auto& e : bse) { for (const auto& e : bse) {
DBbotspells_entries_Struct entry; DBbotspells_entries_Struct entry;
entry.spellid = e.spellid;
entry.spellid = e.spell_id;
entry.type = e.type; entry.type = e.type;
entry.minlevel = e.minlevel; entry.minlevel = e.minlevel;
entry.maxlevel = e.maxlevel; entry.maxlevel = e.maxlevel;
@@ -3345,8 +3346,8 @@ DBbotspells_Struct* ZoneDatabase::GetBotSpells(uint32 bot_spell_id)
if (e.resist_adjust) { if (e.resist_adjust) {
entry.resist_adjust = e.resist_adjust; entry.resist_adjust = e.resist_adjust;
} else if (IsValidSpell(e.spellid)) { } else if (IsValidSpell(e.spell_id)) {
entry.resist_adjust = spells[e.spellid].resist_difficulty; entry.resist_adjust = spells[e.spell_id].resist_difficulty;
} }
spell_set.entries.push_back(entry); spell_set.entries.push_back(entry);
+242 -114
View File
@@ -150,14 +150,14 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
process_timer(100), process_timer(100),
consume_food_timer(CONSUMPTION_TIMER), consume_food_timer(CONSUMPTION_TIMER),
zoneinpacket_timer(1000), zoneinpacket_timer(1000),
linkdead_timer(RuleI(Zone,ClientLinkdeadMS)), linkdead_timer(RuleI(Zone, ClientLinkdeadMS)),
dead_timer(2000), dead_timer(2000),
global_channel_timer(1000), global_channel_timer(1000),
fishing_timer(8000), fishing_timer(8000),
endupkeep_timer(1000), endupkeep_timer(1000),
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)), m_client_npc_aggro_scan_timer(RuleI(Aggro, ClientAggroCheckIdleInterval)),
client_zone_wide_full_position_update_timer(5 * 60 * 1000), m_client_zone_wide_full_position_update_timer(5 * 60 * 1000),
tribute_timer(Tribute_duration), tribute_timer(Tribute_duration),
proximity_timer(ClientProximity_interval), proximity_timer(ClientProximity_interval),
TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000),
@@ -176,16 +176,17 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
helm_toggle_timer(250), helm_toggle_timer(250),
aggro_meter_timer(AGGRO_METER_UPDATE_MS), aggro_meter_timer(AGGRO_METER_UPDATE_MS),
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f,-2.0f), m_ZoneSummonLocation(-2.0f, -2.0f, -2.0f, -2.0f),
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f), m_AutoAttackTargetLocation(0.0f, 0.0f, 0.0f),
last_region_type(RegionTypeUnsupported), last_region_type(RegionTypeUnsupported),
m_dirtyautohaters(false), m_dirtyautohaters(false),
mob_close_scan_timer(6000), m_position_update_timer(10000),
position_update_timer(10000),
consent_throttle_timer(2000), consent_throttle_timer(2000),
tmSitting(0), tmSitting(0),
parcel_timer(RuleI(Parcel, ParcelDeliveryDelay)) parcel_timer(RuleI(Parcel, ParcelDeliveryDelay)),
lazy_load_bank_check_timer(1000),
bandolier_throttle_timer(0)
{ {
for (auto client_filter = FilterNone; client_filter < _FilterCount; client_filter = eqFilterType(client_filter + 1)) { for (auto client_filter = FilterNone; client_filter < _FilterCount; client_filter = eqFilterType(client_filter + 1)) {
SetFilter(client_filter, FilterShow); SetFilter(client_filter, FilterShow);
@@ -284,6 +285,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
memset(&m_epp, 0, sizeof(m_epp)); memset(&m_epp, 0, sizeof(m_epp));
PendingTranslocate = false; PendingTranslocate = false;
PendingSacrifice = false; PendingSacrifice = false;
sacrifice_caster_id = 0;
controlling_boat_id = 0; controlling_boat_id = 0;
controlled_mob_id = 0; controlled_mob_id = 0;
qGlobals = nullptr; qGlobals = nullptr;
@@ -391,7 +393,6 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
SetBotPrecombat(false); SetBotPrecombat(false);
AI_Init(); AI_Init();
} }
Client::~Client() { Client::~Client() {
@@ -444,7 +445,7 @@ Client::~Client() {
m_tradeskill_object = nullptr; m_tradeskill_object = nullptr;
} }
close_mobs.clear(); m_close_mobs.clear();
if(IsDueling() && GetDuelTarget() != 0) { if(IsDueling() && GetDuelTarget() != 0) {
Entity* entity = entity_list.GetID(GetDuelTarget()); Entity* entity = entity_list.GetID(GetDuelTarget());
@@ -1242,45 +1243,28 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
entity_list.ProcessProximitySay(message, this, language); entity_list.ProcessProximitySay(message, this, language);
} }
if ( Mob* t = GetTarget();
GetTarget() &&
GetTarget()->IsNPC() &&
!IsInvisible(GetTarget())
) {
auto* t = GetTarget()->CastToNPC();
if (!t->IsEngaged()) {
CheckLDoNHail(t);
CheckEmoteHail(t, message);
if (DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) { if (
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_SAY)) { t &&
parse->EventNPC(EVENT_SAY, t, this, message, language); !IsInvisible(t) &&
DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)
) {
const bool is_engaged = t->IsEngaged();
if (is_engaged) {
parse->EventBotMercNPC(EVENT_AGGRO_SAY, t, this, [&]() { return message; }, language);
} else {
parse->EventBotMercNPC(EVENT_SAY, t, this, [&]() { return message; }, language);
} }
if (t->IsNPC() && !is_engaged) {
CheckLDoNHail(t->CastToNPC());
CheckEmoteHail(t->CastToNPC(), message);
if (RuleB(TaskSystem, EnableTaskSystem)) { if (RuleB(TaskSystem, EnableTaskSystem)) {
if (UpdateTasksOnSpeakWith(t)) { if (UpdateTasksOnSpeakWith(t->CastToNPC())) {
t->DoQuestPause(this); t->CastToNPC()->DoQuestPause(this);
}
}
}
} else {
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_AGGRO_SAY)) {
if (DistanceSquaredNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) {
parse->EventNPC(EVENT_AGGRO_SAY, t, this, message, language);
}
}
}
}
else if (GetTarget() && GetTarget()->IsBot() && !IsInvisible(GetTarget())) {
if (DistanceNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) {
if (GetTarget()->IsEngaged()) {
if (parse->BotHasQuestSub(EVENT_AGGRO_SAY)) {
parse->EventBot(EVENT_AGGRO_SAY, GetTarget()->CastToBot(), this, message, language);
}
} else {
if (parse->BotHasQuestSub(EVENT_SAY)) {
parse->EventBot(EVENT_SAY, GetTarget()->CastToBot(), this, message, language);
} }
} }
} }
@@ -2298,52 +2282,67 @@ void Client::SetGM(bool toggle) {
UpdateWho(); UpdateWho();
} }
void Client::ReadBook(BookRequest_Struct *book) { void Client::ReadBook(BookRequest_Struct* book)
int16 book_language=0; {
char *txtfile = book->txtfile; const std::string& text_file = book->txtfile;
if(txtfile[0] == '0' && txtfile[1] == '\0') { if (text_file.empty()) {
//invalid book... coming up on non-book items.
return; return;
} }
std::string booktxt2 = content_db.GetBook(txtfile, &book_language); auto b = content_db.GetBook(text_file);
int length = booktxt2.length();
if (booktxt2[0] != '\0') { if (!b.text.empty()) {
#if EQDEBUG >= 6 auto outapp = new EQApplicationPacket(OP_ReadBook, b.text.size() + sizeof(BookText_Struct));
LogInfo("Client::ReadBook() textfile:[{}] Text:[{}]", txtfile, booktxt2.c_str()); auto inst = const_cast<EQ::ItemInstance*>(m_inv[book->invslot]);
#endif
auto outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct));
BookText_Struct *out = (BookText_Struct *) outapp->pBuffer; auto t = (BookText_Struct*) outapp->pBuffer;
out->window = book->window;
out->type = book->type;
out->invslot = book->invslot;
out->target_id = book->target_id;
out->can_cast = 0; // todo: implement
out->can_scribe = false;
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && book->invslot <= EQ::invbag::GENERAL_BAGS_END) t->window = book->window;
{ t->type = book->type;
const EQ::ItemInstance* inst = m_inv[book->invslot]; t->invslot = book->invslot;
if (inst && inst->GetItem()) t->target_id = book->target_id;
{ t->can_cast = 0; // todo: implement
auto recipe = TradeskillRecipeRepository::GetWhere(content_db, t->can_scribe = false;
fmt::format("learned_by_item_id = {} LIMIT 1", inst->GetItem()->ID));
out->type = inst->GetItem()->Book; if (ClientVersion() >= EQ::versions::ClientVersion::SoF && book->invslot <= EQ::invbag::GENERAL_BAGS_END) {
out->can_scribe = !recipe.empty(); if (inst && inst->GetItem()) {
auto recipe = TradeskillRecipeRepository::GetWhere(
content_db,
fmt::format(
"learned_by_item_id = {} LIMIT 1",
inst->GetItem()->ID
)
);
t->type = inst->GetItem()->Book;
t->can_scribe = !recipe.empty();
} }
} }
memcpy(out->booktext, booktxt2.c_str(), length); memcpy(t->booktext, b.text.c_str(), b.text.size());
if (EQ::ValueWithin(book_language, Language::CommonTongue, Language::Unknown27)) { if (EQ::ValueWithin(b.language, Language::CommonTongue, Language::Unknown27)) {
if (m_pp.languages[book_language] < Language::MaxValue) { if (m_pp.languages[b.language] < Language::MaxValue) {
GarbleMessage(out->booktext, (Language::MaxValue - m_pp.languages[book_language])); GarbleMessage(t->booktext, (Language::MaxValue - m_pp.languages[b.language]));
} }
} }
// Send only books and scrolls to this event
if (parse->PlayerHasQuestSub(EVENT_READ_ITEM) && t->type != BookType::ItemInfo) {
std::vector<std::any> args = {
b.text,
t->can_cast,
t->can_scribe,
t->invslot,
t->target_id,
t->type,
inst
};
parse->EventPlayer(EVENT_READ_ITEM, this, book->txtfile, inst ? inst->GetID() : 0, &args);
}
QueuePacket(outapp); QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
} }
@@ -3403,6 +3402,11 @@ void Client::ServerFilter(SetServerFilter_Struct* filter){
} else { // these clients don't have a 'self only' filter } else { // these clients don't have a 'self only' filter
Filter1(FilterHealOverTime); Filter1(FilterHealOverTime);
} }
Filter1(FilterItemSpeech);
Filter1(FilterStrikethrough);
Filter1(FilterStuns);
Filter1(FilterBardSongsOnPets);
} }
// this version is for messages with no parameters // this version is for messages with no parameters
@@ -3965,7 +3969,7 @@ void Client::SetEndurance(int32 newEnd)
CheckManaEndUpdate(); CheckManaEndUpdate();
} }
void Client::SacrificeConfirm(Client *caster) void Client::SacrificeConfirm(Mob *caster)
{ {
auto outapp = new EQApplicationPacket(OP_Sacrifice, sizeof(Sacrifice_Struct)); auto outapp = new EQApplicationPacket(OP_Sacrifice, sizeof(Sacrifice_Struct));
Sacrifice_Struct *ss = (Sacrifice_Struct *)outapp->pBuffer; Sacrifice_Struct *ss = (Sacrifice_Struct *)outapp->pBuffer;
@@ -3992,14 +3996,14 @@ void Client::SacrificeConfirm(Client *caster)
ss->Confirm = 0; ss->Confirm = 0;
QueuePacket(outapp); QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
// We store the Caster's name, because when the packet comes back, it only has the victim's entityID in it, // We store the Caster's id, because when the packet comes back, it only has the victim's entityID in it,
// not the caster. // not the caster.
SacrificeCaster += caster->GetName(); sacrifice_caster_id = caster->GetID();
PendingSacrifice = true; PendingSacrifice = true;
} }
//Essentially a special case death function //Essentially a special case death function
void Client::Sacrifice(Client *caster) void Client::Sacrifice(Mob *caster)
{ {
if (GetLevel() >= RuleI(Spells, SacrificeMinLevel) && GetLevel() <= RuleI(Spells, SacrificeMaxLevel)) { if (GetLevel() >= RuleI(Spells, SacrificeMinLevel) && GetLevel() <= RuleI(Spells, SacrificeMaxLevel)) {
int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000); int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000);
@@ -4047,8 +4051,11 @@ void Client::Sacrifice(Client *caster)
} }
Save(); Save();
GoToDeath(); GoToDeath();
if (caster) // I guess it's possible? if (caster && caster->IsClient()) {
caster->SummonItem(RuleI(Spells, SacrificeItemID)); caster->CastToClient()->SummonItem(RuleI(Spells, SacrificeItemID));
} else if (caster && caster->IsNPC()) {
caster->CastToNPC()->AddItem(RuleI(Spells, SacrificeItemID), 1, false);
}
} }
} else { } else {
caster->MessageString(Chat::Red, SAC_TOO_LOW); // This being is not a worthy sacrifice. caster->MessageString(Chat::Red, SAC_TOO_LOW); // This being is not a worthy sacrifice.
@@ -7649,10 +7656,10 @@ void Client::SetFactionLevel(
content_db.GetFactionData(&faction_modifiers, class_id, race_id, deity_id, e.faction_id); content_db.GetFactionData(&faction_modifiers, class_id, race_id, deity_id, e.faction_id);
if (is_quest) { if (is_quest) {
if (e.npc_value > 0) { if (e.value > 0) {
e.npc_value = -std::abs(e.npc_value); e.value = -std::abs(e.value);
} else if (e.npc_value < 0) { } else if (e.value < 0) {
e.npc_value = std::abs(e.npc_value); e.value = std::abs(e.value);
} }
} }
@@ -9243,19 +9250,6 @@ bool Client::GotoPlayerRaid(const std::string& player_name)
return true; return true;
} }
glm::vec4 &Client::GetLastPositionBeforeBulkUpdate()
{
return last_position_before_bulk_update;
}
/**
* @param in_last_position_before_bulk_update
*/
void Client::SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update)
{
Client::last_position_before_bulk_update = in_last_position_before_bulk_update;
}
void Client::SendToGuildHall() void Client::SendToGuildHall()
{ {
std::string zone_short_name = "guildhall"; std::string zone_short_name = "guildhall";
@@ -10920,23 +10914,24 @@ void Client::SetIPExemption(int exemption_amount)
void Client::ReadBookByName(std::string book_name, uint8 book_type) void Client::ReadBookByName(std::string book_name, uint8 book_type)
{ {
int16 book_language = 0; auto b = content_db.GetBook(book_name);
std::string book_text = content_db.GetBook(book_name.c_str(), &book_language);
int length = book_text.length();
if (book_text[0] != '\0') { if (!b.text.empty()) {
LogDebug("Client::ReadBookByName() Book Name: [{}] Text: [{}]", book_name, book_text.c_str()); LogDebug("Book Name: [{}] Text: [{}]", book_name, b.text);
auto outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct));
BookText_Struct *out = (BookText_Struct *) outapp->pBuffer;
out->window = 0xFF;
out->type = book_type;
out->invslot = 0;
memcpy(out->booktext, book_text.c_str(), length); auto outapp = new EQApplicationPacket(OP_ReadBook, b.text.size() + sizeof(BookText_Struct));
if (EQ::ValueWithin(book_language, Language::CommonTongue, Language::Unknown27)) { auto o = (BookText_Struct *) outapp->pBuffer;
if (m_pp.languages[book_language] < Language::MaxValue) {
GarbleMessage(out->booktext, (Language::MaxValue - m_pp.languages[book_language])); o->window = std::numeric_limits<uint8>::max();
o->type = book_type;
o->invslot = 0;
memcpy(o->booktext, b.text.c_str(), b.text.size());
if (EQ::ValueWithin(b.language, Language::CommonTongue, Language::Unknown27)) {
if (m_pp.languages[b.language] < Language::MaxValue) {
GarbleMessage(o->booktext, (Language::MaxValue - m_pp.languages[b.language]));
} }
} }
@@ -11087,7 +11082,9 @@ void Client::SaveDisciplines()
{ {
std::vector<CharacterDisciplinesRepository::CharacterDisciplines> v; std::vector<CharacterDisciplinesRepository::CharacterDisciplines> v;
for (int slot_id = 0; slot_id < MAX_PP_DISCIPLINES; slot_id++) { std::vector<std::string> delete_slots;
for (uint16 slot_id = 0; slot_id < MAX_PP_DISCIPLINES; slot_id++) {
if (IsValidSpell(m_pp.disciplines.values[slot_id])) { if (IsValidSpell(m_pp.disciplines.values[slot_id])) {
auto e = CharacterDisciplinesRepository::NewEntity(); auto e = CharacterDisciplinesRepository::NewEntity();
@@ -11096,9 +11093,22 @@ void Client::SaveDisciplines()
e.disc_id = m_pp.disciplines.values[slot_id]; e.disc_id = m_pp.disciplines.values[slot_id];
v.emplace_back(e); v.emplace_back(e);
} else {
delete_slots.emplace_back(std::to_string(slot_id));
} }
} }
if (!delete_slots.empty()) {
CharacterDisciplinesRepository::DeleteWhere(
database,
fmt::format(
"`id` = {} AND `slot_id` IN ({})",
CharacterID(),
Strings::Join(delete_slots, ", ")
)
);
}
if (!v.empty()) { if (!v.empty()) {
CharacterDisciplinesRepository::ReplaceMany(database, v); CharacterDisciplinesRepository::ReplaceMany(database, v);
} }
@@ -12701,3 +12711,121 @@ bool Client::TakeMoneyFromPPWithOverFlow(uint64 copper, bool update_client)
RecalcWeight(); RecalcWeight();
return true; return true;
} }
void Client::SendTopLevelInventory()
{
EQ::ItemInstance* inst = nullptr;
static const int16 slots[][2] = {
{ EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END },
{ EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END },
{ EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END }
};
const auto& inv = GetInv();
const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]);
for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) {
for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) {
inst = inv.GetItem(slot_id);
if (inst) {
SendItemPacket(slot_id, inst, ItemPacketType::ItemPacketTrade);
}
}
}
}
// On a normal basis we limit mob movement updates based on distance
// This ensures we send a periodic full zone update to a client that has started moving after 5 or so minutes
//
// For very large zones we will also force a full update based on distance
//
// We ignore a small distance around us so that we don't interrupt already pathing deltas as those npcs will appear
// to full stop when they are actually still pathing
void Client::CheckSendBulkClientPositionUpdate()
{
float distance_moved = DistanceNoZ(m_last_position_before_bulk_update, GetPosition());
bool moved_far_enough_before_bulk_update = distance_moved >= zone->GetNpcPositionUpdateDistance();
bool is_ready_to_update = (
m_client_zone_wide_full_position_update_timer.Check() || moved_far_enough_before_bulk_update
);
if (IsMoving() && is_ready_to_update) {
LogDebug("[[{}]] Client Zone Wide Position Update NPCs", GetCleanName());
auto &mob_movement_manager = MobMovementManager::Get();
auto &mob_list = entity_list.GetMobList();
for (auto &it : mob_list) {
Mob *entity = it.second;
if (!entity->IsNPC()) {
continue;
}
int animation_speed = 0;
if (entity->IsMoving()) {
if (entity->IsRunning()) {
animation_speed = (entity->IsFeared() ? entity->GetFearSpeed() : entity->GetRunspeed());
}
else {
animation_speed = entity->GetWalkspeed();
}
}
mob_movement_manager.SendCommandToClients(entity, 0.0, 0.0, 0.0, 0.0, animation_speed, ClientRangeAny, this);
}
m_last_position_before_bulk_update = GetPosition();
}
}
const uint16 scan_npc_aggro_timer_idle = RuleI(Aggro, ClientAggroCheckIdleInterval);
const uint16 scan_npc_aggro_timer_moving = RuleI(Aggro, ClientAggroCheckMovingInterval);
void Client::CheckClientToNpcAggroTimer()
{
LogAggroDetail(
"ClientUpdate [{}] {}moving, scan timer [{}]",
GetCleanName(),
IsMoving() ? "" : "NOT ",
m_client_npc_aggro_scan_timer.GetRemainingTime()
);
if (IsMoving()) {
if (m_client_npc_aggro_scan_timer.GetRemainingTime() > scan_npc_aggro_timer_moving) {
LogAggroDetail("Client [{}] Restarting with moving timer", GetCleanName());
m_client_npc_aggro_scan_timer.Disable();
m_client_npc_aggro_scan_timer.Start(scan_npc_aggro_timer_moving);
m_client_npc_aggro_scan_timer.Trigger();
}
}
else if (m_client_npc_aggro_scan_timer.GetDuration() == scan_npc_aggro_timer_moving) {
LogAggroDetail("Client [{}] Restarting with idle timer", GetCleanName());
m_client_npc_aggro_scan_timer.Disable();
m_client_npc_aggro_scan_timer.Start(scan_npc_aggro_timer_idle);
}
}
void Client::ClientToNpcAggroProcess()
{
if (zone->CanDoCombat() && !GetFeigned() && m_client_npc_aggro_scan_timer.Check()) {
int npc_scan_count = 0;
for (auto &close_mob: GetCloseMobList()) {
Mob *mob = close_mob.second;
if (!mob) {
continue;
}
if (mob->IsClient()) {
continue;
}
if (mob->CheckWillAggro(this) && !mob->CheckAggro(this)) {
mob->AddToHateList(this, 25);
}
npc_scan_count++;
}
LogAggro("Checking Reverse Aggro (client->npc) scanned_npcs ([{}])", npc_scan_count);
}
}
+29 -15
View File
@@ -196,6 +196,7 @@ struct RespawnOption
float heading; float heading;
}; };
// do not ask what all these mean because I have no idea! // do not ask what all these mean because I have no idea!
// named from the client's CEverQuest::GetInnateDesc, they're missing some // named from the client's CEverQuest::GetInnateDesc, they're missing some
enum eInnateSkill { enum eInnateSkill {
@@ -596,7 +597,7 @@ public:
inline uint8 GetBaseINT() const { return m_pp.INT; } inline uint8 GetBaseINT() const { return m_pp.INT; }
inline uint8 GetBaseAGI() const { return m_pp.AGI; } inline uint8 GetBaseAGI() const { return m_pp.AGI; }
inline uint8 GetBaseWIS() const { return m_pp.WIS; } inline uint8 GetBaseWIS() const { return m_pp.WIS; }
inline uint8 GetBaseCorrup() const { return 15; } // Same for all inline uint8 GetBaseCorrup() const { return m_pp.corruption_resist; }
inline uint8 GetBasePhR() const { return 0; } // Guessing at 0 as base inline uint8 GetBasePhR() const { return 0; } // Guessing at 0 as base
inline virtual int32 GetHeroicSTR() const { return itembonuses.HeroicSTR; } inline virtual int32 GetHeroicSTR() const { return itembonuses.HeroicSTR; }
@@ -762,8 +763,8 @@ public:
void GetRaidAAs(RaidLeadershipAA_Struct *into) const; void GetRaidAAs(RaidLeadershipAA_Struct *into) const;
void ClearGroupAAs(); void ClearGroupAAs();
void UpdateGroupAAs(int32 points, uint32 type); void UpdateGroupAAs(int32 points, uint32 type);
void SacrificeConfirm(Client* caster); void SacrificeConfirm(Mob* caster);
void Sacrifice(Client* caster); void Sacrifice(Mob* caster);
void GoToDeath(); void GoToDeath();
inline const int32 GetInstanceID() const { return zone->GetInstanceID(); } inline const int32 GetInstanceID() const { return zone->GetInstanceID(); }
void SetZoning(bool in) { bZoning = in; } void SetZoning(bool in) { bZoning = in; }
@@ -1025,7 +1026,7 @@ public:
int GetSpentAA() { return m_pp.aapoints_spent; } int GetSpentAA() { return m_pp.aapoints_spent; }
uint32 GetRequiredAAExperience(); uint32 GetRequiredAAExperience();
void AutoGrantAAPoints(); void AutoGrantAAPoints();
void GrantAllAAPoints(uint8 unlock_level = 0); void GrantAllAAPoints(uint8 unlock_level = 0, bool skip_grant_only = false);
bool HasAlreadyPurchasedRank(AA::Rank* rank); bool HasAlreadyPurchasedRank(AA::Rank* rank);
void ListPurchasedAAs(Client *to, std::string search_criteria = std::string()); void ListPurchasedAAs(Client *to, std::string search_criteria = std::string());
@@ -1245,7 +1246,7 @@ public:
bool PendingTranslocate; bool PendingTranslocate;
time_t TranslocateTime; time_t TranslocateTime;
bool PendingSacrifice; bool PendingSacrifice;
std::string SacrificeCaster; uint16 sacrifice_caster_id;
PendingTranslocate_Struct PendingTranslocateData; PendingTranslocate_Struct PendingTranslocateData;
void SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID); void SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID);
@@ -1420,7 +1421,11 @@ public:
{ {
return (task_state ? task_state->EnabledTaskCount(task_set_id) : -1); return (task_state ? task_state->EnabledTaskCount(task_set_id) : -1);
} }
inline int IsTaskCompleted(int task_id) { return (task_state ? task_state->IsTaskCompleted(task_id) : -1); } inline bool IsTaskCompleted(int task_id) { return (task_state ? task_state->IsTaskCompleted(task_id) : false); }
inline bool AreTasksCompleted(std::vector<int> task_ids)
{
return (task_state ? task_state->AreTasksCompleted(task_ids) : false);
}
inline void ShowClientTasks(Client *client) { if (task_state) { task_state->ShowClientTasks(this, client); }} inline void ShowClientTasks(Client *client) { if (task_state) { task_state->ShowClientTasks(this, client); }}
inline void CancelAllTasks() { if (task_state) { task_state->CancelAllTasks(this); }} inline void CancelAllTasks() { if (task_state) { task_state->CancelAllTasks(this); }}
inline int GetActiveTaskCount() { return (task_state ? task_state->GetActiveTaskCount() : 0); } inline int GetActiveTaskCount() { return (task_state ? task_state->GetActiveTaskCount() : 0); }
@@ -1790,9 +1795,6 @@ public:
uint32 trapid; //ID of trap player has triggered. This is cleared when the player leaves the trap's radius, or it despawns. uint32 trapid; //ID of trap player has triggered. This is cleared when the player leaves the trap's radius, or it despawns.
void SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update);
glm::vec4 &GetLastPositionBeforeBulkUpdate();
Raid *p_raid_instance; Raid *p_raid_instance;
void ShowDevToolsMenu(); void ShowDevToolsMenu();
@@ -2008,6 +2010,8 @@ private:
void ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm); void ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm);
void ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); void ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited);
void SendTopLevelInventory();
glm::vec4 m_ZoneSummonLocation; glm::vec4 m_ZoneSummonLocation;
uint16 zonesummon_id; uint16 zonesummon_id;
uint8 zonesummon_ignorerestrictions; uint8 zonesummon_ignorerestrictions;
@@ -2027,8 +2031,6 @@ private:
Timer fishing_timer; Timer fishing_timer;
Timer endupkeep_timer; Timer endupkeep_timer;
Timer autosave_timer; Timer autosave_timer;
Timer client_scan_npc_aggro_timer;
Timer client_zone_wide_full_position_update_timer;
Timer tribute_timer; Timer tribute_timer;
Timer proximity_timer; Timer proximity_timer;
@@ -2045,16 +2047,29 @@ private:
Timer afk_toggle_timer; Timer afk_toggle_timer;
Timer helm_toggle_timer; Timer helm_toggle_timer;
Timer aggro_meter_timer; Timer aggro_meter_timer;
Timer mob_close_scan_timer;
Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */
Timer consent_throttle_timer; Timer consent_throttle_timer;
Timer dynamiczone_removal_timer; Timer dynamiczone_removal_timer;
Timer task_request_timer; Timer task_request_timer;
Timer pick_lock_timer; Timer pick_lock_timer;
Timer parcel_timer; //Used to limit the number of parcels to one every 30 seconds (default). Changable via rule. Timer parcel_timer; //Used to limit the number of parcels to one every 30 seconds (default). Changable via rule.
Timer lazy_load_bank_check_timer;
Timer bandolier_throttle_timer;
bool m_lazy_load_bank = false;
int m_lazy_load_sent_bank_slots = 0;
glm::vec3 m_Proximity; glm::vec3 m_Proximity;
glm::vec4 last_position_before_bulk_update;
// client aggro
Timer m_client_npc_aggro_scan_timer;
void CheckClientToNpcAggroTimer();
void ClientToNpcAggroProcess();
// bulk position updates
glm::vec4 m_last_position_before_bulk_update;
Timer m_client_zone_wide_full_position_update_timer;
Timer m_position_update_timer;
void CheckSendBulkClientPositionUpdate();
void BulkSendInventoryItems(); void BulkSendInventoryItems();
@@ -2171,7 +2186,6 @@ private:
bool m_has_quest_compass = false; bool m_has_quest_compass = false;
std::vector<uint32_t> m_dynamic_zone_ids; std::vector<uint32_t> m_dynamic_zone_ids;
public: public:
enum BotOwnerOption : size_t { enum BotOwnerOption : size_t {
booDeathMarquee, booDeathMarquee,
+5 -295
View File
@@ -1009,65 +1009,7 @@ int Client::CalcHaste()
//in Mob::ResistSpell //in Mob::ResistSpell
int32 Client::CalcMR() int32 Client::CalcMR()
{ {
//racial bases MR = m_pp.magic_resist;
switch (GetBaseRace()) {
case HUMAN:
MR = 25;
break;
case BARBARIAN:
MR = 25;
break;
case ERUDITE:
MR = 30;
break;
case WOOD_ELF:
MR = 25;
break;
case HIGH_ELF:
MR = 25;
break;
case DARK_ELF:
MR = 25;
break;
case HALF_ELF:
MR = 25;
break;
case DWARF:
MR = 30;
break;
case TROLL:
MR = 25;
break;
case OGRE:
MR = 25;
break;
case HALFLING:
MR = 25;
break;
case GNOME:
MR = 25;
break;
case IKSAR:
MR = 25;
break;
case VAHSHIR:
MR = 25;
break;
case FROGLOK:
MR = 30;
break;
case DRAKKIN:
{
MR = 25;
if (GetDrakkinHeritage() == 2)
MR += 10;
else if (GetDrakkinHeritage() == 5)
MR += 2;
break;
}
default:
MR = 20;
}
MR += itembonuses.MR + spellbonuses.MR + aabonuses.MR; MR += itembonuses.MR + spellbonuses.MR + aabonuses.MR;
if (GetClass() == Class::Warrior || GetClass() == Class::Berserker) { if (GetClass() == Class::Warrior || GetClass() == Class::Berserker) {
MR += GetLevel() / 2; MR += GetLevel() / 2;
@@ -1083,65 +1025,7 @@ int32 Client::CalcMR()
int32 Client::CalcFR() int32 Client::CalcFR()
{ {
//racial bases FR = m_pp.fire_resist;
switch (GetBaseRace()) {
case HUMAN:
FR = 25;
break;
case BARBARIAN:
FR = 25;
break;
case ERUDITE:
FR = 25;
break;
case WOOD_ELF:
FR = 25;
break;
case HIGH_ELF:
FR = 25;
break;
case DARK_ELF:
FR = 25;
break;
case HALF_ELF:
FR = 25;
break;
case DWARF:
FR = 25;
break;
case TROLL:
FR = 5;
break;
case OGRE:
FR = 25;
break;
case HALFLING:
FR = 25;
break;
case GNOME:
FR = 25;
break;
case IKSAR:
FR = 30;
break;
case VAHSHIR:
FR = 25;
break;
case FROGLOK:
FR = 25;
break;
case DRAKKIN:
{
FR = 25;
if (GetDrakkinHeritage() == 0)
FR += 10;
else if (GetDrakkinHeritage() == 5)
FR += 2;
break;
}
default:
FR = 20;
}
int c = GetClass(); int c = GetClass();
if (c == Class::Ranger) { if (c == Class::Ranger) {
FR += 4; FR += 4;
@@ -1169,65 +1053,7 @@ int32 Client::CalcFR()
int32 Client::CalcDR() int32 Client::CalcDR()
{ {
//racial bases DR = m_pp.disease_resist;
switch (GetBaseRace()) {
case HUMAN:
DR = 15;
break;
case BARBARIAN:
DR = 15;
break;
case ERUDITE:
DR = 10;
break;
case WOOD_ELF:
DR = 15;
break;
case HIGH_ELF:
DR = 15;
break;
case DARK_ELF:
DR = 15;
break;
case HALF_ELF:
DR = 15;
break;
case DWARF:
DR = 15;
break;
case TROLL:
DR = 15;
break;
case OGRE:
DR = 15;
break;
case HALFLING:
DR = 20;
break;
case GNOME:
DR = 15;
break;
case IKSAR:
DR = 15;
break;
case VAHSHIR:
DR = 15;
break;
case FROGLOK:
DR = 15;
break;
case DRAKKIN:
{
DR = 15;
if (GetDrakkinHeritage() == 1)
DR += 10;
else if (GetDrakkinHeritage() == 5)
DR += 2;
break;
}
default:
DR = 15;
}
int c = GetClass(); int c = GetClass();
// the monk one is part of base resist // the monk one is part of base resist
if (c == Class::Monk) { if (c == Class::Monk) {
@@ -1261,65 +1087,7 @@ int32 Client::CalcDR()
int32 Client::CalcPR() int32 Client::CalcPR()
{ {
//racial bases PR = m_pp.poison_resist;
switch (GetBaseRace()) {
case HUMAN:
PR = 15;
break;
case BARBARIAN:
PR = 15;
break;
case ERUDITE:
PR = 15;
break;
case WOOD_ELF:
PR = 15;
break;
case HIGH_ELF:
PR = 15;
break;
case DARK_ELF:
PR = 15;
break;
case HALF_ELF:
PR = 15;
break;
case DWARF:
PR = 20;
break;
case TROLL:
PR = 15;
break;
case OGRE:
PR = 15;
break;
case HALFLING:
PR = 20;
break;
case GNOME:
PR = 15;
break;
case IKSAR:
PR = 15;
break;
case VAHSHIR:
PR = 15;
break;
case FROGLOK:
PR = 30;
break;
case DRAKKIN:
{
PR = 15;
if (GetDrakkinHeritage() == 3)
PR += 10;
else if (GetDrakkinHeritage() == 5)
PR += 2;
break;
}
default:
PR = 15;
}
int c = GetClass(); int c = GetClass();
// this monk bonus is part of the base // this monk bonus is part of the base
if (c == Class::Monk) { if (c == Class::Monk) {
@@ -1353,65 +1121,7 @@ int32 Client::CalcPR()
int32 Client::CalcCR() int32 Client::CalcCR()
{ {
//racial bases CR = m_pp.cold_resist;
switch (GetBaseRace()) {
case HUMAN:
CR = 25;
break;
case BARBARIAN:
CR = 35;
break;
case ERUDITE:
CR = 25;
break;
case WOOD_ELF:
CR = 25;
break;
case HIGH_ELF:
CR = 25;
break;
case DARK_ELF:
CR = 25;
break;
case HALF_ELF:
CR = 25;
break;
case DWARF:
CR = 25;
break;
case TROLL:
CR = 25;
break;
case OGRE:
CR = 25;
break;
case HALFLING:
CR = 25;
break;
case GNOME:
CR = 25;
break;
case IKSAR:
CR = 15;
break;
case VAHSHIR:
CR = 25;
break;
case FROGLOK:
CR = 25;
break;
case DRAKKIN:
{
CR = 25;
if (GetDrakkinHeritage() == 4)
CR += 10;
else if (GetDrakkinHeritage() == 5)
CR += 2;
break;
}
default:
CR = 25;
}
int c = GetClass(); int c = GetClass();
if (c == Class::Ranger || c == Class::Beastlord) { if (c == Class::Ranger || c == Class::Beastlord) {
CR += 4; CR += 4;
+41 -124
View File
@@ -794,7 +794,7 @@ void Client::CompleteConnect()
// sent to a succor point // sent to a succor point
SendMobPositions(); SendMobPositions();
SetLastPositionBeforeBulkUpdate(GetPosition()); m_last_position_before_bulk_update = GetPosition();
/* This sub event is for if a player logs in for the first time since entering world. */ /* This sub event is for if a player logs in for the first time since entering world. */
if (firstlogon == 1) { if (firstlogon == 1) {
@@ -934,12 +934,13 @@ void Client::CompleteConnect()
} }
database.LoadAuras(this); // this ends up spawning them so probably safer to load this later (here) database.LoadAuras(this); // this ends up spawning them so probably safer to load this later (here)
database.LoadCharacterDisciplines(this);
entity_list.RefreshClientXTargets(this); entity_list.RefreshClientXTargets(this);
worldserver.RequestTellQueue(GetName()); worldserver.RequestTellQueue(GetName());
entity_list.ScanCloseMobs(close_mobs, this, true); entity_list.ScanCloseMobs(this);
if (GetGM() && IsDevToolsEnabled()) { if (GetGM() && IsDevToolsEnabled()) {
ShowDevToolsMenu(); ShowDevToolsMenu();
@@ -1318,7 +1319,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
database.LoadCharacterInspectMessage(cid, &m_inspect_message); /* Load Character Inspect Message */ database.LoadCharacterInspectMessage(cid, &m_inspect_message); /* Load Character Inspect Message */
database.LoadCharacterSpellBook(cid, &m_pp); /* Load Character Spell Book */ database.LoadCharacterSpellBook(cid, &m_pp); /* Load Character Spell Book */
database.LoadCharacterMemmedSpells(cid, &m_pp); /* Load Character Memorized Spells */ database.LoadCharacterMemmedSpells(cid, &m_pp); /* Load Character Memorized Spells */
database.LoadCharacterDisciplines(cid, &m_pp); /* Load Character Disciplines */
database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */ database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */
database.LoadCharacterLeadershipAbilities(cid, &m_pp); /* Load Character Leadership AA's */ database.LoadCharacterLeadershipAbilities(cid, &m_pp); /* Load Character Leadership AA's */
database.LoadCharacterTribute(this); /* Load CharacterTribute */ database.LoadCharacterTribute(this); /* Load CharacterTribute */
@@ -3285,6 +3285,10 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
} }
} }
if (new_aug->GetItem()->Attuneable) {
new_aug->SetAttuned(true);
}
tobe_auged->PutAugment(in_augment->augment_index, *new_aug); tobe_auged->PutAugment(in_augment->augment_index, *new_aug);
tobe_auged->UpdateOrnamentationInfo(); tobe_auged->UpdateOrnamentationInfo();
@@ -3638,17 +3642,15 @@ void Client::Handle_OP_AutoFire(const EQApplicationPacket *app)
void Client::Handle_OP_Bandolier(const EQApplicationPacket *app) void Client::Handle_OP_Bandolier(const EQApplicationPacket *app)
{ {
// Although there are three different structs for OP_Bandolier, they are all the same size. // Although there are three different structs for OP_Bandolier, they are all the same size.
//
if (app->size != sizeof(BandolierCreate_Struct)) { if (app->size != sizeof(BandolierCreate_Struct)) {
LogDebug("Size mismatch in OP_Bandolier expected [{}] got [{}]", sizeof(BandolierCreate_Struct), app->size); LogDebug("Size mismatch in OP_Bandolier expected [{}] got [{}]", sizeof(BandolierCreate_Struct), app->size);
DumpPacket(app); DumpPacket(app);
return; return;
} }
BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; auto bs = (BandolierCreate_Struct*) app->pBuffer;
switch (bs->Action) switch (bs->Action) {
{
case bandolierCreate: case bandolierCreate:
CreateBandolier(app); CreateBandolier(app);
break; break;
@@ -3656,6 +3658,18 @@ void Client::Handle_OP_Bandolier(const EQApplicationPacket *app)
RemoveBandolier(app); RemoveBandolier(app);
break; break;
case bandolierSet: case bandolierSet:
if (bandolier_throttle_timer.GetDuration() && !bandolier_throttle_timer.Check()) {
Message(
Chat::White,
fmt::format(
"You may only modify your bandolier once every {}.",
Strings::ToLower(Strings::MillisecondsToTime(RuleI(Character, BandolierSwapDelay)))
).c_str()
);
SendTopLevelInventory();
break;
}
SetBandolier(app); SetBandolier(app);
break; break;
default: default:
@@ -4998,103 +5012,9 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
SetMoving(!(cy == m_Position.y && cx == m_Position.x)); SetMoving(!(cy == m_Position.y && cx == m_Position.x));
/** CheckClientToNpcAggroTimer();
* Client aggro scanning CheckScanCloseMobsMovingTimer();
*/ CheckSendBulkClientPositionUpdate();
const uint16 client_scan_npc_aggro_timer_idle = RuleI(Aggro, ClientAggroCheckIdleInterval);
const uint16 client_scan_npc_aggro_timer_moving = RuleI(Aggro, ClientAggroCheckMovingInterval);
LogAggroDetail(
"ClientUpdate [{}] {}moving, scan timer [{}]",
GetCleanName(),
IsMoving() ? "" : "NOT ",
client_scan_npc_aggro_timer.GetRemainingTime()
);
if (IsMoving()) {
if (client_scan_npc_aggro_timer.GetRemainingTime() > client_scan_npc_aggro_timer_moving) {
LogAggroDetail("Client [{}] Restarting with moving timer", GetCleanName());
client_scan_npc_aggro_timer.Disable();
client_scan_npc_aggro_timer.Start(client_scan_npc_aggro_timer_moving);
client_scan_npc_aggro_timer.Trigger();
}
}
else if (client_scan_npc_aggro_timer.GetDuration() == client_scan_npc_aggro_timer_moving) {
LogAggroDetail("Client [{}] Restarting with idle timer", GetCleanName());
client_scan_npc_aggro_timer.Disable();
client_scan_npc_aggro_timer.Start(client_scan_npc_aggro_timer_idle);
}
/**
* Client mob close list cache scan timer
*/
const uint16 client_mob_close_scan_timer_moving = 6000;
const uint16 client_mob_close_scan_timer_idle = 60000;
LogAIScanCloseDetail(
"Client [{}] {}moving, scan timer [{}]",
GetCleanName(),
IsMoving() ? "" : "NOT ",
mob_close_scan_timer.GetRemainingTime()
);
if (IsMoving()) {
if (mob_close_scan_timer.GetRemainingTime() > client_mob_close_scan_timer_moving) {
LogAIScanCloseDetail("Client [{}] Restarting with moving timer", GetCleanName());
mob_close_scan_timer.Disable();
mob_close_scan_timer.Start(client_mob_close_scan_timer_moving);
mob_close_scan_timer.Trigger();
}
}
else if (mob_close_scan_timer.GetDuration() == client_mob_close_scan_timer_moving) {
LogAIScanCloseDetail("Client [{}] Restarting with idle timer", GetCleanName());
mob_close_scan_timer.Disable();
mob_close_scan_timer.Start(client_mob_close_scan_timer_idle);
}
/**
* On a normal basis we limit mob movement updates based on distance
* This ensures we send a periodic full zone update to a client that has started moving after 5 or so minutes
*
* For very large zones we will also force a full update based on distance
*
* We ignore a small distance around us so that we don't interrupt already pathing deltas as those npcs will appear
* to full stop when they are actually still pathing
*/
float distance_moved = DistanceNoZ(GetLastPositionBeforeBulkUpdate(), GetPosition());
bool moved_far_enough_before_bulk_update = distance_moved >= zone->GetNpcPositionUpdateDistance();
bool is_ready_to_update = (
client_zone_wide_full_position_update_timer.Check() || moved_far_enough_before_bulk_update
);
if (IsMoving() && is_ready_to_update) {
LogDebug("[[{}]] Client Zone Wide Position Update NPCs", GetCleanName());
auto &mob_movement_manager = MobMovementManager::Get();
auto &mob_list = entity_list.GetMobList();
for (auto &it : mob_list) {
Mob *entity = it.second;
if (!entity->IsNPC()) {
continue;
}
int animation_speed = 0;
if (entity->IsMoving()) {
if (entity->IsRunning()) {
animation_speed = (entity->IsFeared() ? entity->GetFearSpeed() : entity->GetRunspeed());
}
else {
animation_speed = entity->GetWalkspeed();
}
}
mob_movement_manager.SendCommandToClients(entity, 0.0, 0.0, 0.0, 0.0, animation_speed, ClientRangeAny, this);
}
SetLastPositionBeforeBulkUpdate(GetPosition());
}
int32 new_animation = ppu->animation; int32 new_animation = ppu->animation;
@@ -12064,15 +11984,7 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app)
auto t = GetTarget(); auto t = GetTarget();
if (t) { if (t) {
if (t->IsNPC()) { parse->EventBotMercNPC(EVENT_POPUP_RESPONSE, t, this, [&]() { return std::to_string(popup_response->popupid); });
if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_POPUP_RESPONSE)) {
parse->EventNPC(EVENT_POPUP_RESPONSE, t->CastToNPC(), this, std::to_string(popup_response->popupid), 0);
}
} else if (t->IsBot()) {
if (parse->BotHasQuestSub(EVENT_POPUP_RESPONSE)) {
parse->EventBot(EVENT_POPUP_RESPONSE, t->CastToBot(), this, std::to_string(popup_response->popupid), 0);
}
}
} }
} }
@@ -13126,14 +13038,14 @@ void Client::Handle_OP_ReadBook(const EQApplicationPacket *app)
LogError("Wrong size: OP_ReadBook, size=[{}], expected [{}]", app->size, sizeof(BookRequest_Struct)); LogError("Wrong size: OP_ReadBook, size=[{}], expected [{}]", app->size, sizeof(BookRequest_Struct));
return; return;
} }
BookRequest_Struct* book = (BookRequest_Struct*)app->pBuffer;
ReadBook(book); auto b = (BookRequest_Struct*) app->pBuffer;
if (ClientVersion() >= EQ::versions::ClientVersion::SoF) ReadBook(b);
{
EQApplicationPacket EndOfBook(OP_FinishWindow, 0); if (ClientVersion() >= EQ::versions::ClientVersion::SoF) {
QueuePacket(&EndOfBook); EQApplicationPacket end_of_book(OP_FinishWindow, 0);
QueuePacket(&end_of_book);
} }
return;
} }
void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app) void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app)
@@ -13667,11 +13579,11 @@ void Client::Handle_OP_Sacrifice(const EQApplicationPacket *app)
} }
if (ss->Confirm) { if (ss->Confirm) {
Client *Caster = entity_list.GetClientByName(SacrificeCaster.c_str()); Mob *Caster = entity_list.GetMob(sacrifice_caster_id);
if (Caster) Sacrifice(Caster); if (Caster) Sacrifice(Caster);
} }
PendingSacrifice = false; PendingSacrifice = false;
SacrificeCaster.clear(); sacrifice_caster_id = 0;
} }
void Client::Handle_OP_SafeFallSuccess(const EQApplicationPacket *app) // bit of a misnomer, sent whenever safe fall is used (success of fail) void Client::Handle_OP_SafeFallSuccess(const EQApplicationPacket *app) // bit of a misnomer, sent whenever safe fall is used (success of fail)
@@ -16893,9 +16805,14 @@ void Client::Handle_OP_RaidClearNPCMarks(const EQApplicationPacket* app)
void Client::RecordStats() void Client::RecordStats()
{ {
const uint32 character_id = CharacterID();
if (!character_id) {
return;
}
auto r = CharacterStatsRecordRepository::FindOne( auto r = CharacterStatsRecordRepository::FindOne(
database, database,
CharacterID() character_id
); );
r.status = Admin(); r.status = Admin();
@@ -16973,7 +16890,7 @@ void Client::RecordStats()
if (r.character_id > 0) { if (r.character_id > 0) {
CharacterStatsRecordRepository::UpdateOne(database, r); CharacterStatsRecordRepository::UpdateOne(database, r);
} else { } else {
r.character_id = CharacterID(); r.character_id = character_id;
r.created_at = std::time(nullptr); r.created_at = std::time(nullptr);
CharacterStatsRecordRepository::InsertOne(database, r); CharacterStatsRecordRepository::InsertOne(database, r);
} }
+35 -31
View File
@@ -121,7 +121,7 @@ bool Client::Process() {
} }
/* I haven't naturally updated my position in 10 seconds, updating manually */ /* I haven't naturally updated my position in 10 seconds, updating manually */
if (!IsMoving() && position_update_timer.Check()) { if (!IsMoving() && m_position_update_timer.Check()) {
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0); SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
} }
@@ -281,12 +281,37 @@ bool Client::Process() {
} }
} }
/** ScanCloseMobProcess();
* Scan close range mobs
* Used in aggro checks if (RuleB(Inventory, LazyLoadBank)) {
*/ // poll once a second to see if we are close to a banker and we haven't loaded the bank yet
if (mob_close_scan_timer.Check()) { if (!m_lazy_load_bank && lazy_load_bank_check_timer.Check()) {
entity_list.ScanCloseMobs(close_mobs, this, IsMoving()); if (m_lazy_load_sent_bank_slots <= EQ::invslot::SHARED_BANK_END && IsCloseToBanker()) {
m_lazy_load_bank = true;
lazy_load_bank_check_timer.Disable();
}
}
if (m_lazy_load_bank && m_lazy_load_sent_bank_slots <= EQ::invslot::SHARED_BANK_END) {
const EQ::ItemInstance *inst = nullptr;
// Jump the gaps
if (m_lazy_load_sent_bank_slots < EQ::invslot::BANK_BEGIN) {
m_lazy_load_sent_bank_slots = EQ::invslot::BANK_BEGIN;
}
else if (m_lazy_load_sent_bank_slots > EQ::invslot::BANK_END &&
m_lazy_load_sent_bank_slots < EQ::invslot::SHARED_BANK_BEGIN) {
m_lazy_load_sent_bank_slots = EQ::invslot::SHARED_BANK_BEGIN;
}
else {
m_lazy_load_sent_bank_slots++;
}
inst = m_inv[m_lazy_load_sent_bank_slots];
if (inst) {
SendItemPacket(m_lazy_load_sent_bank_slots, inst, ItemPacketType::ItemPacketTrade);
}
}
} }
bool may_use_attacks = false; bool may_use_attacks = false;
@@ -577,30 +602,7 @@ bool Client::Process() {
} }
} }
//At this point, we are still connected, everything important has taken ClientToNpcAggroProcess();
//place, now check to see if anybody wants to aggro us.
// only if client is not feigned
if (zone->CanDoCombat() && ret && !GetFeigned() && client_scan_npc_aggro_timer.Check()) {
int npc_scan_count = 0;
for (auto & close_mob : close_mobs) {
Mob *mob = close_mob.second;
if (!mob) {
continue;
}
if (mob->IsClient()) {
continue;
}
if (mob->CheckWillAggro(this) && !mob->CheckAggro(this)) {
mob->AddToHateList(this, 25);
}
npc_scan_count++;
}
LogAggro("Checking Reverse Aggro (client->npc) scanned_npcs ([{}])", npc_scan_count);
}
if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED))) if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED)))
{ {
@@ -780,6 +782,7 @@ void Client::BulkSendInventoryItems()
last_pos = ob.tellp(); last_pos = ob.tellp();
} }
if (!RuleB(Inventory, LazyLoadBank)) {
// Bank items // Bank items
for (int16 slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; slot_id++) { for (int16 slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; slot_id++) {
const EQ::ItemInstance* inst = m_inv[slot_id]; const EQ::ItemInstance* inst = m_inv[slot_id];
@@ -807,6 +810,7 @@ void Client::BulkSendInventoryItems()
last_pos = ob.tellp(); last_pos = ob.tellp();
} }
}
auto outapp = new EQApplicationPacket(OP_CharInventory); auto outapp = new EQApplicationPacket(OP_CharInventory);
outapp->size = ob.size(); outapp->size = ob.size();
+2 -2
View File
@@ -542,8 +542,8 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
if (EQ::ValueWithin(m_open_type, 57, 58) && HasDestinationZone()) { if (EQ::ValueWithin(m_open_type, 57, 58) && HasDestinationZone()) {
bool has_key_required = (required_key_item && required_key_item == player_key); bool has_key_required = (required_key_item && required_key_item == player_key);
if (sender->GetGM() && has_key_required) { if (sender->GetGM() && !has_key_required) {
has_key_required = false; has_key_required = true;
sender->Message(Chat::White, "Your GM flag allows you to open this door without a key."); sender->Message(Chat::White, "Your GM flag allows you to open this door without a key.");
} }
+6
View File
@@ -60,6 +60,12 @@ public:
void SetPosition(const glm::vec4 &position); void SetPosition(const glm::vec4 &position);
void SetSize(uint16 size); void SetSize(uint16 size);
void ToggleState(Mob *sender); void ToggleState(Mob *sender);
inline std::string GetDestinationZoneName() { return m_destination_zone_name; }
inline int GetDestinationInstanceID() { return m_destination_instance_id; }
inline float GetDestinationX() { return m_destination.x; }
inline float GetDestinationY() { return m_destination.y; }
inline float GetDestinationZ() { return m_destination.z; }
inline float GetDestinationHeading() { return m_destination.w; }
float GetX(); float GetX();
float GetY(); float GetY();
+86 -122
View File
@@ -1028,24 +1028,15 @@ void Client::SendDisciplineTimer(uint32 timer_id, uint32 duration)
} }
} }
/** void EntityList::AETaunt(Client* taunter, float range, int bonus_hate)
* @param taunter
* @param range
* @param bonus_hate
*/
void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate)
{ {
/**
* Live AE taunt range - Hardcoded.
*/
if (range == 0) { if (range == 0) {
range = 40; range = 40;
} }
float range_squared = range * range; float range_squared = range * range;
for (auto &it : entity_list.GetCloseMobList(taunter, range)) { for (auto& it: taunter->GetCloseMobList(range)) {
Mob *them = it.second; Mob *them = it.second;
if (!them) { if (!them) {
continue; continue;
@@ -1060,9 +1051,11 @@ void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate)
z_difference *= -1; z_difference *= -1;
} }
if (z_difference < 10 if (
&& taunter->IsAttackAllowed(them) z_difference < 10 &&
&& DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range_squared) { taunter->IsAttackAllowed(them) &&
DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range_squared
) {
if (taunter->CheckLosFN(them)) { if (taunter->CheckLosFN(them)) {
taunter->Taunt(them->CastToNPC(), true, 0, true, bonus_hate); taunter->Taunt(them->CastToNPC(), true, 0, true, bonus_hate);
} }
@@ -1070,38 +1063,31 @@ void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate)
} }
} }
/**
* Causes caster to hit every mob within dist range of center with spell_id
*
* @param caster_mob
* @param center_mob
* @param spell_id
* @param affect_caster
* @param resist_adjust
* @param max_targets
*/
void EntityList::AESpell( void EntityList::AESpell(
Mob *caster_mob, Mob* caster_mob,
Mob *center_mob, Mob* center_mob,
uint16 spell_id, uint16 spell_id,
bool affect_caster, bool affect_caster,
int16 resist_adjust, int16 resist_adjust,
int *max_targets int* max_targets,
bool is_scripted
) )
{ {
const auto &cast_target_position = const auto& cast_target_position = (
spells[spell_id].target_type == ST_Ring ? (!is_scripted && spells[spell_id].target_type == ST_Ring) ?
caster_mob->GetTargetRingLocation() : caster_mob->GetTargetRingLocation() :
static_cast<glm::vec3>(center_mob->GetPosition()); static_cast<glm::vec3>(center_mob->GetPosition())
);
Mob* current_mob = nullptr;
Mob *current_mob = nullptr;
bool is_detrimental_spell = IsDetrimentalSpell(spell_id); bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
bool is_npc = caster_mob->IsNPC(); bool is_npc = caster_mob->IsNPC();
float distance = caster_mob->GetAOERange(spell_id); float distance = caster_mob->GetAOERange(spell_id);
float distance_squared = distance * distance; float distance_squared = distance * distance;
float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; float min_range_squared = spells[spell_id].min_range * spells[spell_id].min_range;
glm::vec2 min = {cast_target_position.x - distance, cast_target_position.y - distance}; glm::vec2 min = { cast_target_position.x - distance, cast_target_position.y - distance };
glm::vec2 max = {cast_target_position.x + distance, cast_target_position.y + distance}; glm::vec2 max = { cast_target_position.x + distance, cast_target_position.y + distance };
/** /**
* If using Old Rain Targets - there is no max target limitation * If using Old Rain Targets - there is no max target limitation
@@ -1110,18 +1096,25 @@ void EntityList::AESpell(
max_targets = nullptr; max_targets = nullptr;
} }
/** int max_targets_allowed = RuleI(Spells, DefaultAOEMaxTargets);;
* Max AOE targets
*/
int max_targets_allowed = RuleI(Range, AOEMaxTargets); // unlimited
if (max_targets) { // rains pass this in since they need to preserve the count through waves if (max_targets) { // rains pass this in since they need to preserve the count through waves
max_targets_allowed = *max_targets; max_targets_allowed = *max_targets;
} } else if (spells[spell_id].aoe_max_targets) {
else if (spells[spell_id].aoe_max_targets) {
max_targets_allowed = spells[spell_id].aoe_max_targets; max_targets_allowed = spells[spell_id].aoe_max_targets;
} } else if (
else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc && !IsEffectInSpell(spell_id, SE_Lull) && !IsEffectInSpell(spell_id, SE_Mez)) { IsTargetableAESpell(spell_id) &&
max_targets_allowed = 4; is_detrimental_spell &&
!is_npc &&
!IsEffectInSpell(spell_id, SE_Lull) &&
!IsEffectInSpell(spell_id, SE_Mez)
) {
max_targets_allowed = RuleI(Spells, TargetedAOEMaxTargets);
} else if (
IsPBAENukeSpell(spell_id) &&
IsDetrimentalSpell &&
!is_npc
) {
max_targets_allowed = RuleI(Spells, PointBlankAOEMaxTargets);
} }
int target_hit_counter = 0; int target_hit_counter = 0;
@@ -1133,7 +1126,7 @@ void EntityList::AESpell(
distance distance
); );
for (auto &it : entity_list.GetCloseMobList(caster_mob, distance)) { for (auto& it: caster_mob->GetCloseMobList(distance)) {
current_mob = it.second; current_mob = it.second;
if (!current_mob) { if (!current_mob) {
continue; continue;
@@ -1161,16 +1154,11 @@ void EntityList::AESpell(
continue; continue;
} }
/** if (spells[spell_id].pcnpc_only_flag == PCNPCOnlyFlagType::PC && !current_mob->IsOfClientBotMerc()) {
* Check PC / NPC
* 1 = PC
* 2 = NPC
*/
if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsOfClientBotMerc()) {
continue; continue;
} }
if (spells[spell_id].pcnpc_only_flag == 2 && current_mob->IsOfClientBotMerc()) { if (spells[spell_id].pcnpc_only_flag == PCNPCOnlyFlagType::NPC && current_mob->IsOfClientBotMerc()) {
continue; continue;
} }
@@ -1184,41 +1172,40 @@ void EntityList::AESpell(
continue; continue;
} }
if (distance_to_target < min_range2) { if (distance_to_target < min_range_squared) {
continue; continue;
} }
if (is_npc && current_mob->IsNPC() && if (
spells[spell_id].target_type != ST_AreaNPCOnly) { //check npc->npc casting is_npc &&
FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob); current_mob->IsNPC() &&
spells[spell_id].target_type != ST_AreaNPCOnly
) {
const auto faction_value = current_mob->GetReverseFactionCon(caster_mob);
if (is_detrimental_spell) { if (is_detrimental_spell) {
//affect mobs that are on our hate list, or
//which have bad faction with us
if ( if (
!(caster_mob->CheckAggro(current_mob) || !(caster_mob->CheckAggro(current_mob) ||
faction_value == FACTION_THREATENINGLY || faction_value == FACTION_THREATENINGLY ||
faction_value == FACTION_SCOWLS)) { faction_value == FACTION_SCOWLS)
) {
continue; continue;
} }
} } else {
else {
//only affect mobs we would assist.
if (!(faction_value <= FACTION_AMIABLY)) { if (!(faction_value <= FACTION_AMIABLY)) {
continue; continue;
} }
} }
} }
/**
* Finally, make sure they are within range
*/
if (is_detrimental_spell) { if (is_detrimental_spell) {
if (!caster_mob->IsAttackAllowed(current_mob, true)) { if (!caster_mob->IsAttackAllowed(current_mob, true)) {
continue; continue;
} }
if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) { if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) {
continue; continue;
} }
if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN( if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN(
caster_mob->GetTargetRingX(), caster_mob->GetTargetRingX(),
caster_mob->GetTargetRingY(), caster_mob->GetTargetRingY(),
@@ -1226,9 +1213,7 @@ void EntityList::AESpell(
current_mob->GetSize())) { current_mob->GetSize())) {
continue; continue;
} }
} } else {
else {
/** /**
* Check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... * Check to stop casting beneficial ae buffs (to wit: bard songs) on enemies...
* This does not check faction for beneficial AE buffs... only agro and attackable. * This does not check faction for beneficial AE buffs... only agro and attackable.
@@ -1238,6 +1223,7 @@ void EntityList::AESpell(
if (caster_mob->IsAttackAllowed(current_mob, true)) { if (caster_mob->IsAttackAllowed(current_mob, true)) {
continue; continue;
} }
if (caster_mob->CheckAggro(current_mob)) { if (caster_mob->CheckAggro(current_mob)) {
continue; continue;
} }
@@ -1264,40 +1250,29 @@ void EntityList::AESpell(
} }
} }
/**
* @param caster
* @param center
* @param spell_id
* @param affect_caster
*/
void EntityList::MassGroupBuff( void EntityList::MassGroupBuff(
Mob *caster, Mob* caster,
Mob *center, Mob* center,
uint16 spell_id, uint16 spell_id,
bool affect_caster) bool affect_caster
)
{ {
Mob *current_mob = nullptr; Mob* current_mob = nullptr;
float distance = caster->GetAOERange(spell_id); float distance = caster->GetAOERange(spell_id);
float distance_squared = distance * distance; float distance_squared = distance * distance;
bool is_detrimental_spell = IsDetrimentalSpell(spell_id); bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
for (auto &it : entity_list.GetCloseMobList(caster, distance)) { for (auto& it: caster->GetCloseMobList(distance)) {
current_mob = it.second; current_mob = it.second;
if (!current_mob) { if (!current_mob) {
continue; continue;
} }
/** if (current_mob == center) { // Skip Center
* Skip center
*/
if (current_mob == center) {
continue; continue;
} }
/** if (current_mob == caster && !affect_caster) { // Skip Caster
* Skip self
*/
if (current_mob == caster && !affect_caster) {
continue; continue;
} }
@@ -1305,17 +1280,13 @@ void EntityList::MassGroupBuff(
continue; continue;
} }
/**
* Pets
*/
if (current_mob->IsNPC()) { if (current_mob->IsNPC()) {
Mob *owner = current_mob->GetOwner(); Mob* owner = current_mob->GetOwner();
if (owner) { if (owner) {
if (!owner->IsOfClientBot()) { if (!owner->IsOfClientBot()) {
continue; continue;
} }
} } else {
else {
continue; continue;
} }
} }
@@ -1328,55 +1299,48 @@ void EntityList::MassGroupBuff(
} }
} }
/**
* Rampage - Normal and Duration rampages
* NPCs handle it differently in Mob::Rampage
*
* @param attacker
* @param distance
* @param Hand
* @param count
* @param is_from_spell
*/
void EntityList::AEAttack( void EntityList::AEAttack(
Mob *attacker, Mob* attacker,
float distance, float distance,
int Hand, int16 slot_id,
int count, int hit_count,
bool is_from_spell, bool is_from_spell,
int attack_rounds) int attack_rounds
)
{ {
Mob *current_mob = nullptr; Mob* current_mob = nullptr;
float distance_squared = distance * distance; float distance_squared = distance * distance;
int hit_count = 0; int current_hits = 0;
for (auto &it : entity_list.GetCloseMobList(attacker, distance)) { for (auto& it: attacker->GetCloseMobList(distance)) {
current_mob = it.second; current_mob = it.second;
if (!current_mob) { if (!current_mob) {
continue; continue;
} }
if (current_mob->IsNPC() if (
&& current_mob != attacker //this is not needed unless NPCs can use this current_mob->IsNPC() &&
&& (attacker->IsAttackAllowed(current_mob)) current_mob != attacker &&
&& !current_mob->IsHorse() /* dont attack mounts */ attacker->IsAttackAllowed(current_mob) &&
&& (DistanceSquared(current_mob->GetPosition(), attacker->GetPosition()) <= distance_squared) !current_mob->IsHorse() &&
DistanceSquared(current_mob->GetPosition(), attacker->GetPosition()) <= distance_squared
) { ) {
for (int i = 0; i < attack_rounds; i++) { for (int i = 0; i < attack_rounds; i++) {
if (!attacker->IsClient() || attacker->GetClass() == Class::Monk || attacker->GetClass() == Class::Ranger) { if (
attacker->Attack(current_mob, Hand, false, false, is_from_spell); !attacker->IsClient() ||
attacker->GetClass() == Class::Monk ||
attacker->GetClass() == Class::Ranger
) {
attacker->Attack(current_mob, slot_id, false, false, is_from_spell);
} else { } else {
attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell); attacker->CastToClient()->DoAttackRounds(current_mob, slot_id, is_from_spell);
} }
} }
hit_count++; current_hits++;
if (count != 0 && hit_count >= count) { if (hit_count != 0 && current_hits >= hit_count) {
return; return;
} }
} }
} }
} }
+231 -60
View File
@@ -57,6 +57,7 @@ void perl_register_expedition();
void perl_register_expedition_lock_messages(); void perl_register_expedition_lock_messages();
void perl_register_bot(); void perl_register_bot();
void perl_register_buff(); void perl_register_buff();
void perl_register_merc();
#endif // EMBPERL_XS_CLASSES #endif // EMBPERL_XS_CLASSES
#endif // EMBPERL_XS #endif // EMBPERL_XS
@@ -203,6 +204,7 @@ const char* QuestEventSubroutines[_LargestEventID] = {
"EVENT_ENTITY_VARIABLE_UPDATE", "EVENT_ENTITY_VARIABLE_UPDATE",
"EVENT_AA_LOSS", "EVENT_AA_LOSS",
"EVENT_SPELL_BLOCKED", "EVENT_SPELL_BLOCKED",
"EVENT_READ_ITEM",
// Add new events before these or Lua crashes // Add new events before these or Lua crashes
"EVENT_SPELL_EFFECT_BOT", "EVENT_SPELL_EFFECT_BOT",
@@ -216,6 +218,8 @@ PerlembParser::PerlembParser() : perl(nullptr)
global_player_quest_status_ = questUnloaded; global_player_quest_status_ = questUnloaded;
bot_quest_status_ = questUnloaded; bot_quest_status_ = questUnloaded;
global_bot_quest_status_ = questUnloaded; global_bot_quest_status_ = questUnloaded;
merc_quest_status_ = questUnloaded;
global_merc_quest_status_ = questUnloaded;
} }
PerlembParser::~PerlembParser() PerlembParser::~PerlembParser()
@@ -257,6 +261,8 @@ void PerlembParser::ReloadQuests()
global_player_quest_status_ = questUnloaded; global_player_quest_status_ = questUnloaded;
bot_quest_status_ = questUnloaded; bot_quest_status_ = questUnloaded;
global_bot_quest_status_ = questUnloaded; global_bot_quest_status_ = questUnloaded;
merc_quest_status_ = questUnloaded;
global_merc_quest_status_ = questUnloaded;
item_quest_status_.clear(); item_quest_status_.clear();
spell_quest_status_.clear(); spell_quest_status_.clear();
@@ -284,6 +290,8 @@ int PerlembParser::EventCommon(
bool is_global_npc_quest = false; bool is_global_npc_quest = false;
bool is_bot_quest = false; bool is_bot_quest = false;
bool is_global_bot_quest = false; bool is_global_bot_quest = false;
bool is_merc_quest = false;
bool is_global_merc_quest = false;
bool is_item_quest = false; bool is_item_quest = false;
bool is_spell_quest = false; bool is_spell_quest = false;
@@ -294,6 +302,8 @@ int PerlembParser::EventCommon(
is_global_player_quest, is_global_player_quest,
is_bot_quest, is_bot_quest,
is_global_bot_quest, is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest, is_global_npc_quest,
is_item_quest, is_item_quest,
is_spell_quest, is_spell_quest,
@@ -309,6 +319,8 @@ int PerlembParser::EventCommon(
is_global_player_quest, is_global_player_quest,
is_bot_quest, is_bot_quest,
is_global_bot_quest, is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest, is_global_npc_quest,
is_item_quest, is_item_quest,
is_spell_quest, is_spell_quest,
@@ -338,6 +350,8 @@ int PerlembParser::EventCommon(
is_global_player_quest, is_global_player_quest,
is_bot_quest, is_bot_quest,
is_global_bot_quest, is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest, is_global_npc_quest,
is_item_quest, is_item_quest,
is_spell_quest, is_spell_quest,
@@ -355,6 +369,8 @@ int PerlembParser::EventCommon(
is_global_player_quest, is_global_player_quest,
is_bot_quest, is_bot_quest,
is_global_bot_quest, is_global_bot_quest,
is_merc_quest,
is_global_merc_quest,
is_global_npc_quest, is_global_npc_quest,
is_item_quest, is_item_quest,
is_spell_quest, is_spell_quest,
@@ -381,7 +397,7 @@ int PerlembParser::EventCommon(
if (is_player_quest || is_global_player_quest) { if (is_player_quest || is_global_player_quest) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, nullptr, nullptr); return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, nullptr, nullptr);
} else if (is_bot_quest || is_global_bot_quest) { } else if (is_bot_quest || is_global_bot_quest || is_merc_quest || is_global_merc_quest) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, npc_mob, mob, nullptr, nullptr); return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, npc_mob, mob, nullptr, nullptr);
} else if (is_item_quest) { } else if (is_item_quest) {
return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, inst, nullptr); return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, inst, nullptr);
@@ -1008,41 +1024,22 @@ int PerlembParser::SendCommands(
#ifdef EMBPERL_XS_CLASSES #ifdef EMBPERL_XS_CLASSES
dTHX; dTHX;
{ {
std::string cl = fmt::format("${}::client", prefix); const std::vector<std::string>& suffixes = {
std::string np = fmt::format("${}::npc", prefix); "bot",
std::string qi = fmt::format("${}::questitem", prefix); "client",
std::string sp = fmt::format("${}::spell", prefix); "entity_list",
std::string enl = fmt::format("${}::entity_list", prefix); "merc",
std::string bot = fmt::format("${}::bot", prefix); "npc",
"questitem",
"spell"
};
if (clear_vars_.find(cl) != clear_vars_.end()) { for (const auto& suffix : suffixes) {
auto e = fmt::format("{} = undef;", cl); const std::string& key = fmt::format("${}::{}", prefix, suffix);
if (clear_vars_.find(suffix) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", key);
perl->eval(e.c_str()); perl->eval(e.c_str());
} }
if (clear_vars_.find(np) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", np);
perl->eval(e.c_str());
}
if (clear_vars_.find(qi) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", qi);
perl->eval(e.c_str());
}
if (clear_vars_.find(sp) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", sp);
perl->eval(e.c_str());
}
if (clear_vars_.find(enl) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", enl);
perl->eval(e.c_str());
}
if (clear_vars_.find(bot) != clear_vars_.end()) {
auto e = fmt::format("{} = undef;", bot);
perl->eval(e.c_str());
} }
} }
@@ -1059,19 +1056,21 @@ int PerlembParser::SendCommands(
sv_setsv(client, _empty_sv); sv_setsv(client, _empty_sv);
} }
//only export NPC if it's a npc quest if (other->IsBot()) {
if (!other->IsClient() && other->IsNPC()) {
NPC* n = quest_manager.GetNPC();
buf = fmt::format("{}::npc", prefix);
SV* npc = get_sv(buf.c_str(), true);
sv_setref_pv(npc, "NPC", n);
}
if (!other->IsClient() && other->IsBot()) {
Bot* b = quest_manager.GetBot(); Bot* b = quest_manager.GetBot();
buf = fmt::format("{}::bot", prefix); buf = fmt::format("{}::bot", prefix);
SV* bot = get_sv(buf.c_str(), true); SV* bot = get_sv(buf.c_str(), true);
sv_setref_pv(bot, "Bot", b); sv_setref_pv(bot, "Bot", b);
} else if (other->IsMerc()) {
Merc* m = quest_manager.GetMerc();
buf = fmt::format("{}::merc", prefix);
SV* merc = get_sv(buf.c_str(), true);
sv_setref_pv(merc, "Merc", m);
} else if (other->IsNPC()) {
NPC* n = quest_manager.GetNPC();
buf = fmt::format("{}::npc", prefix);
SV* npc = get_sv(buf.c_str(), true);
sv_setref_pv(npc, "NPC", n);
} }
//only export QuestItem if it's an inst quest //only export QuestItem if it's an inst quest
@@ -1097,23 +1096,25 @@ int PerlembParser::SendCommands(
#endif #endif
//now call the requested sub //now call the requested sub
ret_value = perl->dosub(std::string(prefix).append("::").append(event_id).c_str()); const std::string& sub_key = fmt::format("{}::{}", prefix, event_id);
ret_value = perl->dosub(sub_key.c_str());
#ifdef EMBPERL_XS_CLASSES #ifdef EMBPERL_XS_CLASSES
{ {
std::string cl = fmt::format("${}::client", prefix); const std::vector<std::string>& suffixes = {
std::string np = fmt::format("${}::npc", prefix); "bot",
std::string qi = fmt::format("${}::questitem", prefix); "client",
std::string sp = fmt::format("${}::spell", prefix); "entity_list",
std::string enl = fmt::format("${}::entity_list", prefix); "merc",
std::string bot = fmt::format("${}::bot", prefix); "npc",
"questitem",
"spell"
};
clear_vars_[cl] = 1; for (const auto& suffix : suffixes) {
clear_vars_[np] = 1; const std::string& key = fmt::format("${}::{}", prefix, suffix);
clear_vars_[qi] = 1; clear_vars_[key] = 1;
clear_vars_[sp] = 1; }
clear_vars_[enl] = 1;
clear_vars_[bot] = 1;
} }
#endif #endif
@@ -1183,6 +1184,7 @@ void PerlembParser::MapFunctions()
perl_register_expedition_lock_messages(); perl_register_expedition_lock_messages();
perl_register_bot(); perl_register_bot();
perl_register_buff(); perl_register_buff();
perl_register_merc();
#endif // EMBPERL_XS_CLASSES #endif // EMBPERL_XS_CLASSES
} }
@@ -1191,6 +1193,8 @@ void PerlembParser::GetQuestTypes(
bool& is_global_player_quest, bool& is_global_player_quest,
bool& is_bot_quest, bool& is_bot_quest,
bool& is_global_bot_quest, bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest, bool& is_global_npc_quest,
bool& is_item_quest, bool& is_item_quest,
bool& is_spell_quest, bool& is_spell_quest,
@@ -1218,10 +1222,14 @@ void PerlembParser::GetQuestTypes(
if (is_global) { if (is_global) {
if (npc_mob->IsBot()) { if (npc_mob->IsBot()) {
is_global_bot_quest = true; is_global_bot_quest = true;
} else if (npc_mob->IsMerc()) {
is_global_merc_quest = true;
} }
} else { } else {
if (npc_mob->IsBot()) { if (npc_mob->IsBot()) {
is_bot_quest = true; is_bot_quest = true;
} else if (npc_mob->IsMerc()) {
is_merc_quest = true;
} }
} }
} else { } else {
@@ -1250,6 +1258,8 @@ void PerlembParser::GetQuestPackageName(
bool& is_global_player_quest, bool& is_global_player_quest,
bool& is_bot_quest, bool& is_bot_quest,
bool& is_global_bot_quest, bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest, bool& is_global_npc_quest,
bool& is_item_quest, bool& is_item_quest,
bool& is_spell_quest, bool& is_spell_quest,
@@ -1267,6 +1277,8 @@ void PerlembParser::GetQuestPackageName(
!is_global_player_quest && !is_global_player_quest &&
!is_bot_quest && !is_bot_quest &&
!is_global_bot_quest && !is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest && !is_item_quest &&
!is_spell_quest !is_spell_quest
) { ) {
@@ -1290,6 +1302,10 @@ void PerlembParser::GetQuestPackageName(
package_name = "qst_bot"; package_name = "qst_bot";
} else if (is_global_bot_quest) { } else if (is_global_bot_quest) {
package_name = "qst_global_bot"; package_name = "qst_global_bot";
} else if (is_merc_quest) {
package_name = "qst_merc";
} else if (is_global_merc_quest) {
package_name = "qst_global_merc";
} else { } else {
package_name = fmt::format("qst_spell_{}", object_id); package_name = fmt::format("qst_spell_{}", object_id);
} }
@@ -1315,6 +1331,8 @@ void PerlembParser::ExportQGlobals(
bool is_global_player_quest, bool is_global_player_quest,
bool is_bot_quest, bool is_bot_quest,
bool is_global_bot_quest, bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest, bool is_global_npc_quest,
bool is_item_quest, bool is_item_quest,
bool is_spell_quest, bool is_spell_quest,
@@ -1330,6 +1348,8 @@ void PerlembParser::ExportQGlobals(
!is_global_player_quest && !is_global_player_quest &&
!is_bot_quest && !is_bot_quest &&
!is_global_bot_quest && !is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest && !is_item_quest &&
!is_spell_quest !is_spell_quest
) { ) {
@@ -1465,6 +1485,8 @@ void PerlembParser::ExportMobVariables(
bool is_global_player_quest, bool is_global_player_quest,
bool is_bot_quest, bool is_bot_quest,
bool is_global_bot_quest, bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest, bool is_global_npc_quest,
bool is_item_quest, bool is_item_quest,
bool is_spell_quest, bool is_spell_quest,
@@ -1490,6 +1512,8 @@ void PerlembParser::ExportMobVariables(
!is_global_player_quest && !is_global_player_quest &&
!is_bot_quest && !is_bot_quest &&
!is_global_bot_quest && !is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest !is_item_quest
) { ) {
if (mob && mob->IsClient() && npc_mob && npc_mob->IsNPC()) { if (mob && mob->IsClient() && npc_mob && npc_mob->IsNPC()) {
@@ -1520,6 +1544,8 @@ void PerlembParser::ExportMobVariables(
!is_global_player_quest && !is_global_player_quest &&
!is_bot_quest && !is_bot_quest &&
!is_global_bot_quest && !is_global_bot_quest &&
!is_merc_quest &&
!is_global_merc_quest &&
!is_item_quest && !is_item_quest &&
!is_spell_quest !is_spell_quest
) { ) {
@@ -2064,9 +2090,14 @@ void PerlembParser::ExportEventVariables(
Corpse* corpse = std::any_cast<Corpse*>(extra_pointers->at(0)); Corpse* corpse = std::any_cast<Corpse*>(extra_pointers->at(0));
if (corpse) { if (corpse) {
ExportVar(package_name.c_str(), "killed_corpse_id", corpse->GetID()); ExportVar(package_name.c_str(), "killed_corpse_id", corpse->GetID());
ExportVar(package_name.c_str(), "killed_x", corpse->GetX());
ExportVar(package_name.c_str(), "killed_y", corpse->GetY());
ExportVar(package_name.c_str(), "killed_z", corpse->GetZ());
ExportVar(package_name.c_str(), "killed_h", corpse->GetHeading());
} }
} }
// EVENT_DEATH_ZONE only
if (extra_pointers && extra_pointers->size() >= 2) { if (extra_pointers && extra_pointers->size() >= 2) {
NPC* killed = std::any_cast<NPC*>(extra_pointers->at(1)); NPC* killed = std::any_cast<NPC*>(extra_pointers->at(1));
if (killed) { if (killed) {
@@ -2075,11 +2106,8 @@ void PerlembParser::ExportEventVariables(
"killed_bot_id", "killed_bot_id",
killed->IsBot() ? killed->CastToBot()->GetBotID() : 0 killed->IsBot() ? killed->CastToBot()->GetBotID() : 0
); );
ExportVar(package_name.c_str(), "killed_npc_id", killed->IsNPC() ? killed->GetNPCTypeID() : 0); ExportVar(package_name.c_str(), "killed_merc_id", killed->IsMerc() ? killed->CastToMerc()->GetMercenaryID() : 0);
ExportVar(package_name.c_str(), "killed_x", killed->GetX()); ExportVar(package_name.c_str(), "killed_npc_id", !killed->IsMerc() && killed->IsNPC() ? killed->GetNPCTypeID() : 0);
ExportVar(package_name.c_str(), "killed_y", killed->GetY());
ExportVar(package_name.c_str(), "killed_z", killed->GetZ());
ExportVar(package_name.c_str(), "killed_h", killed->GetHeading());
} }
} }
break; break;
@@ -2355,6 +2383,7 @@ void PerlembParser::ExportEventVariables(
case EVENT_DESPAWN: { case EVENT_DESPAWN: {
ExportVar(package_name.c_str(), "despawned_entity_id", npc_mob->GetID()); ExportVar(package_name.c_str(), "despawned_entity_id", npc_mob->GetID());
ExportVar(package_name.c_str(), "despawned_bot_id", npc_mob->IsBot() ? npc_mob->CastToBot()->GetBotID() : 0); ExportVar(package_name.c_str(), "despawned_bot_id", npc_mob->IsBot() ? npc_mob->CastToBot()->GetBotID() : 0);
ExportVar(package_name.c_str(), "despawned_merc_id", npc_mob->IsMerc() ? npc_mob->CastToMerc()->GetMercenaryID() : 0);
ExportVar(package_name.c_str(), "despawned_npc_id", npc_mob->IsNPC() ? npc_mob->GetNPCTypeID() : 0); ExportVar(package_name.c_str(), "despawned_npc_id", npc_mob->IsNPC() ? npc_mob->GetNPCTypeID() : 0);
break; break;
} }
@@ -2487,6 +2516,28 @@ void PerlembParser::ExportEventVariables(
break; break;
} }
case EVENT_READ_ITEM: {;
ExportVar(package_name.c_str(), "item_id", extra_data);
ExportVar(package_name.c_str(), "text_file", data);
if (extra_pointers && extra_pointers->size() == 7) {
ExportVar(package_name.c_str(), "book_text", std::any_cast<std::string>(extra_pointers->at(0)).c_str());
ExportVar(package_name.c_str(), "can_cast", std::any_cast<int8>(extra_pointers->at(1)));
ExportVar(package_name.c_str(), "can_scribe", std::any_cast<int8>(extra_pointers->at(2)));
ExportVar(package_name.c_str(), "slot_id", std::any_cast<int16>(extra_pointers->at(3)));
ExportVar(package_name.c_str(), "target_id", std::any_cast<int>(extra_pointers->at(4)));
ExportVar(package_name.c_str(), "type", std::any_cast<uint8>(extra_pointers->at(5)));
ExportVar(
package_name.c_str(),
"item",
"QuestItem",
std::any_cast<EQ::ItemInstance*>(extra_pointers->at(6))
);
}
break;
}
default: { default: {
break; break;
} }
@@ -2613,4 +2664,124 @@ int PerlembParser::EventGlobalBot(
); );
} }
void PerlembParser::LoadMercScript(std::string filename)
{
if (!perl || merc_quest_status_ != questUnloaded) {
return;
}
try {
perl->eval_file("qst_merc", filename.c_str());
} catch (std::string e) {
AddError(
fmt::format(
"Error Compiling Merc Quest File [{}] Error [{}]",
filename,
e
)
);
merc_quest_status_ = questFailedToLoad;
return;
}
merc_quest_status_ = questLoaded;
}
void PerlembParser::LoadGlobalMercScript(std::string filename)
{
if (!perl || global_merc_quest_status_ != questUnloaded) {
return;
}
try {
perl->eval_file("qst_global_merc", filename.c_str());
} catch (std::string e) {
AddError(
fmt::format(
"Error Compiling Global Merc Quest File [{}] Error [{}]",
filename,
e
)
);
global_merc_quest_status_ = questFailedToLoad;
return;
}
global_merc_quest_status_ = questLoaded;
}
bool PerlembParser::MercHasQuestSub(QuestEventID event_id)
{
if (
!perl ||
merc_quest_status_ != questLoaded ||
event_id >= _LargestEventID
) {
return false;
}
return perl->SubExists("qst_merc", QuestEventSubroutines[event_id]);
}
bool PerlembParser::GlobalMercHasQuestSub(QuestEventID event_id)
{
if (
!perl ||
global_merc_quest_status_ != questLoaded ||
event_id >= _LargestEventID
) {
return false;
}
return (perl->SubExists("qst_global_merc", QuestEventSubroutines[event_id]));
}
int PerlembParser::EventMerc(
QuestEventID event_id,
Merc* merc,
Mob* mob,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return EventCommon(
event_id,
0,
data.c_str(),
merc,
nullptr,
nullptr,
mob,
extra_data,
false,
extra_pointers
);
}
int PerlembParser::EventGlobalMerc(
QuestEventID event_id,
Merc* merc,
Mob* mob,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
)
{
return EventCommon(
event_id,
0,
data.c_str(),
merc,
nullptr,
nullptr,
mob,
extra_data,
true,
extra_pointers
);
}
#endif #endif
+32
View File
@@ -118,6 +118,24 @@ public:
std::vector<std::any>* extra_pointers std::vector<std::any>* extra_pointers
); );
virtual int EventMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual int EventGlobalMerc(
QuestEventID event_id,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id); virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id);
virtual bool HasGlobalQuestSub(QuestEventID event_id); virtual bool HasGlobalQuestSub(QuestEventID event_id);
virtual bool PlayerHasQuestSub(QuestEventID event_id); virtual bool PlayerHasQuestSub(QuestEventID event_id);
@@ -126,6 +144,8 @@ public:
virtual bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id); virtual bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id);
virtual bool BotHasQuestSub(QuestEventID event_id); virtual bool BotHasQuestSub(QuestEventID event_id);
virtual bool GlobalBotHasQuestSub(QuestEventID event_id); virtual bool GlobalBotHasQuestSub(QuestEventID event_id);
virtual bool MercHasQuestSub(QuestEventID event_id);
virtual bool GlobalMercHasQuestSub(QuestEventID event_id);
virtual void LoadNPCScript(std::string filename, int npc_id); virtual void LoadNPCScript(std::string filename, int npc_id);
virtual void LoadGlobalNPCScript(std::string filename); virtual void LoadGlobalNPCScript(std::string filename);
@@ -135,6 +155,8 @@ public:
virtual void LoadSpellScript(std::string filename, uint32 spell_id); virtual void LoadSpellScript(std::string filename, uint32 spell_id);
virtual void LoadBotScript(std::string filename); virtual void LoadBotScript(std::string filename);
virtual void LoadGlobalBotScript(std::string filename); virtual void LoadGlobalBotScript(std::string filename);
virtual void LoadMercScript(std::string filename);
virtual void LoadGlobalMercScript(std::string filename);
virtual void AddVar(std::string name, std::string val); virtual void AddVar(std::string name, std::string val);
virtual std::string GetVar(std::string name); virtual std::string GetVar(std::string name);
@@ -182,6 +204,8 @@ private:
bool& is_global_player_quest, bool& is_global_player_quest,
bool& is_bot_quest, bool& is_bot_quest,
bool& is_global_bot_quest, bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest, bool& is_global_npc_quest,
bool& is_item_quest, bool& is_item_quest,
bool& is_spell_quest, bool& is_spell_quest,
@@ -197,6 +221,8 @@ private:
bool& is_global_player_quest, bool& is_global_player_quest,
bool& is_bot_quest, bool& is_bot_quest,
bool& is_global_bot_quest, bool& is_global_bot_quest,
bool& is_merc_quest,
bool& is_global_merc_quest,
bool& is_global_npc_quest, bool& is_global_npc_quest,
bool& is_item_quest, bool& is_item_quest,
bool& is_spell_quest, bool& is_spell_quest,
@@ -216,6 +242,8 @@ private:
bool is_global_player_quest, bool is_global_player_quest,
bool is_bot_quest, bool is_bot_quest,
bool is_global_bot_quest, bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest, bool is_global_npc_quest,
bool is_item_quest, bool is_item_quest,
bool is_spell_quest, bool is_spell_quest,
@@ -230,6 +258,8 @@ private:
bool is_global_player_quest, bool is_global_player_quest,
bool is_bot_quest, bool is_bot_quest,
bool is_global_bot_quest, bool is_global_bot_quest,
bool is_merc_quest,
bool is_global_merc_quest,
bool is_global_npc_quest, bool is_global_npc_quest,
bool is_item_quest, bool is_item_quest,
bool is_spell_quest, bool is_spell_quest,
@@ -263,6 +293,8 @@ private:
PerlQuestStatus global_player_quest_status_; PerlQuestStatus global_player_quest_status_;
PerlQuestStatus bot_quest_status_; PerlQuestStatus bot_quest_status_;
PerlQuestStatus global_bot_quest_status_; PerlQuestStatus global_bot_quest_status_;
PerlQuestStatus merc_quest_status_;
PerlQuestStatus global_merc_quest_status_;
SV* _empty_sv; SV* _empty_sv;
+13 -1
View File
@@ -1276,7 +1276,7 @@ int Perl__tasktimeleft(int task_id)
return quest_manager.tasktimeleft(task_id); return quest_manager.tasktimeleft(task_id);
} }
int Perl__istaskcompleted(int task_id) bool Perl__istaskcompleted(int task_id)
{ {
return quest_manager.istaskcompleted(task_id); return quest_manager.istaskcompleted(task_id);
} }
@@ -5967,6 +5967,17 @@ bool Perl__send_parcel(perl::reference table_ref)
return out; return out;
} }
bool Perl__aretaskscompleted(perl::array task_ids)
{
std::vector<int> v;
for (const auto& e : task_ids) {
v.emplace_back(static_cast<int>(e));
}
return quest_manager.aretaskscompleted(v);
}
void perl_register_quest() void perl_register_quest()
{ {
perl::interpreter perl(PERL_GET_THX); perl::interpreter perl(PERL_GET_THX);
@@ -6290,6 +6301,7 @@ void perl_register_quest()
package.add("addloot", (void(*)(int, int))&Perl__addloot); package.add("addloot", (void(*)(int, int))&Perl__addloot);
package.add("addloot", (void(*)(int, int, bool))&Perl__addloot); package.add("addloot", (void(*)(int, int, bool))&Perl__addloot);
package.add("addskill", &Perl__addskill); package.add("addskill", &Perl__addskill);
package.add("aretaskscompleted", &Perl__aretaskscompleted);
package.add("assigntask", (void(*)(int))&Perl__assigntask); package.add("assigntask", (void(*)(int))&Perl__assigntask);
package.add("assigntask", (void(*)(int, bool))&Perl__assigntask); package.add("assigntask", (void(*)(int, bool))&Perl__assigntask);
package.add("attack", &Perl__attack); package.add("attack", &Perl__attack);
+83 -52
View File
@@ -696,7 +696,7 @@ void EntityList::AddNPC(NPC *npc, bool send_spawn_packet, bool dont_queue)
npc_list.emplace(std::pair<uint16, NPC *>(npc->GetID(), npc)); npc_list.emplace(std::pair<uint16, NPC *>(npc->GetID(), npc));
mob_list.emplace(std::pair<uint16, Mob *>(npc->GetID(), npc)); mob_list.emplace(std::pair<uint16, Mob *>(npc->GetID(), npc));
entity_list.ScanCloseMobs(npc->close_mobs, npc, true); entity_list.ScanCloseMobs(npc);
if (parse->HasQuestSub(npc->GetNPCTypeID(), EVENT_SPAWN)) { if (parse->HasQuestSub(npc->GetNPCTypeID(), EVENT_SPAWN)) {
parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0); parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0);
@@ -776,6 +776,10 @@ void EntityList::AddMerc(Merc *merc, bool SendSpawnPacket, bool dontqueue)
merc_list.emplace(std::pair<uint16, Merc *>(merc->GetID(), merc)); merc_list.emplace(std::pair<uint16, Merc *>(merc->GetID(), merc));
mob_list.emplace(std::pair<uint16, Mob *>(merc->GetID(), merc)); mob_list.emplace(std::pair<uint16, Mob *>(merc->GetID(), merc));
if (parse->MercHasQuestSub(EVENT_SPAWN)) {
parse->EventMerc(EVENT_SPAWN, merc, nullptr, "", 0);
}
} }
} }
@@ -1742,8 +1746,7 @@ void EntityList::QueueCloseClients(
} }
float distance_squared = distance * distance; float distance_squared = distance * distance;
for (auto &e : sender->GetCloseMobList(distance)) {
for (auto &e : GetCloseMobList(sender, distance)) {
Mob *mob = e.second; Mob *mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -2886,7 +2889,7 @@ bool EntityList::RemoveMobFromCloseLists(Mob *mob)
entity_id entity_id
); );
it->second->close_mobs.erase(entity_id); it->second->m_close_mobs.erase(entity_id);
++it; ++it;
} }
@@ -2911,49 +2914,40 @@ void EntityList::RemoveAuraFromMobs(Mob *aura)
} }
} }
/** // The purpose of this system is so that we cache relevant entities that are "close"
* The purpose of this system is so that we cache relevant entities that are "close" //
* // In general; it becomes incredibly expensive to run zone-wide checks against every single mob in the zone when in reality
* In general; it becomes incredibly expensive to run zone-wide checks against every single mob in the zone when in reality // we only care about entities closest to us
* we only care about entities closest to us //
* // A very simple example of where this is relevant is Aggro, the below example is skewed because the overall implementation
* A very simple example of where this is relevant is Aggro, the below example is skewed because the overall implementation // of Aggro was also tweaked in conjunction with close lists. We also scan more aggressively when entities are moving (1-6 seconds)
* of Aggro was also tweaked in conjunction with close lists. We also scan more aggressively when entities are moving (1-6 seconds) // versus 60 seconds when idle. We also have entities that are moving add themselves to those closest to them so that their close
* versus 60 seconds when idle. We also have entities that are moving add themselves to those closest to them so that their close // lists remain always up to date
* lists remain always up to date //
* // Before: Aggro checks for NPC to Client aggro | (40 clients in zone) x (525 npcs) x 2 (times a second) = 2,520,000 checks a minute
* Before: Aggro checks for NPC to Client aggro | (40 clients in zone) x (525 npcs) x 2 (times a second) = 2,520,000 checks a minute // After: Aggro checks for NPC to Client aggro | (40 clients in zone) x (20-30 npcs) x 2 (times a second) = 144,000 checks a minute (This is // tually far less today)
* After: Aggro checks for NPC to Client aggro | (40 clients in zone) x (20-30 npcs) x 2 (times a second) = 144,000 checks a minute (This is actually far less today) //
* // Places in the code where this logic makes a huge impact
* Places in the code where this logic makes a huge impact //
* // Aggro checks (zone wide -> close)
* Aggro checks (zone wide -> close) // Aura processing (zone wide -> close)
* Aura processing (zone wide -> close) // AE Taunt (zone wide -> close)
* AE Taunt (zone wide -> close) // AOE Spells (zone wide -> close)
* AOE Spells (zone wide -> close) // Bard Pulse AOE (zone wide -> close)
* Bard Pulse AOE (zone wide -> close) // Mass Group Buff (zone wide -> close)
* Mass Group Buff (zone wide -> close) // AE Attack (zone wide -> close)
* AE Attack (zone wide -> close) // Packet QueueCloseClients (zone wide -> close)
* Packet QueueCloseClients (zone wide -> close) // Check Close Beneficial Spells (Buffs; should I heal other npcs) (zone wide -> close)
* Check Close Beneficial Spells (Buffs; should I heal other npcs) (zone wide -> close) // AI Yell for Help (NPC Assist other NPCs) (zone wide -> close)
* AI Yell for Help (NPC Assist other NPCs) (zone wide -> close) //
* // All of the above makes a tremendous impact on the bottom line of cpu cycle performance because we run an order of magnitude
* All of the above makes a tremendous impact on the bottom line of cpu cycle performance because we run an order of magnitude // less checks by focusing our hot path logic down to a very small subset of relevant entities instead of looping an entire
* less checks by focusing our hot path logic down to a very small subset of relevant entities instead of looping an entire // entity list (zone wide)
* entity list (zone wide) void EntityList::ScanCloseMobs(Mob *scanning_mob)
*
* @param close_mobs
* @param scanning_mob
*/
void EntityList::ScanCloseMobs(
std::unordered_map<uint16, Mob *> &close_mobs,
Mob *scanning_mob,
bool add_self_to_other_lists
)
{ {
float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance); float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance);
close_mobs.clear(); scanning_mob->m_close_mobs.clear();
for (auto &e : mob_list) { for (auto &e : mob_list) {
auto mob = e.second; auto mob = e.second;
@@ -2963,12 +2957,13 @@ void EntityList::ScanCloseMobs(
float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition());
if (distance <= scan_range || mob->GetAggroRange() >= scan_range) { if (distance <= scan_range || mob->GetAggroRange() >= scan_range) {
close_mobs.emplace(std::pair<uint16, Mob *>(mob->GetID(), mob)); scanning_mob->m_close_mobs.emplace(std::pair<uint16, Mob *>(mob->GetID(), mob));
if (add_self_to_other_lists && scanning_mob->GetID() > 0) { // add self to other mobs close list
if (scanning_mob->GetID() > 0) {
bool has_mob = false; bool has_mob = false;
for (auto &cm: mob->close_mobs) { for (auto &cm: mob->m_close_mobs) {
if (scanning_mob->GetID() == cm.first) { if (scanning_mob->GetID() == cm.first) {
has_mob = true; has_mob = true;
break; break;
@@ -2976,7 +2971,7 @@ void EntityList::ScanCloseMobs(
} }
if (!has_mob) { if (!has_mob) {
mob->close_mobs.insert(std::pair<uint16, Mob *>(scanning_mob->GetID(), scanning_mob)); mob->m_close_mobs.insert(std::pair<uint16, Mob *>(scanning_mob->GetID(), scanning_mob));
} }
} }
} }
@@ -2985,7 +2980,7 @@ void EntityList::ScanCloseMobs(
LogAIScanCloseDetail( LogAIScanCloseDetail(
"[{}] Scanning Close List | list_size [{}] moving [{}]", "[{}] Scanning Close List | list_size [{}] moving [{}]",
scanning_mob->GetCleanName(), scanning_mob->GetCleanName(),
close_mobs.size(), scanning_mob->m_close_mobs.size(),
scanning_mob->IsMoving() ? "true" : "false" scanning_mob->IsMoving() ? "true" : "false"
); );
} }
@@ -4448,7 +4443,7 @@ void EntityList::QuestJournalledSayClose(
buf.WriteInt32(0); buf.WriteInt32(0);
if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) { if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) {
for (auto &e : GetCloseMobList(sender, (dist * dist))) { for (auto &e : sender->GetCloseMobList(dist)) {
Mob *mob = e.second; Mob *mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -5651,7 +5646,7 @@ std::vector<Mob*> EntityList::GetTargetsForVirusEffect(Mob *spreader, Mob *origi
std::vector<Mob *> spreader_list = {}; std::vector<Mob *> spreader_list = {};
bool is_detrimental_spell = IsDetrimentalSpell(spell_id); bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
for (auto &it : entity_list.GetCloseMobList(spreader, range)) { for (auto &it : spreader->GetCloseMobList(range)) {
Mob *mob = it.second; Mob *mob = it.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -5781,7 +5776,7 @@ void EntityList::ReloadMerchants() {
std::unordered_map<uint16, Mob *> &EntityList::GetCloseMobList(Mob *mob, float distance) std::unordered_map<uint16, Mob *> &EntityList::GetCloseMobList(Mob *mob, float distance)
{ {
if (distance <= RuleI(Range, MobCloseScanDistance)) { if (distance <= RuleI(Range, MobCloseScanDistance)) {
return mob->close_mobs; return mob->m_close_mobs;
} }
return mob_list; return mob_list;
@@ -5946,3 +5941,39 @@ void EntityList::DamageArea(
} }
} }
} }
std::vector<NPC*> EntityList::GetNPCsByIDs(std::vector<uint32> npc_ids)
{
std::vector<NPC*> v;
for (const auto& e : GetNPCList()) {
const auto& n = std::find(npc_ids.begin(), npc_ids.end(), e.second->GetNPCTypeID());
if (e.second) {
if (n != npc_ids.end()) {
continue;
}
v.emplace_back(e.second);
}
}
return v;
}
std::vector<NPC*> EntityList::GetExcludedNPCsByIDs(std::vector<uint32> npc_ids)
{
std::vector<NPC*> v;
for (const auto& e : GetNPCList()) {
const auto& n = std::find(npc_ids.begin(), npc_ids.end(), e.second->GetNPCTypeID());
if (e.second) {
if (n == npc_ids.end()) {
continue;
}
v.emplace_back(e.second);
}
}
return v;
}
+13 -13
View File
@@ -436,23 +436,24 @@ public:
void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app); void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app);
void AEAttack( void AEAttack(
Mob *attacker, Mob* attacker,
float distance, float distance,
int Hand = EQ::invslot::slotPrimary, int16 slot_id = EQ::invslot::slotPrimary,
int count = 0, int hit_count = 0,
bool is_from_spell = false, bool is_from_spell = false,
int attack_rounds = 1 int attack_rounds = 1
); );
void AETaunt(Client *caster, float range = 0, int32 bonus_hate = 0); void AETaunt(Client* caster, float range = 0, int bonus_hate = 0);
void AESpell( void AESpell(
Mob *caster, Mob* caster,
Mob *center, Mob* center,
uint16 spell_id, uint16 spell_id,
bool affect_caster = true, bool affect_caster = true,
int16 resist_adjust = 0, int16 resist_adjust = 0,
int *max_targets = nullptr int* max_targets = nullptr,
bool is_scripted = false
); );
void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); void MassGroupBuff(Mob* caster, Mob* center, uint16 spell_id, bool affect_caster = true);
//trap stuff //trap stuff
Mob* GetTrapTrigger(Trap* trap); Mob* GetTrapTrigger(Trap* trap);
@@ -559,17 +560,16 @@ public:
std::unordered_map<uint16, Mob *> &GetCloseMobList(Mob *mob, float distance = 0.0f); std::unordered_map<uint16, Mob *> &GetCloseMobList(Mob *mob, float distance = 0.0f);
std::vector<NPC*> GetNPCsByIDs(std::vector<uint32> npc_ids);
std::vector<NPC*> GetExcludedNPCsByIDs(std::vector<uint32> npc_ids);
void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); void DepopAll(int NPCTypeID, bool StartSpawnTimer = true);
uint16 GetFreeID(); uint16 GetFreeID();
void RefreshAutoXTargets(Client *c); void RefreshAutoXTargets(Client *c);
void RefreshClientXTargets(Client *c); void RefreshClientXTargets(Client *c);
void SendAlternateAdvancementStats(); void SendAlternateAdvancementStats();
void ScanCloseMobs( void ScanCloseMobs(Mob *scanning_mob);
std::unordered_map<uint16, Mob *> &close_mobs,
Mob *scanning_mob,
bool add_self_to_other_lists = false
);
void GetTrapInfo(Client* c); void GetTrapInfo(Client* c);
bool IsTrapGroupSpawned(uint32 trap_id, uint8 group); bool IsTrapGroupSpawned(uint32 trap_id, uint8 group);
+1
View File
@@ -144,6 +144,7 @@ typedef enum {
EVENT_ENTITY_VARIABLE_UPDATE, EVENT_ENTITY_VARIABLE_UPDATE,
EVENT_AA_LOSS, EVENT_AA_LOSS,
EVENT_SPELL_BLOCKED, EVENT_SPELL_BLOCKED,
EVENT_READ_ITEM,
// Add new events before these or Lua crashes // Add new events before these or Lua crashes
EVENT_SPELL_EFFECT_BOT, EVENT_SPELL_EFFECT_BOT,
+58 -50
View File
@@ -527,7 +527,7 @@ void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, boo
// Are we also doing linear AA acceleration? // Are we also doing linear AA acceleration?
if (RuleB(AA, ModernAAScalingEnabled) && aaexp > 0) if (RuleB(AA, ModernAAScalingEnabled) && aaexp > 0)
{ {
aaexp = ScaleAAXPBasedOnCurrentAATotal(GetAAPoints(), aaexp); aaexp = ScaleAAXPBasedOnCurrentAATotal(GetSpentAA() + GetAAPoints(), aaexp);
} }
// Check for AA XP Cap // Check for AA XP Cap
@@ -607,10 +607,10 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
Message(Chat::Red, "Error in Client::SetEXP. EXP not set."); Message(Chat::Red, "Error in Client::SetEXP. EXP not set.");
return; // Must be invalid class/race return; // Must be invalid class/race
} }
uint32 i = 0; uint32 i = 0;
uint32 membercount = 0; uint32 membercount = 0;
if(GetGroup()) if(GetGroup()) {
{
for (i = 0; i < MAX_GROUP_MEMBERS; i++) { for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
if (GetGroup()->members[i] != nullptr) { if (GetGroup()->members[i] != nullptr) {
membercount++; membercount++;
@@ -627,25 +627,29 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
if (RuleI(Character, ShowExpValues) >= 1) { if (RuleI(Character, ShowExpValues) >= 1) {
if (exp_gained > 0 && aa_exp_gained > 0) { if (exp_gained > 0 && aa_exp_gained > 0) {
exp_amount_message = fmt::format("({}) ({})", exp_gained, aa_exp_gained); exp_amount_message = fmt::format("({}) ({})", exp_gained, aa_exp_gained);
} } else if (exp_gained > 0) {
else if (exp_gained > 0) {
exp_amount_message = fmt::format("({})", exp_gained); exp_amount_message = fmt::format("({})", exp_gained);
} } else {
else {
exp_amount_message = fmt::format("({}) AA", aa_exp_gained); exp_amount_message = fmt::format("({}) AA", aa_exp_gained);
} }
} }
std::string exp_percent_message = ""; std::string exp_percent_message = "";
if (RuleI(Character, ShowExpValues) >= 2) { if (RuleI(Character, ShowExpValues) >= 2) {
if (exp_gained > 0 && aa_exp_gained > 0) exp_percent_message = StringFormat("(%.3f%%, %.3f%%AA)", exp_percent, aa_exp_percent); if (exp_gained > 0 && aa_exp_gained > 0) {
else if (exp_gained > 0) exp_percent_message = StringFormat("(%.3f%%)", exp_percent); exp_percent_message = StringFormat("(%.3f%%, %.3f%%AA)", exp_percent, aa_exp_percent);
else exp_percent_message = StringFormat("(%.3f%%AA)", aa_exp_percent); } else if (exp_gained > 0) {
exp_percent_message = StringFormat("(%.3f%%)", exp_percent);
} else {
exp_percent_message = StringFormat("(%.3f%%AA)", aa_exp_percent);
}
} }
if (isrezzexp) { if (isrezzexp) {
if (RuleI(Character, ShowExpValues) > 0) if (RuleI(Character, ShowExpValues) > 0) {
Message(Chat::Experience, "You regain %s experience from resurrection. %s", exp_amount_message.c_str(), exp_percent_message.c_str()); Message(Chat::Experience, "You regain %s experience from resurrection. %s", exp_amount_message.c_str(), exp_percent_message.c_str());
else MessageString(Chat::Experience, REZ_REGAIN); } else {
MessageString(Chat::Experience, REZ_REGAIN);
}
} else { } else {
if (membercount > 1) { if (membercount > 1) {
if (RuleI(Character, ShowExpValues) > 0) { if (RuleI(Character, ShowExpValues) > 0) {
@@ -673,14 +677,17 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
} }
} }
} }
} } else if(total_add_exp < total_current_exp) { //only loss message if you lose exp, no message if you gained/lost nothing.
else if(total_add_exp < total_current_exp){ //only loss message if you lose exp, no message if you gained/lost nothing.
uint64 exp_lost = current_exp - set_exp; uint64 exp_lost = current_exp - set_exp;
float exp_percent = (float)((float)exp_lost / (float)(GetEXPForLevel(GetLevel() + 1) - GetEXPForLevel(GetLevel())))*(float)100; float exp_percent = (float)((float)exp_lost / (float)(GetEXPForLevel(GetLevel() + 1) - GetEXPForLevel(GetLevel())))*(float)100;
if (RuleI(Character, ShowExpValues) == 1 && exp_lost > 0) Message(Chat::Yellow, "You have lost %i experience.", exp_lost); if (RuleI(Character, ShowExpValues) == 1 && exp_lost > 0) {
else if (RuleI(Character, ShowExpValues) == 2 && exp_lost > 0) Message(Chat::Yellow, "You have lost %i experience. (%.3f%%)", exp_lost, exp_percent); Message(Chat::Yellow, "You have lost %i experience.", exp_lost);
else Message(Chat::Yellow, "You have lost experience."); } else if (RuleI(Character, ShowExpValues) == 2 && exp_lost > 0) {
Message(Chat::Yellow, "You have lost %i experience. (%.3f%%)", exp_lost, exp_percent);
} else {
Message(Chat::Yellow, "You have lost experience.");
}
} }
//check_level represents the level we should be when we have //check_level represents the level we should be when we have
@@ -698,9 +705,10 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
} }
level_count++; level_count++;
if(GetMercenaryID()) if (GetMercenaryID()) {
UpdateMercLevel(); UpdateMercLevel();
} }
}
//see if we lost any levels //see if we lost any levels
while (set_exp < GetEXPForLevel(check_level-1)) { while (set_exp < GetEXPForLevel(check_level-1)) {
check_level--; check_level--;
@@ -709,9 +717,10 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
break; break;
} }
level_increase = false; level_increase = false;
if(GetMercenaryID()) if (GetMercenaryID()) {
UpdateMercLevel(); UpdateMercLevel();
} }
}
check_level--; check_level--;
@@ -747,12 +756,14 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
//Message(Chat::Yellow, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA); //Message(Chat::Yellow, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA);
char val1[20] = { 0 }; char val1[20] = { 0 };
char val2[20] = { 0 }; char val2[20] = { 0 };
if (gained == 1 && m_pp.aapoints == 1)
if (gained == 1 && m_pp.aapoints == 1) {
MessageString(Chat::Experience, GAIN_SINGLE_AA_SINGLE_AA, ConvertArray(m_pp.aapoints, val1)); //You have gained an ability point! You now have %1 ability point. MessageString(Chat::Experience, GAIN_SINGLE_AA_SINGLE_AA, ConvertArray(m_pp.aapoints, val1)); //You have gained an ability point! You now have %1 ability point.
else if (gained == 1 && m_pp.aapoints > 1) } else if (gained == 1 && m_pp.aapoints > 1) {
MessageString(Chat::Experience, GAIN_SINGLE_AA_MULTI_AA, ConvertArray(m_pp.aapoints, val1)); //You have gained an ability point! You now have %1 ability points. MessageString(Chat::Experience, GAIN_SINGLE_AA_MULTI_AA, ConvertArray(m_pp.aapoints, val1)); //You have gained an ability point! You now have %1 ability points.
else } else {
MessageString(Chat::Experience, GAIN_MULTI_AA_MULTI_AA, ConvertArray(gained, val1), ConvertArray(m_pp.aapoints, val2)); //You have gained %1 ability point(s)! You now have %2 ability point(s). MessageString(Chat::Experience, GAIN_MULTI_AA_MULTI_AA, ConvertArray(gained, val1), ConvertArray(m_pp.aapoints, val2)); //You have gained %1 ability point(s)! You now have %2 ability point(s).
}
if (RuleB(AA, SoundForAAEarned)) { if (RuleB(AA, SoundForAAEarned)) {
SendSound(); SendSound();
@@ -765,7 +776,7 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
RecordPlayerEventLog(PlayerEvent::AA_GAIN, PlayerEvent::AAGainedEvent{gained}); RecordPlayerEventLog(PlayerEvent::AA_GAIN, PlayerEvent::AAGainedEvent{gained});
/* QS: PlayerLogAARate */ /* QS: PlayerLogAARate */
if (RuleB(QueryServ, PlayerLogAARate)){ if (RuleB(QueryServ, PlayerLogAARate)) {
int add_points = (m_pp.aapoints - last_unspentAA); int add_points = (m_pp.aapoints - last_unspentAA);
std::string query = StringFormat("INSERT INTO `qs_player_aa_rate_hourly` (char_id, aa_count, hour_time) VALUES (%i, %i, UNIX_TIMESTAMP() - MOD(UNIX_TIMESTAMP(), 3600)) ON DUPLICATE KEY UPDATE `aa_count` = `aa_count` + %i", CharacterID(), add_points, add_points); std::string query = StringFormat("INSERT INTO `qs_player_aa_rate_hourly` (char_id, aa_count, hour_time) VALUES (%i, %i, UNIX_TIMESTAMP() - MOD(UNIX_TIMESTAMP(), 3600)) ON DUPLICATE KEY UPDATE `aa_count` = `aa_count` + %i", CharacterID(), add_points, add_points);
QServ->SendQuery(query.c_str()); QServ->SendQuery(query.c_str());
@@ -774,48 +785,45 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
//Message(Chat::Yellow, "You now have %d skill points available to spend.", m_pp.aapoints); //Message(Chat::Yellow, "You now have %d skill points available to spend.", m_pp.aapoints);
} }
uint8 maxlevel = RuleI(Character, MaxExpLevel) + 1; uint8 max_level = RuleI(Character, MaxExpLevel) + 1;
if(maxlevel <= 1) if (max_level <= 1) {
maxlevel = RuleI(Character, MaxLevel) + 1; max_level = RuleI(Character, MaxLevel) + 1;
if(check_level > maxlevel) {
check_level = maxlevel;
if(RuleB(Character, KeepLevelOverMax)) {
set_exp = GetEXPForLevel(GetLevel()+1);
}
else {
set_exp = GetEXPForLevel(maxlevel);
}
} }
auto client_max_level = GetClientMaxLevel(); auto client_max_level = GetClientMaxLevel();
if (client_max_level) { if (client_max_level) {
if (GetLevel() >= client_max_level) { max_level = client_max_level + 1;
auto exp_needed = GetEXPForLevel(client_max_level);
if (set_exp > exp_needed) {
set_exp = exp_needed;
} }
if (check_level > max_level) {
check_level = max_level;
if (RuleB(Character, KeepLevelOverMax)) {
set_exp = GetEXPForLevel(GetLevel()+1);
} else {
set_exp = GetEXPForLevel(max_level);
} }
} }
if ((GetLevel() != check_level) && !(check_level >= maxlevel)) { if ((GetLevel() != check_level) && !(check_level >= max_level)) {
char val1[20]={0}; char val1[20]={0};
if (level_increase) if (level_increase) {
{ if (level_count == 1) {
if (level_count == 1)
MessageString(Chat::Experience, GAIN_LEVEL, ConvertArray(check_level, val1)); MessageString(Chat::Experience, GAIN_LEVEL, ConvertArray(check_level, val1));
else } else {
Message(Chat::Yellow, "Welcome to level %i!", check_level); Message(Chat::Yellow, "Welcome to level %i!", check_level);
}
if (check_level == RuleI(Character, DeathItemLossLevel) && if (check_level == RuleI(Character, DeathItemLossLevel) &&
m_ClientVersionBit & EQ::versions::maskUFAndEarlier) m_ClientVersionBit & EQ::versions::maskUFAndEarlier) {
MessageString(Chat::Yellow, CORPSE_ITEM_LOST); MessageString(Chat::Yellow, CORPSE_ITEM_LOST);
}
if (check_level == RuleI(Character, DeathExpLossLevel)) if (check_level == RuleI(Character, DeathExpLossLevel)) {
MessageString(Chat::Yellow, CORPSE_EXP_LOST); MessageString(Chat::Yellow, CORPSE_EXP_LOST);
} }
}
uint8 myoldlevel = GetLevel(); uint8 myoldlevel = GetLevel();
@@ -828,9 +836,9 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
} }
//If were at max level then stop gaining experience if we make it to the cap //If were at max level then stop gaining experience if we make it to the cap
if(GetLevel() == maxlevel - 1){ if (GetLevel() == max_level - 1){
uint32 expneeded = GetEXPForLevel(maxlevel); uint32 expneeded = GetEXPForLevel(max_level);
if(set_exp > expneeded) { if (set_exp > expneeded) {
set_exp = expneeded; set_exp = expneeded;
} }
} }
+5 -3
View File
@@ -8,14 +8,15 @@ void command_grantaa(Client *c, const Seperator *sep)
} }
const uint8 unlock_level = sep->IsNumber(1) ? static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[1])) : 0; const uint8 unlock_level = sep->IsNumber(1) ? static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[1])) : 0;
const bool skip_grant_only = sep->IsNumber(2) ? Strings::ToBool(sep->arg[2]) : false;
auto t = c->GetTarget()->CastToClient(); auto t = c->GetTarget()->CastToClient();
t->GrantAllAAPoints(unlock_level); t->GrantAllAAPoints(unlock_level, skip_grant_only);
c->Message( c->Message(
Chat::White, Chat::White,
fmt::format( fmt::format(
"Successfully granted all Alternate Advancements for {}{}.", "Successfully granted all Alternate Advancements for {}{}{}.",
c->GetTargetDescription(t), c->GetTargetDescription(t),
( (
unlock_level ? unlock_level ?
@@ -24,7 +25,8 @@ void command_grantaa(Client *c, const Seperator *sep)
unlock_level unlock_level
) : ) :
"" ""
) ),
skip_grant_only ? "except for grant only AAs" : ""
).c_str() ).c_str()
); );
} }
+1 -1
View File
@@ -17,7 +17,7 @@ void SetMOTD(Client *c, const Seperator *sep)
auto m = (ServerMotd_Struct *) pack->pBuffer; auto m = (ServerMotd_Struct *) pack->pBuffer;
strn0cpy(m->myname, c->GetName(), sizeof(m->myname)); strn0cpy(m->myname, c->GetName(), sizeof(m->myname));
strn0cpy(m->motd, message.c_str(), sizeof(m->motd)); strn0cpy(m->motd, !message.empty() ? message.c_str() : "", sizeof(m->motd));
worldserver.SendPacket(pack); worldserver.SendPacket(pack);
safe_delete(pack); safe_delete(pack);
+5 -6
View File
@@ -160,23 +160,22 @@ void ShowInventory(Client *c, const Seperator *sep)
linker.SetItemInst(inst_main); linker.SetItemInst(inst_main);
if (item_data) { if (item_data && inst_main) {
//auto inst = c->GetInv().GetItem(scope_bit & peekWorld ? EQ::invslot::WORLD_BEGIN + index_main : index_main);
c->Message( c->Message(
Chat::White, Chat::White,
fmt::format( fmt::format(
"Slot {} | {} ({}/{}){}", "Slot {} | {} ({}/{}){}",
((scope_bit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + index_main) : index_main), scope_bit & peekWorld ? EQ::invslot::WORLD_BEGIN + index_main : index_main,
linker.GenerateLink(), linker.GenerateLink(),
item_data->ID, item_data->ID,
c->GetInv().GetItem(((scope_bit &peekWorld) ? (EQ::invslot::WORLD_BEGIN + index_main) : index_main))->GetSerialNumber(), inst_main->GetSerialNumber(),
(
inst_main->IsStackable() && inst_main->GetCharges() > 0 ? inst_main->IsStackable() && inst_main->GetCharges() > 0 ?
fmt::format( fmt::format(
" (Stack of {})", " (Stack of {})",
inst_main->GetCharges() inst_main->GetCharges()
) : ) :
"" ""
)
).c_str() ).c_str()
); );
} }
@@ -239,7 +238,7 @@ void ShowInventory(Client *c, const Seperator *sep)
sub_index, sub_index,
linker.GenerateLink(), linker.GenerateLink(),
item_data->ID, item_data->ID,
c->GetInv().GetItem(EQ::InventoryProfile::CalcSlotId(index_main, sub_index))->GetSerialNumber(), inst_sub->GetSerialNumber(),
( (
inst_sub->IsStackable() && inst_sub->GetCharges() > 0 ? inst_sub->IsStackable() && inst_sub->GetCharges() > 0 ?
fmt::format( fmt::format(
+9 -4
View File
@@ -2117,14 +2117,19 @@ void Group::UnDelegateMarkNPC(const char *OldNPCMarkerName)
void Group::SaveGroupLeaderAA() void Group::SaveGroupLeaderAA()
{ {
const uint32 group_id = GetID();
if (!group_id) {
return;
}
// Stores the Group Leaders Leadership AA data from the Player Profile as a blob in the group_leaders table. // Stores the Group Leaders Leadership AA data from the Player Profile as a blob in the group_leaders table.
// This is done so that group members not in the same zone as the Leader still have access to this information. // This is done so that group members not in the same zone as the Leader still have access to this information.
std::string aa((char *) &LeaderAbilities, sizeof(GroupLeadershipAA_Struct)); std::string aa((char*) &LeaderAbilities, sizeof(GroupLeadershipAA_Struct));
auto results = GroupLeadersRepository::UpdateLeadershipAA(database, aa, GetID());
if (!results) { if (!GroupLeadersRepository::UpdateLeadershipAA(database, aa, group_id)) {
LogError("Unable to store GroupLeadershipAA for group_id: [{}]", GetID()); LogError("Unable to store GroupLeadershipAA for group_id: [{}]", group_id);
} }
} }
+5
View File
@@ -3431,6 +3431,11 @@ void Client::SetBandolier(const EQApplicationPacket *app)
} }
} }
} }
if (RuleI(Character, BandolierSwapDelay) > 0) {
bandolier_throttle_timer.Start(RuleI(Character, BandolierSwapDelay));
}
// finally, recalculate any stat bonuses from the item change // finally, recalculate any stat bonuses from the item change
CalcBonuses(); CalcBonuses();
} }
+77 -17
View File
@@ -1214,25 +1214,23 @@ void Lua_Client::AddPVPPoints(uint32 points) {
self->AddPVPPoints(points); self->AddPVPPoints(points);
} }
void Lua_Client::AddCrystals(uint32 radiant, uint32 ebon) { void Lua_Client::AddCrystals(uint32 radiant_count, uint32 ebon_count) {
Lua_Safe_Call_Void(); Lua_Safe_Call_Void();
if (ebon != 0) { if (ebon_count != 0) {
if (ebon > 0) { if (ebon_count > 0) {
self->AddEbonCrystals(ebon); self->AddEbonCrystals(ebon_count);
return; } else {
self->RemoveEbonCrystals(ebon_count);
}
} }
self->RemoveEbonCrystals(ebon); if (radiant_count != 0) {
if (radiant_count > 0) {
self->AddRadiantCrystals(radiant_count);
} else {
self->RemoveRadiantCrystals(radiant_count);
} }
if (radiant != 0) {
if (radiant > 0) {
self->AddRadiantCrystals(radiant);
return;
}
self->RemoveRadiantCrystals(radiant);
} }
} }
@@ -1411,9 +1409,9 @@ void Lua_Client::FailTask(int task) {
self->FailTask(task); self->FailTask(task);
} }
bool Lua_Client::IsTaskCompleted(int task) { bool Lua_Client::IsTaskCompleted(int task_id) {
Lua_Safe_Call_Bool(); Lua_Safe_Call_Bool();
return self->IsTaskCompleted(task) != 0; return self->IsTaskCompleted(task_id);
} }
bool Lua_Client::IsTaskActive(int task) { bool Lua_Client::IsTaskActive(int task) {
@@ -1771,7 +1769,7 @@ int Lua_Client::CalcATK() {
return self->CalcATK(); return self->CalcATK();
} }
void Lua_Client::FilteredMessage(Mob *sender, uint32 type, int filter, const char *message) void Lua_Client::FilteredMessage(Lua_Mob sender, uint32 type, int filter, const char *message)
{ {
Lua_Safe_Call_Void(); Lua_Safe_Call_Void();
self->FilteredMessage(sender, type, (eqFilterType)filter, message); self->FilteredMessage(sender, type, (eqFilterType)filter, message);
@@ -3218,6 +3216,12 @@ void Lua_Client::GrantAllAAPoints(uint8 unlock_level)
self->GrantAllAAPoints(unlock_level); self->GrantAllAAPoints(unlock_level);
} }
void Lua_Client::GrantAllAAPoints(uint8 unlock_level, bool skip_grant_only)
{
Lua_Safe_Call_Void();
self->GrantAllAAPoints(unlock_level, skip_grant_only);
}
void Lua_Client::AddEbonCrystals(uint32 amount) void Lua_Client::AddEbonCrystals(uint32 amount)
{ {
Lua_Safe_Call_Void(); Lua_Safe_Call_Void();
@@ -3381,6 +3385,57 @@ uint8 Lua_Client::GetSkillTrainLevel(int skill_id)
return self->GetSkillTrainLevel(static_cast<EQ::skills::SkillType>(skill_id), self->GetClass()); return self->GetSkillTrainLevel(static_cast<EQ::skills::SkillType>(skill_id), self->GetClass());
} }
bool Lua_Client::AreTasksCompleted(luabind::object task_ids)
{
Lua_Safe_Call_Int();
if (luabind::type(task_ids) != LUA_TTABLE) {
return false;
}
std::vector<int> v;
int index = 1;
while (luabind::type(task_ids[index]) != LUA_TNIL) {
auto current_id = task_ids[index];
int task_id = 0;
if (luabind::type(current_id) != LUA_TNIL) {
try {
task_id = luabind::object_cast<int>(current_id);
} catch(luabind::cast_failed &) {
}
} else {
break;
}
v.push_back(task_id);
++index;
}
if (v.empty()) {
return false;
}
return self->AreTasksCompleted(v);
}
void Lua_Client::AreaTaunt()
{
Lua_Safe_Call_Void();
entity_list.AETaunt(self);
}
void Lua_Client::AreaTaunt(float range)
{
Lua_Safe_Call_Void();
entity_list.AETaunt(self, range);
}
void Lua_Client::AreaTaunt(float range, int bonus_hate)
{
Lua_Safe_Call_Void();
entity_list.AETaunt(self, range, bonus_hate);
}
luabind::scope lua_register_client() { luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client") return luabind::class_<Lua_Client, Lua_Mob>("Client")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
@@ -3427,6 +3482,10 @@ luabind::scope lua_register_client() {
.def("ApplySpellRaid", (void(Lua_Client::*)(int,int,int,bool))&Lua_Client::ApplySpellRaid) .def("ApplySpellRaid", (void(Lua_Client::*)(int,int,int,bool))&Lua_Client::ApplySpellRaid)
.def("ApplySpellRaid", (void(Lua_Client::*)(int,int,int,bool,bool))&Lua_Client::ApplySpellRaid) .def("ApplySpellRaid", (void(Lua_Client::*)(int,int,int,bool,bool))&Lua_Client::ApplySpellRaid)
.def("ApplySpellRaid", (void(Lua_Client::*)(int,int,int,bool,bool,bool))&Lua_Client::ApplySpellRaid) .def("ApplySpellRaid", (void(Lua_Client::*)(int,int,int,bool,bool,bool))&Lua_Client::ApplySpellRaid)
.def("AreTasksCompleted", (bool(Lua_Client::*)(luabind::object))&Lua_Client::AreTasksCompleted)
.def("AreaTaunt", (void(Lua_Client::*)(void))&Lua_Client::AreaTaunt)
.def("AreaTaunt", (void(Lua_Client::*)(float))&Lua_Client::AreaTaunt)
.def("AreaTaunt", (void(Lua_Client::*)(float, int))&Lua_Client::AreaTaunt)
.def("AssignTask", (void(Lua_Client::*)(int))&Lua_Client::AssignTask) .def("AssignTask", (void(Lua_Client::*)(int))&Lua_Client::AssignTask)
.def("AssignTask", (void(Lua_Client::*)(int,int))&Lua_Client::AssignTask) .def("AssignTask", (void(Lua_Client::*)(int,int))&Lua_Client::AssignTask)
.def("AssignTask", (void(Lua_Client::*)(int,int,bool))&Lua_Client::AssignTask) .def("AssignTask", (void(Lua_Client::*)(int,int,bool))&Lua_Client::AssignTask)
@@ -3646,6 +3705,7 @@ luabind::scope lua_register_client() {
.def("GoFish", (void(Lua_Client::*)(void))&Lua_Client::GoFish) .def("GoFish", (void(Lua_Client::*)(void))&Lua_Client::GoFish)
.def("GrantAllAAPoints", (void(Lua_Client::*)(void))&Lua_Client::GrantAllAAPoints) .def("GrantAllAAPoints", (void(Lua_Client::*)(void))&Lua_Client::GrantAllAAPoints)
.def("GrantAllAAPoints", (void(Lua_Client::*)(uint8))&Lua_Client::GrantAllAAPoints) .def("GrantAllAAPoints", (void(Lua_Client::*)(uint8))&Lua_Client::GrantAllAAPoints)
.def("GrantAllAAPoints", (void(Lua_Client::*)(uint8,bool))&Lua_Client::GrantAllAAPoints)
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int))&Lua_Client::GrantAlternateAdvancementAbility) .def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int))&Lua_Client::GrantAlternateAdvancementAbility)
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int, bool))&Lua_Client::GrantAlternateAdvancementAbility) .def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int, bool))&Lua_Client::GrantAlternateAdvancementAbility)
.def("GuildID", (uint32(Lua_Client::*)(void))&Lua_Client::GuildID) .def("GuildID", (uint32(Lua_Client::*)(void))&Lua_Client::GuildID)
+8 -3
View File
@@ -316,7 +316,7 @@ public:
void KeyRingAdd(uint32 item); void KeyRingAdd(uint32 item);
bool KeyRingCheck(uint32 item); bool KeyRingCheck(uint32 item);
void AddPVPPoints(uint32 points); void AddPVPPoints(uint32 points);
void AddCrystals(uint32 radiant, uint32 ebon); void AddCrystals(uint32 radiant_count, uint32 ebon_count);
void SetEbonCrystals(uint32 value); void SetEbonCrystals(uint32 value);
void SetRadiantCrystals(uint32 value); void SetRadiantCrystals(uint32 value);
uint32 GetPVPPoints(); uint32 GetPVPPoints();
@@ -360,7 +360,7 @@ public:
void AssignTask(int task_id, int npc_id); void AssignTask(int task_id, int npc_id);
void AssignTask(int task_id, int npc_id, bool enforce_level_requirement); void AssignTask(int task_id, int npc_id, bool enforce_level_requirement);
void FailTask(int task); void FailTask(int task);
bool IsTaskCompleted(int task); bool IsTaskCompleted(int task_id);
bool IsTaskActive(int task); bool IsTaskActive(int task);
bool IsTaskActivityActive(int task, int activity); bool IsTaskActivityActive(int task, int activity);
void LockSharedTask(bool lock); void LockSharedTask(bool lock);
@@ -424,7 +424,7 @@ public:
bool IsDead(); bool IsDead();
int CalcCurrentWeight(); int CalcCurrentWeight();
int CalcATK(); int CalcATK();
void FilteredMessage(Mob *sender, uint32 type, int filter, const char* message); void FilteredMessage(Lua_Mob sender, uint32 type, int filter, const char* message);
void EnableAreaHPRegen(int value); void EnableAreaHPRegen(int value);
void DisableAreaHPRegen(); void DisableAreaHPRegen();
void EnableAreaManaRegen(int value); void EnableAreaManaRegen(int value);
@@ -487,6 +487,7 @@ public:
void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration); void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration);
void GrantAllAAPoints(); void GrantAllAAPoints();
void GrantAllAAPoints(uint8 unlock_level); void GrantAllAAPoints(uint8 unlock_level);
void GrantAllAAPoints(uint8 unlock_level, bool skip_grant_only);
void AddEbonCrystals(uint32 amount); void AddEbonCrystals(uint32 amount);
void AddRadiantCrystals(uint32 amount); void AddRadiantCrystals(uint32 amount);
void RemoveEbonCrystals(uint32 amount); void RemoveEbonCrystals(uint32 amount);
@@ -505,6 +506,9 @@ public:
void DescribeSpecialAbilities(Lua_NPC n); void DescribeSpecialAbilities(Lua_NPC n);
void ResetLeadershipAA(); void ResetLeadershipAA();
uint8 GetSkillTrainLevel(int skill_id); uint8 GetSkillTrainLevel(int skill_id);
void AreaTaunt();
void AreaTaunt(float range);
void AreaTaunt(float range, int bonus_hate);
void ApplySpell(int spell_id); void ApplySpell(int spell_id);
void ApplySpell(int spell_id, int duration); void ApplySpell(int spell_id, int duration);
@@ -577,6 +581,7 @@ public:
void CampAllBots(uint8 class_id); void CampAllBots(uint8 class_id);
bool RemoveAAPoints(uint32 points); bool RemoveAAPoints(uint32 points);
bool RemoveAlternateCurrencyValue(uint32 currency_id, uint32 amount); bool RemoveAlternateCurrencyValue(uint32 currency_id, uint32 amount);
bool AreTasksCompleted(luabind::object task_ids);
void DialogueWindow(std::string markdown); void DialogueWindow(std::string markdown);
+108
View File
@@ -180,6 +180,96 @@ uint32 Lua_Door::GetID() {
return self->GetID(); return self->GetID();
} }
uint8 Lua_Door::GetTriggerDoorID() {
Lua_Safe_Call_Int();
return self->GetTriggerDoorID();
}
uint8 Lua_Door::GetTriggerType() {
Lua_Safe_Call_Int();
return self->GetTriggerType();
}
bool Lua_Door::IsLDoNDoor() {
Lua_Safe_Call_Bool();
return self->IsLDoNDoor();
}
uint32 Lua_Door::GetClientVersionMask() {
Lua_Safe_Call_Int();
return self->GetClientVersionMask();
}
int Lua_Door::GetDoorParam() {
Lua_Safe_Call_Int();
return self->GetDoorParam();
}
bool Lua_Door::HasDestinationZone() {
Lua_Safe_Call_Bool();
return self->HasDestinationZone();
}
bool Lua_Door::IsDestinationZoneSame() {
Lua_Safe_Call_Bool();
return self->IsDestinationZoneSame();
}
bool Lua_Door::IsDoorBlacklisted() {
Lua_Safe_Call_Bool();
return self->IsDoorBlacklisted();
}
std::string Lua_Door::GetDestinationZoneName() {
Lua_Safe_Call_String();
return self->GetDestinationZoneName();
}
int Lua_Door::GetDestinationInstanceID() {
Lua_Safe_Call_Int();
return self->GetDestinationInstanceID();
}
float Lua_Door::GetDestinationX() {
Lua_Safe_Call_Real();
return self->GetDestinationX();
}
float Lua_Door::GetDestinationY() {
Lua_Safe_Call_Real();
return self->GetDestinationY();
}
float Lua_Door::GetDestinationZ() {
Lua_Safe_Call_Real();
return self->GetDestinationZ();
}
float Lua_Door::GetDestinationHeading() {
Lua_Safe_Call_Real();
return self->GetDestinationHeading();
}
int Lua_Door::GetDzSwitchID() {
Lua_Safe_Call_Int();
return self->GetDzSwitchID();
}
int Lua_Door::GetInvertState() {
Lua_Safe_Call_Int();
return self->GetInvertState();
}
void Lua_Door::SetInvertState(int invert_state) {
Lua_Safe_Call_Void();
self->SetInvertState(invert_state);
}
uint32 Lua_Door::GetGuildID() {
Lua_Safe_Call_Int();
return self->GetGuildID();
}
luabind::scope lua_register_door() { luabind::scope lua_register_door() {
return luabind::class_<Lua_Door, Lua_Entity>("Door") return luabind::class_<Lua_Door, Lua_Entity>("Door")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
@@ -191,24 +281,42 @@ luabind::scope lua_register_door() {
.def("ForceOpen", (void(Lua_Door::*)(Lua_Mob))&Lua_Door::ForceOpen) .def("ForceOpen", (void(Lua_Door::*)(Lua_Mob))&Lua_Door::ForceOpen)
.def("ForceOpen", (void(Lua_Door::*)(Lua_Mob,bool))&Lua_Door::ForceOpen) .def("ForceOpen", (void(Lua_Door::*)(Lua_Mob,bool))&Lua_Door::ForceOpen)
.def("GetDisableTimer", (bool(Lua_Door::*)(void))&Lua_Door::GetDisableTimer) .def("GetDisableTimer", (bool(Lua_Door::*)(void))&Lua_Door::GetDisableTimer)
.def("GetClientVersionMask", (uint32(Lua_Door::*)(void))&Lua_Door::GetClientVersionMask)
.def("GetDestinationHeading", (float(Lua_Door::*)(void))&Lua_Door::GetDestinationHeading)
.def("GetDestinationInstanceID", (int(Lua_Door::*)(void))&Lua_Door::GetDestinationInstanceID)
.def("GetDestinationX", (float(Lua_Door::*)(void))&Lua_Door::GetDestinationX)
.def("GetDestinationY", (float(Lua_Door::*)(void))&Lua_Door::GetDestinationY)
.def("GetDestinationZ", (float(Lua_Door::*)(void))&Lua_Door::GetDestinationZ)
.def("GetDestinationZoneName", (std::string(Lua_Door::*)(void))&Lua_Door::GetDestinationZoneName)
.def("GetDoorDBID", (uint32(Lua_Door::*)(void))&Lua_Door::GetDoorDBID) .def("GetDoorDBID", (uint32(Lua_Door::*)(void))&Lua_Door::GetDoorDBID)
.def("GetDoorID", (uint32(Lua_Door::*)(void))&Lua_Door::GetDoorID) .def("GetDoorID", (uint32(Lua_Door::*)(void))&Lua_Door::GetDoorID)
.def("GetDoorName", (const char*(Lua_Door::*)(void))&Lua_Door::GetDoorName) .def("GetDoorName", (const char*(Lua_Door::*)(void))&Lua_Door::GetDoorName)
.def("GetDoorParam", (int(Lua_Door::*)(void))&Lua_Door::GetDoorParam)
.def("GetDzSwitchID", (int(Lua_Door::*)(void))&Lua_Door::GetDzSwitchID)
.def("GetGuildID", (uint32(Lua_Door::*)(void))&Lua_Door::GetGuildID)
.def("GetHeading", (float(Lua_Door::*)(void))&Lua_Door::GetHeading) .def("GetHeading", (float(Lua_Door::*)(void))&Lua_Door::GetHeading)
.def("GetID", (uint32(Lua_Door::*)(void))&Lua_Door::GetID) .def("GetID", (uint32(Lua_Door::*)(void))&Lua_Door::GetID)
.def("GetIncline", (uint32(Lua_Door::*)(void))&Lua_Door::GetIncline) .def("GetIncline", (uint32(Lua_Door::*)(void))&Lua_Door::GetIncline)
.def("GetInvertState", (int(Lua_Door::*)(void))&Lua_Door::GetInvertState)
.def("GetKeyItem", (uint32(Lua_Door::*)(void))&Lua_Door::GetKeyItem) .def("GetKeyItem", (uint32(Lua_Door::*)(void))&Lua_Door::GetKeyItem)
.def("GetLockPick", (uint32(Lua_Door::*)(void))&Lua_Door::GetLockPick) .def("GetLockPick", (uint32(Lua_Door::*)(void))&Lua_Door::GetLockPick)
.def("GetNoKeyring", (int(Lua_Door::*)(void))&Lua_Door::GetNoKeyring) .def("GetNoKeyring", (int(Lua_Door::*)(void))&Lua_Door::GetNoKeyring)
.def("GetOpenType", (uint32(Lua_Door::*)(void))&Lua_Door::GetOpenType) .def("GetOpenType", (uint32(Lua_Door::*)(void))&Lua_Door::GetOpenType)
.def("GetSize", (uint32(Lua_Door::*)(void))&Lua_Door::GetSize) .def("GetSize", (uint32(Lua_Door::*)(void))&Lua_Door::GetSize)
.def("GetTriggerDoorID", (uint8(Lua_Door::*)(void))&Lua_Door::GetTriggerDoorID)
.def("GetTriggerType", (uint8(Lua_Door::*)(void))&Lua_Door::GetTriggerType)
.def("GetX", (float(Lua_Door::*)(void))&Lua_Door::GetX) .def("GetX", (float(Lua_Door::*)(void))&Lua_Door::GetX)
.def("GetY", (float(Lua_Door::*)(void))&Lua_Door::GetY) .def("GetY", (float(Lua_Door::*)(void))&Lua_Door::GetY)
.def("GetZ", (float(Lua_Door::*)(void))&Lua_Door::GetZ) .def("GetZ", (float(Lua_Door::*)(void))&Lua_Door::GetZ)
.def("HasDestinationZone", (bool(Lua_Door::*)(void))&Lua_Door::HasDestinationZone)
.def("IsDestinationZoneSame", (bool(Lua_Door::*)(void))&Lua_Door::IsDestinationZoneSame)
.def("IsDoorBlacklisted", (bool(Lua_Door::*)(void))&Lua_Door::IsDoorBlacklisted)
.def("IsLDoNDoor", (bool(Lua_Door::*)(void))&Lua_Door::IsLDoNDoor)
.def("SetDisableTimer", (void(Lua_Door::*)(bool))&Lua_Door::SetDisableTimer) .def("SetDisableTimer", (void(Lua_Door::*)(bool))&Lua_Door::SetDisableTimer)
.def("SetDoorName", (void(Lua_Door::*)(const char*))&Lua_Door::SetDoorName) .def("SetDoorName", (void(Lua_Door::*)(const char*))&Lua_Door::SetDoorName)
.def("SetHeading", (void(Lua_Door::*)(float))&Lua_Door::SetHeading) .def("SetHeading", (void(Lua_Door::*)(float))&Lua_Door::SetHeading)
.def("SetIncline", (void(Lua_Door::*)(uint32))&Lua_Door::SetIncline) .def("SetIncline", (void(Lua_Door::*)(uint32))&Lua_Door::SetIncline)
.def("SetInvertState", (void(Lua_Door::*)(int))&Lua_Door::SetInvertState)
.def("SetKeyItem", (void(Lua_Door::*)(uint32))&Lua_Door::SetKeyItem) .def("SetKeyItem", (void(Lua_Door::*)(uint32))&Lua_Door::SetKeyItem)
.def("SetLocation", (void(Lua_Door::*)(float,float,float))&Lua_Door::SetLocation) .def("SetLocation", (void(Lua_Door::*)(float,float,float))&Lua_Door::SetLocation)
.def("SetLockPick", (void(Lua_Door::*)(uint32))&Lua_Door::SetLockPick) .def("SetLockPick", (void(Lua_Door::*)(uint32))&Lua_Door::SetLockPick)
+18
View File
@@ -63,6 +63,24 @@ public:
void ForceClose(Lua_Mob sender); void ForceClose(Lua_Mob sender);
void ForceClose(Lua_Mob sender, bool alt_mode); void ForceClose(Lua_Mob sender, bool alt_mode);
uint32 GetID(); uint32 GetID();
uint8 GetTriggerDoorID();
uint8 GetTriggerType();
bool IsLDoNDoor();
uint32 GetClientVersionMask();
int GetDoorParam();
bool HasDestinationZone();
bool IsDestinationZoneSame();
bool IsDoorBlacklisted();
std::string GetDestinationZoneName();
int GetDestinationInstanceID();
float GetDestinationX();
float GetDestinationY();
float GetDestinationZ();
float GetDestinationHeading();
int GetDzSwitchID();
int GetInvertState();
void SetInvertState(int invert_state);
uint32 GetGuildID();
}; };
#endif #endif
+8
View File
@@ -12,6 +12,7 @@
#include "lua_door.h" #include "lua_door.h"
#include "lua_bot.h" #include "lua_bot.h"
#include "lua_merc.h"
bool Lua_Entity::IsClient() { bool Lua_Entity::IsClient() {
Lua_Safe_Call_Bool(); Lua_Safe_Call_Bool();
@@ -140,6 +141,12 @@ Lua_Bot Lua_Entity::CastToBot() {
return Lua_Bot(b); return Lua_Bot(b);
} }
Lua_Merc Lua_Entity::CastToMerc() {
void *d = GetLuaPtrData();
Merc *m = reinterpret_cast<Merc*>(d);
return Lua_Merc(m);
}
luabind::scope lua_register_entity() { luabind::scope lua_register_entity() {
return luabind::class_<Lua_Entity>("Entity") return luabind::class_<Lua_Entity>("Entity")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
@@ -149,6 +156,7 @@ luabind::scope lua_register_entity() {
.def("CastToClient", &Lua_Entity::CastToClient) .def("CastToClient", &Lua_Entity::CastToClient)
.def("CastToCorpse", &Lua_Entity::CastToCorpse) .def("CastToCorpse", &Lua_Entity::CastToCorpse)
.def("CastToDoor", &Lua_Entity::CastToDoor) .def("CastToDoor", &Lua_Entity::CastToDoor)
.def("CastToMerc", &Lua_Entity::CastToMerc)
.def("CastToMob", &Lua_Entity::CastToMob) .def("CastToMob", &Lua_Entity::CastToMob)
.def("CastToNPC", &Lua_Entity::CastToNPC) .def("CastToNPC", &Lua_Entity::CastToNPC)
.def("CastToObject", &Lua_Entity::CastToObject) .def("CastToObject", &Lua_Entity::CastToObject)
+2
View File
@@ -7,6 +7,7 @@
class Entity; class Entity;
class Lua_Client; class Lua_Client;
class Lua_Bot; class Lua_Bot;
class Lua_Merc;
class Lua_NPC; class Lua_NPC;
class Lua_Mob; class Lua_Mob;
struct Lua_HateList; struct Lua_HateList;
@@ -59,6 +60,7 @@ public:
Lua_Object CastToObject(); Lua_Object CastToObject();
Lua_Door CastToDoor(); Lua_Door CastToDoor();
Lua_Bot CastToBot(); Lua_Bot CastToBot();
Lua_Merc CastToMerc();
}; };
#endif #endif
+161
View File
@@ -16,6 +16,7 @@
#include "lua_spawn.h" #include "lua_spawn.h"
#include "lua_bot.h" #include "lua_bot.h"
#include "lua_merc.h"
struct Lua_Mob_List { struct Lua_Mob_List {
std::vector<Lua_Mob> entries; std::vector<Lua_Mob> entries;
@@ -680,11 +681,167 @@ Lua_Mob_List Lua_EntityList::GetCloseMobList(Lua_Mob mob, float distance, bool i
return ret; return ret;
} }
void Lua_EntityList::AreaAttack(Lua_Mob attacker, float distance)
{
Lua_Safe_Call_Void();
self->AEAttack(attacker, distance);
}
void Lua_EntityList::AreaAttack(Lua_Mob attacker, float distance, int16 slot_id)
{
Lua_Safe_Call_Void();
self->AEAttack(attacker, distance, slot_id);
}
void Lua_EntityList::AreaAttack(Lua_Mob attacker, float distance, int16 slot_id, int count)
{
Lua_Safe_Call_Void();
self->AEAttack(attacker, distance, slot_id, count);
}
void Lua_EntityList::AreaAttack(Lua_Mob attacker, float distance, int16 slot_id, int count, bool is_from_spell)
{
Lua_Safe_Call_Void();
self->AEAttack(attacker, distance, slot_id, count, is_from_spell);
}
void Lua_EntityList::AreaAttack(Lua_Mob attacker, float distance, int16 slot_id, int count, bool is_from_spell, int attack_rounds)
{
Lua_Safe_Call_Void();
self->AEAttack(attacker, distance, slot_id, count, is_from_spell, attack_rounds);
}
void Lua_EntityList::AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id)
{
Lua_Safe_Call_Void();
self->AESpell(caster, center, spell_id);
}
void Lua_EntityList::AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster)
{
Lua_Safe_Call_Void();
self->AESpell(caster, center, spell_id, affect_caster);
}
void Lua_EntityList::AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust)
{
Lua_Safe_Call_Void();
self->AESpell(caster, center, spell_id, affect_caster, resist_adjust);
}
void Lua_EntityList::AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int max_targets)
{
Lua_Safe_Call_Void();
self->AESpell(caster, center, spell_id, affect_caster, resist_adjust, &max_targets);
}
void Lua_EntityList::AreaTaunt(Lua_Client caster)
{
Lua_Safe_Call_Void();
self->AETaunt(caster);
}
void Lua_EntityList::AreaTaunt(Lua_Client caster, float range)
{
Lua_Safe_Call_Void();
self->AETaunt(caster, range);
}
void Lua_EntityList::AreaTaunt(Lua_Client caster, float range, int bonus_hate)
{
Lua_Safe_Call_Void();
self->AETaunt(caster, range, bonus_hate);
}
void Lua_EntityList::MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_id)
{
Lua_Safe_Call_Void();
self->MassGroupBuff(caster, center, spell_id);
}
void Lua_EntityList::MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster)
{
Lua_Safe_Call_Void();
self->MassGroupBuff(caster, center, spell_id, affect_caster);
}
Lua_NPC_List Lua_EntityList::GetNPCsByExcludedIDs(luabind::adl::object table)
{
Lua_Safe_Call_Class(Lua_NPC_List);
Lua_NPC_List ret;
if (luabind::type(table) != LUA_TTABLE) {
return ret;
}
if (d_) {
auto self = reinterpret_cast<NativeType*>(d_);
std::vector<uint32> ids;
int index = 1;
while (luabind::type(table[index]) != LUA_TNIL) {
ids.emplace_back(luabind::object_cast<uint32>(table[index]));
index++;
}
const auto& l = self->GetExcludedNPCsByIDs(ids);
for (const auto& e : l) {
ret.entries.emplace_back(Lua_NPC(e));
}
}
return ret;
}
Lua_NPC_List Lua_EntityList::GetNPCsByIDs(luabind::adl::object table)
{
Lua_Safe_Call_Class(Lua_NPC_List);
Lua_NPC_List ret;
if (luabind::type(table) != LUA_TTABLE) {
return ret;
}
if (d_) {
auto self = reinterpret_cast<NativeType*>(d_);
std::vector<uint32> ids;
int index = 1;
while (luabind::type(table[index]) != LUA_TNIL) {
ids.emplace_back(luabind::object_cast<uint32>(table[index]));
index++;
}
const auto& l = self->GetNPCsByIDs(ids);
for (const auto& e : l) {
ret.entries.emplace_back(Lua_NPC(e));
}
}
return ret;
}
luabind::scope lua_register_entity_list() { luabind::scope lua_register_entity_list() {
return luabind::class_<Lua_EntityList>("EntityList") return luabind::class_<Lua_EntityList>("EntityList")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
.property("null", &Lua_EntityList::Null) .property("null", &Lua_EntityList::Null)
.property("valid", &Lua_EntityList::Valid) .property("valid", &Lua_EntityList::Valid)
.def("AreaAttack", (void(Lua_EntityList::*)(Lua_Mob, float))&Lua_EntityList::AreaAttack)
.def("AreaAttack", (void(Lua_EntityList::*)(Lua_Mob, float, int16))&Lua_EntityList::AreaAttack)
.def("AreaAttack", (void(Lua_EntityList::*)(Lua_Mob, float, int16, int))&Lua_EntityList::AreaAttack)
.def("AreaAttack", (void(Lua_EntityList::*)(Lua_Mob, float, int16, int, bool))&Lua_EntityList::AreaAttack)
.def("AreaAttack", (void(Lua_EntityList::*)(Lua_Mob, float, int16, int, bool, int))&Lua_EntityList::AreaAttack)
.def("AreaSpell", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob, uint16))&Lua_EntityList::AreaSpell)
.def("AreaSpell", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob, uint16, bool))&Lua_EntityList::AreaSpell)
.def("AreaSpell", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob, uint16, bool, int16))&Lua_EntityList::AreaSpell)
.def("AreaSpell", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob, uint16, bool, int16, int))&Lua_EntityList::AreaSpell)
.def("AreaTaunt", (void(Lua_EntityList::*)(Lua_Client))&Lua_EntityList::AreaTaunt)
.def("AreaTaunt", (void(Lua_EntityList::*)(Lua_Client, float))&Lua_EntityList::AreaTaunt)
.def("AreaTaunt", (void(Lua_EntityList::*)(Lua_Client, float, int))&Lua_EntityList::AreaTaunt)
.def("CanAddHateForMob", (bool(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::CanAddHateForMob) .def("CanAddHateForMob", (bool(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::CanAddHateForMob)
.def("ChannelMessage", (void(Lua_EntityList::*)(Lua_Mob, int, uint8, const char*))&Lua_EntityList::ChannelMessage) .def("ChannelMessage", (void(Lua_EntityList::*)(Lua_Mob, int, uint8, const char*))&Lua_EntityList::ChannelMessage)
.def("ClearClientPetitionQueue", (void(Lua_EntityList::*)(void))&Lua_EntityList::ClearClientPetitionQueue) .def("ClearClientPetitionQueue", (void(Lua_EntityList::*)(void))&Lua_EntityList::ClearClientPetitionQueue)
@@ -733,6 +890,8 @@ luabind::scope lua_register_entity_list() {
.def("GetNPCByNPCTypeID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByNPCTypeID) .def("GetNPCByNPCTypeID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByNPCTypeID)
.def("GetNPCBySpawnID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCBySpawnID) .def("GetNPCBySpawnID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCBySpawnID)
.def("GetNPCList", (Lua_NPC_List(Lua_EntityList::*)(void))&Lua_EntityList::GetNPCList) .def("GetNPCList", (Lua_NPC_List(Lua_EntityList::*)(void))&Lua_EntityList::GetNPCList)
.def("GetNPCsByExcludedIDs", (Lua_NPC_List(Lua_EntityList::*)(luabind::adl::object))&Lua_EntityList::GetNPCsByExcludedIDs)
.def("GetNPCsByIDs", (Lua_NPC_List(Lua_EntityList::*)(luabind::adl::object))&Lua_EntityList::GetNPCsByIDs)
.def("GetObjectByDBID", (Lua_Object(Lua_EntityList::*)(uint32))&Lua_EntityList::GetObjectByDBID) .def("GetObjectByDBID", (Lua_Object(Lua_EntityList::*)(uint32))&Lua_EntityList::GetObjectByDBID)
.def("GetObjectByID", (Lua_Object(Lua_EntityList::*)(int))&Lua_EntityList::GetObjectByID) .def("GetObjectByID", (Lua_Object(Lua_EntityList::*)(int))&Lua_EntityList::GetObjectByID)
.def("GetObjectList", (Lua_Object_List(Lua_EntityList::*)(void))&Lua_EntityList::GetObjectList) .def("GetObjectList", (Lua_Object_List(Lua_EntityList::*)(void))&Lua_EntityList::GetObjectList)
@@ -759,6 +918,8 @@ luabind::scope lua_register_entity_list() {
.def("Marquee", (void(Lua_EntityList::*)(uint32, std::string))&Lua_EntityList::Marquee) .def("Marquee", (void(Lua_EntityList::*)(uint32, std::string))&Lua_EntityList::Marquee)
.def("Marquee", (void(Lua_EntityList::*)(uint32, std::string, uint32))&Lua_EntityList::Marquee) .def("Marquee", (void(Lua_EntityList::*)(uint32, std::string, uint32))&Lua_EntityList::Marquee)
.def("Marquee", (void(Lua_EntityList::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_EntityList::Marquee) .def("Marquee", (void(Lua_EntityList::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_EntityList::Marquee)
.def("MassGroupBuff", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob, uint16))&Lua_EntityList::MassGroupBuff)
.def("MassGroupBuff", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob, uint16, bool))&Lua_EntityList::MassGroupBuff)
.def("Message", (void(Lua_EntityList::*)(uint32, uint32, const char*))&Lua_EntityList::Message) .def("Message", (void(Lua_EntityList::*)(uint32, uint32, const char*))&Lua_EntityList::Message)
.def("MessageClose", (void(Lua_EntityList::*)(Lua_Mob, bool, float, uint32, const char*))&Lua_EntityList::MessageClose) .def("MessageClose", (void(Lua_EntityList::*)(Lua_Mob, bool, float, uint32, const char*))&Lua_EntityList::MessageClose)
.def("MessageGroup", (void(Lua_EntityList::*)(Lua_Mob, bool, uint32, const char*))&Lua_EntityList::MessageGroup) .def("MessageGroup", (void(Lua_EntityList::*)(Lua_Mob, bool, uint32, const char*))&Lua_EntityList::MessageGroup)
+17
View File
@@ -8,6 +8,7 @@ class EntityList;
class Lua_Mob; class Lua_Mob;
class Lua_Client; class Lua_Client;
class Lua_Bot; class Lua_Bot;
class Lua_Merc;
class Lua_NPC; class Lua_NPC;
class Lua_Door; class Lua_Door;
class Lua_Corpse; class Lua_Corpse;
@@ -142,6 +143,22 @@ public:
Lua_Mob_List GetCloseMobList(Lua_Mob mob); Lua_Mob_List GetCloseMobList(Lua_Mob mob);
Lua_Mob_List GetCloseMobList(Lua_Mob mob, float distance); Lua_Mob_List GetCloseMobList(Lua_Mob mob, float distance);
Lua_Mob_List GetCloseMobList(Lua_Mob mob, float distance, bool ignore_self); Lua_Mob_List GetCloseMobList(Lua_Mob mob, float distance, bool ignore_self);
void AreaAttack(Lua_Mob attacker, float distance);
void AreaAttack(Lua_Mob attacker, float distance, int16 slot_id);
void AreaAttack(Lua_Mob attacker, float distance, int16 slot_id, int count);
void AreaAttack(Lua_Mob attacker, float distance, int16 slot_id, int count, bool is_from_spell);
void AreaAttack(Lua_Mob attacker, float distance, int16 slot_id, int count, bool is_from_spell, int attack_rounds);
void AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id);
void AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster);
void AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust);
void AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int max_targets);
void AreaTaunt(Lua_Client caster);
void AreaTaunt(Lua_Client caster, float range);
void AreaTaunt(Lua_Client caster, float range, int bonus_hate);
void MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_id);
void MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster);
Lua_NPC_List GetNPCsByIDs(luabind::adl::object npc_ids);
Lua_NPC_List GetNPCsByExcludedIDs(luabind::adl::object npc_ids);
}; };
#endif #endif
+38 -5
View File
@@ -5604,6 +5604,37 @@ uint32 lua_get_zone_uptime()
return Timer::GetCurrentTime() / 1000; return Timer::GetCurrentTime() / 1000;
} }
int lua_are_tasks_completed(luabind::object task_ids)
{
if (luabind::type(task_ids) != LUA_TTABLE) {
return 0;
}
std::vector<int> v;
int index = 1;
while (luabind::type(task_ids[index]) != LUA_TNIL) {
auto current_id = task_ids[index];
int task_id = 0;
if (luabind::type(current_id) != LUA_TNIL) {
try {
task_id = luabind::object_cast<int>(current_id);
} catch(luabind::cast_failed &) {
}
} else {
break;
}
v.push_back(task_id);
++index;
}
if (v.empty()) {
return 0;
}
return quest_manager.aretaskscompleted(v);
}
#define LuaCreateNPCParse(name, c_type, default_value) do { \ #define LuaCreateNPCParse(name, c_type, default_value) do { \
cur = table[#name]; \ cur = table[#name]; \
if(luabind::type(cur) != LUA_TNIL) { \ if(luabind::type(cur) != LUA_TNIL) { \
@@ -6410,6 +6441,7 @@ luabind::scope lua_register_general() {
luabind::def("get_zone_short_name_by_long_name", &lua_get_zone_short_name_by_long_name), luabind::def("get_zone_short_name_by_long_name", &lua_get_zone_short_name_by_long_name),
luabind::def("send_parcel", &lua_send_parcel), luabind::def("send_parcel", &lua_send_parcel),
luabind::def("get_zone_uptime", &lua_get_zone_uptime), luabind::def("get_zone_uptime", &lua_get_zone_uptime),
luabind::def("are_tasks_completed", &lua_are_tasks_completed),
/* /*
Cross Zone Cross Zone
*/ */
@@ -6875,7 +6907,8 @@ luabind::scope lua_register_events() {
luabind::value("entity_variable_delete", static_cast<int>(EVENT_ENTITY_VARIABLE_DELETE)), luabind::value("entity_variable_delete", static_cast<int>(EVENT_ENTITY_VARIABLE_DELETE)),
luabind::value("entity_variable_set", static_cast<int>(EVENT_ENTITY_VARIABLE_SET)), luabind::value("entity_variable_set", static_cast<int>(EVENT_ENTITY_VARIABLE_SET)),
luabind::value("entity_variable_update", static_cast<int>(EVENT_ENTITY_VARIABLE_UPDATE)), luabind::value("entity_variable_update", static_cast<int>(EVENT_ENTITY_VARIABLE_UPDATE)),
luabind::value("aa_loss", static_cast<int>(EVENT_AA_LOSS)) luabind::value("aa_loss", static_cast<int>(EVENT_AA_LOSS)),
luabind::value("read", static_cast<int>(EVENT_READ_ITEM))
)]; )];
} }
@@ -7256,10 +7289,10 @@ luabind::scope lua_register_filters() {
luabind::value("FocusEffects", FilterFocusEffects), luabind::value("FocusEffects", FilterFocusEffects),
luabind::value("PetSpells", FilterPetSpells), luabind::value("PetSpells", FilterPetSpells),
luabind::value("HealOverTime", FilterHealOverTime), luabind::value("HealOverTime", FilterHealOverTime),
luabind::value("Unknown25", FilterUnknown25), luabind::value("ItemSpeech", FilterItemSpeech),
luabind::value("Unknown26", FilterUnknown26), luabind::value("Strikethrough", FilterStrikethrough),
luabind::value("Unknown27", FilterUnknown27), luabind::value("Stuns", FilterStuns),
luabind::value("Unknown28", FilterUnknown28) luabind::value("BardSongsOnPets", FilterBardSongsOnPets)
)]; )];
} }
+229
View File
@@ -0,0 +1,229 @@
#ifdef LUA_EQEMU
#include "lua.hpp"
#include <luabind/luabind.hpp>
#include "merc.h"
#include "lua_client.h"
#include "lua_merc.h"
#include "lua_group.h"
#include "lua_item.h"
#include "lua_iteminst.h"
#include "lua_mob.h"
uint32 Lua_Merc::GetCostFormula()
{
Lua_Safe_Call_Int();
return self->GetCostFormula();
}
Lua_Group Lua_Merc::GetGroup()
{
Lua_Safe_Call_Class(Lua_Group);
return self->GetGroup();
}
int Lua_Merc::GetHatedCount()
{
Lua_Safe_Call_Int();
return self->GetHatedCount();
}
float Lua_Merc::GetMaxMeleeRangeToTarget(Lua_Mob target)
{
Lua_Safe_Call_Real();
return self->GetMaxMeleeRangeToTarget(target);
}
uint32 Lua_Merc::GetMercenaryCharacterID()
{
Lua_Safe_Call_Int();
return self->GetMercenaryCharacterID();
}
uint32 Lua_Merc::GetMercenaryID()
{
Lua_Safe_Call_Int();
return self->GetMercenaryID();
}
uint32 Lua_Merc::GetMercenaryNameType()
{
Lua_Safe_Call_Int();
return self->GetMercNameType();
}
Lua_Client Lua_Merc::GetMercenaryOwner()
{
Lua_Safe_Call_Class(Lua_Client);
return Lua_Client(self->GetMercenaryOwner());
}
uint32 Lua_Merc::GetMercenarySubtype()
{
Lua_Safe_Call_Int();
return self->GetMercenarySubType();
}
uint32 Lua_Merc::GetMercenaryTemplateID()
{
Lua_Safe_Call_Int();
return self->GetMercenaryTemplateID();
}
uint32 Lua_Merc::GetMercenaryType()
{
Lua_Safe_Call_Int();
return self->GetMercenaryType();
}
Lua_Mob Lua_Merc::GetOwner()
{
Lua_Safe_Call_Class(Lua_Mob);
return Lua_Mob(self->GetOwner());
}
Lua_Mob Lua_Merc::GetOwnerOrSelf()
{
Lua_Safe_Call_Class(Lua_Mob);
return Lua_Mob(self->GetOwnerOrSelf());
}
uint8 Lua_Merc::GetProficiencyID()
{
Lua_Safe_Call_Int();
return self->GetProficiencyID();
}
uint8 Lua_Merc::GetStance()
{
Lua_Safe_Call_Int();
return self->GetStance();
}
uint8 Lua_Merc::GetTierID()
{
Lua_Safe_Call_Int();
return self->GetTierID();
}
bool Lua_Merc::HasOrMayGetAggro()
{
Lua_Safe_Call_Bool();
return self->HasOrMayGetAggro();
}
bool Lua_Merc::IsMercenaryCaster()
{
Lua_Safe_Call_Bool();
return self->IsMercCaster();
}
bool Lua_Merc::IsMercenaryCasterCombatRange(Lua_Mob target)
{
Lua_Safe_Call_Bool();
return self->IsMercCasterCombatRange(target);
}
bool Lua_Merc::IsSitting()
{
Lua_Safe_Call_Bool();
return self->IsSitting();
}
bool Lua_Merc::IsStanding()
{
Lua_Safe_Call_Bool();
return self->IsStanding();
}
void Lua_Merc::ScaleStats(int scale_percentage)
{
Lua_Safe_Call_Void();
self->ScaleStats(scale_percentage);
}
void Lua_Merc::ScaleStats(int scale_percentage, bool set_to_max)
{
Lua_Safe_Call_Void();
self->ScaleStats(scale_percentage, set_to_max);
}
void Lua_Merc::SendPayload(int payload_id, std::string payload_value)
{
Lua_Safe_Call_Void();
self->SendPayload(payload_id, payload_value);
}
void Lua_Merc::SetTarget(Lua_Mob target)
{
Lua_Safe_Call_Void();
self->SetTarget(target);
}
void Lua_Merc::Signal(int signal_id)
{
Lua_Safe_Call_Void();
self->Signal(signal_id);
}
void Lua_Merc::Sit()
{
Lua_Safe_Call_Void();
self->Sit();
}
void Lua_Merc::Stand()
{
Lua_Safe_Call_Void();
self->Stand();
}
bool Lua_Merc::Suspend()
{
Lua_Safe_Call_Bool();
return self->Suspend();
}
bool Lua_Merc::UseDiscipline(uint16 spell_id, uint16 target_id)
{
Lua_Safe_Call_Bool();
return self->UseDiscipline(spell_id, target_id);
}
luabind::scope lua_register_merc() {
return luabind::class_<Lua_Merc, Lua_Mob>("Merc")
.def(luabind::constructor<>())
.def("GetCostFormula", &Lua_Merc::GetCostFormula)
.def("GetGroup", &Lua_Merc::GetGroup)
.def("GetHatedCount", &Lua_Merc::GetHatedCount)
.def("GetMaxMeleeRangeToTarget", &Lua_Merc::GetMaxMeleeRangeToTarget)
.def("GetMercenaryCharacterID", &Lua_Merc::GetMercenaryCharacterID)
.def("GetMercenaryID", &Lua_Merc::GetMercenaryID)
.def("GetMercenaryNameType", &Lua_Merc::GetMercenaryNameType)
.def("GetMercenaryOwner", &Lua_Merc::GetMercenaryOwner)
.def("GetMercenarySubtype", &Lua_Merc::GetMercenarySubtype)
.def("GetMercenaryTemplateID", &Lua_Merc::GetMercenaryTemplateID)
.def("GetMercenaryType", &Lua_Merc::GetMercenaryType)
.def("GetOwner", &Lua_Merc::GetOwner)
.def("GetOwnerOrSelf", &Lua_Merc::GetOwnerOrSelf)
.def("GetProficiencyID", &Lua_Merc::GetProficiencyID)
.def("GetStance", &Lua_Merc::GetStance)
.def("GetTierID", &Lua_Merc::GetTierID)
.def("HasOrMayGetAggro", &Lua_Merc::HasOrMayGetAggro)
.def("IsMercenaryCaster", &Lua_Merc::IsMercenaryCaster)
.def("IsMercenaryCasterCombatRange", &Lua_Merc::IsMercenaryCasterCombatRange)
.def("IsSitting", &Lua_Merc::IsSitting)
.def("IsStanding", &Lua_Merc::IsStanding)
.def("ScaleStats", (void(Lua_Merc::*)(int))&Lua_Merc::ScaleStats)
.def("ScaleStats", (void(Lua_Merc::*)(int,bool))&Lua_Merc::ScaleStats)
.def("SendPayload", &Lua_Merc::SendPayload)
.def("SetTarget", &Lua_Merc::SetTarget)
.def("Signal", &Lua_Merc::Signal)
.def("Sit", &Lua_Merc::Sit)
.def("Stand", &Lua_Merc::Stand)
.def("Suspend", &Lua_Merc::Suspend)
.def("UseDiscipline", &Lua_Merc::UseDiscipline);
}
#endif
+63
View File
@@ -0,0 +1,63 @@
#ifndef EQEMU_LUA_MERC_H
#define EQEMU_LUA_MERC_H
#ifdef LUA_EQEMU
#include "lua_mob.h"
class Merc;
class Lua_Group;
class Lua_Merc;
class Lua_Mob;
namespace luabind {
struct scope;
}
luabind::scope lua_register_merc();
class Lua_Merc : public Lua_Mob
{
typedef Merc NativeType;
public:
Lua_Merc() { SetLuaPtrData(nullptr); }
Lua_Merc(Merc *d) { SetLuaPtrData(reinterpret_cast<Entity*>(d)); }
virtual ~Lua_Merc() { }
operator Merc*() {
return reinterpret_cast<Merc*>(GetLuaPtrData());
}
uint32 GetCostFormula();
Lua_Group GetGroup();
int GetHatedCount();
float GetMaxMeleeRangeToTarget(Lua_Mob target);
uint32 GetMercenaryCharacterID();
uint32 GetMercenaryID();
uint32 GetMercenaryNameType();
Lua_Client GetMercenaryOwner();
uint32 GetMercenarySubtype();
uint32 GetMercenaryTemplateID();
uint32 GetMercenaryType();
Lua_Mob GetOwner();
Lua_Mob GetOwnerOrSelf();
uint8 GetProficiencyID();
uint8 GetStance();
uint8 GetTierID();
bool HasOrMayGetAggro();
bool IsMercenaryCaster();
bool IsMercenaryCasterCombatRange(Lua_Mob target);
bool IsSitting();
bool IsStanding();
void ScaleStats(int scale_percentage);
void ScaleStats(int scale_percentage, bool set_to_max);
void SendPayload(int payload_id, std::string payload_value);
void SetTarget(Lua_Mob target);
void Signal(int signal_id);
void Sit();
void Stand();
bool Suspend();
bool UseDiscipline(uint16 spell_id, uint16 target_id);
};
#endif // LUA_EQEMU
#endif // EQEMU_LUA_MERC_H
+112
View File
@@ -3380,6 +3380,102 @@ int Lua_Mob::GetExtraHaste()
return self->GetExtraHaste(); return self->GetExtraHaste();
} }
void Lua_Mob::AreaAttack(float distance)
{
Lua_Safe_Call_Void();
entity_list.AEAttack(self, distance);
}
void Lua_Mob::AreaAttack(float distance, int16 slot_id)
{
Lua_Safe_Call_Void();
entity_list.AEAttack(self, distance, slot_id);
}
void Lua_Mob::AreaAttack(float distance, int16 slot_id, int count)
{
Lua_Safe_Call_Void();
entity_list.AEAttack(self, distance, slot_id, count);
}
void Lua_Mob::AreaAttack(float distance, int16 slot_id, int count, bool is_from_spell)
{
Lua_Safe_Call_Void();
entity_list.AEAttack(self, distance, slot_id, count, is_from_spell);
}
void Lua_Mob::AreaAttack(float distance, int16 slot_id, int count, bool is_from_spell, int attack_rounds)
{
Lua_Safe_Call_Void();
entity_list.AEAttack(self, distance, slot_id, count, is_from_spell, attack_rounds);
}
void Lua_Mob::AreaSpell(Lua_Mob center, uint16 spell_id)
{
Lua_Safe_Call_Void();
entity_list.AESpell(self, center, spell_id);
}
void Lua_Mob::AreaSpell(Lua_Mob center, uint16 spell_id, bool affect_caster)
{
Lua_Safe_Call_Void();
entity_list.AESpell(self, center, spell_id, affect_caster);
}
void Lua_Mob::AreaSpell(Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust)
{
Lua_Safe_Call_Void();
entity_list.AESpell(self, center, spell_id, affect_caster, resist_adjust);
}
void Lua_Mob::AreaSpell(Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int max_targets)
{
Lua_Safe_Call_Void();
entity_list.AESpell(self, center, spell_id, affect_caster, resist_adjust, &max_targets);
}
void Lua_Mob::MassGroupBuff(Lua_Mob center, uint16 spell_id)
{
Lua_Safe_Call_Void();
entity_list.MassGroupBuff(self, center, spell_id);
}
void Lua_Mob::MassGroupBuff(Lua_Mob center, uint16 spell_id, bool affect_caster)
{
Lua_Safe_Call_Void();
entity_list.MassGroupBuff(self, center, spell_id, affect_caster);
}
void Lua_Mob::BuffFadeBeneficial()
{
Lua_Safe_Call_Void();
self->BuffFadeBeneficial();
}
void Lua_Mob::BuffFadeDetrimental()
{
Lua_Safe_Call_Void();
self->BuffFadeDetrimental();
}
void Lua_Mob::BuffFadeDetrimentalByCaster(Lua_Mob caster)
{
Lua_Safe_Call_Void();
self->BuffFadeDetrimentalByCaster(caster);
}
void Lua_Mob::BuffFadeNonPersistDeath()
{
Lua_Safe_Call_Void();
self->BuffFadeNonPersistDeath();
}
void Lua_Mob::BuffFadeSongs()
{
Lua_Safe_Call_Void();
self->BuffFadeSongs();
}
luabind::scope lua_register_mob() { luabind::scope lua_register_mob() {
return luabind::class_<Lua_Mob, Lua_Entity>("Mob") return luabind::class_<Lua_Mob, Lua_Entity>("Mob")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
@@ -3393,6 +3489,15 @@ luabind::scope lua_register_mob() {
.def("ApplySpellBuff", (void(Lua_Mob::*)(int))&Lua_Mob::ApplySpellBuff) .def("ApplySpellBuff", (void(Lua_Mob::*)(int))&Lua_Mob::ApplySpellBuff)
.def("ApplySpellBuff", (void(Lua_Mob::*)(int,int))&Lua_Mob::ApplySpellBuff) .def("ApplySpellBuff", (void(Lua_Mob::*)(int,int))&Lua_Mob::ApplySpellBuff)
.def("ApplySpellBuff", (void(Lua_Mob::*)(int,int,int))&Lua_Mob::ApplySpellBuff) .def("ApplySpellBuff", (void(Lua_Mob::*)(int,int,int))&Lua_Mob::ApplySpellBuff)
.def("AreaAttack", (void(Lua_Mob::*)(float))&Lua_Mob::AreaAttack)
.def("AreaAttack", (void(Lua_Mob::*)(float, int16))&Lua_Mob::AreaAttack)
.def("AreaAttack", (void(Lua_Mob::*)(float, int16, int))&Lua_Mob::AreaAttack)
.def("AreaAttack", (void(Lua_Mob::*)(float, int16, int, bool))&Lua_Mob::AreaAttack)
.def("AreaAttack", (void(Lua_Mob::*)(float, int16, int, bool, int))&Lua_Mob::AreaAttack)
.def("AreaSpell", (void(Lua_Mob::*)(Lua_Mob, uint16))&Lua_Mob::AreaSpell)
.def("AreaSpell", (void(Lua_Mob::*)(Lua_Mob, uint16, bool))&Lua_Mob::AreaSpell)
.def("AreaSpell", (void(Lua_Mob::*)(Lua_Mob, uint16, bool, int16))&Lua_Mob::AreaSpell)
.def("AreaSpell", (void(Lua_Mob::*)(Lua_Mob, uint16, bool, int16, int))&Lua_Mob::AreaSpell)
.def("Attack", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::Attack) .def("Attack", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::Attack)
.def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int))&Lua_Mob::Attack) .def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int))&Lua_Mob::Attack)
.def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int,bool))&Lua_Mob::Attack) .def("Attack", (bool(Lua_Mob::*)(Lua_Mob,int,bool))&Lua_Mob::Attack)
@@ -3408,11 +3513,16 @@ luabind::scope lua_register_mob() {
.def("BuffCount", (uint32(Lua_Mob::*)(bool))&Lua_Mob::BuffCount) .def("BuffCount", (uint32(Lua_Mob::*)(bool))&Lua_Mob::BuffCount)
.def("BuffCount", (uint32(Lua_Mob::*)(bool,bool))&Lua_Mob::BuffCount) .def("BuffCount", (uint32(Lua_Mob::*)(bool,bool))&Lua_Mob::BuffCount)
.def("BuffFadeAll", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeAll) .def("BuffFadeAll", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeAll)
.def("BuffFadeBeneficial", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeBeneficial)
.def("BuffFadeByEffect", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeByEffect) .def("BuffFadeByEffect", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeByEffect)
.def("BuffFadeByEffect", (void(Lua_Mob::*)(int,int))&Lua_Mob::BuffFadeByEffect) .def("BuffFadeByEffect", (void(Lua_Mob::*)(int,int))&Lua_Mob::BuffFadeByEffect)
.def("BuffFadeBySlot", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySlot) .def("BuffFadeBySlot", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySlot)
.def("BuffFadeBySlot", (void(Lua_Mob::*)(int,bool))&Lua_Mob::BuffFadeBySlot) .def("BuffFadeBySlot", (void(Lua_Mob::*)(int,bool))&Lua_Mob::BuffFadeBySlot)
.def("BuffFadeBySpellID", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySpellID) .def("BuffFadeBySpellID", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySpellID)
.def("BuffFadeDetrimental", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeDetrimental)
.def("BuffFadeDetrimentalByCaster", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::BuffFadeDetrimentalByCaster)
.def("BuffFadeNonPersistDeath", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeNonPersistDeath)
.def("BuffFadeSongs", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeSongs)
.def("CalculateDistance", (float(Lua_Mob::*)(double,double,double))&Lua_Mob::CalculateDistance) .def("CalculateDistance", (float(Lua_Mob::*)(double,double,double))&Lua_Mob::CalculateDistance)
.def("CalculateDistance", (float(Lua_Mob::*)(Lua_Mob))&Lua_Mob::CalculateDistance) .def("CalculateDistance", (float(Lua_Mob::*)(Lua_Mob))&Lua_Mob::CalculateDistance)
.def("CalculateHeadingToTarget", (double(Lua_Mob::*)(double,double))&Lua_Mob::CalculateHeadingToTarget) .def("CalculateHeadingToTarget", (double(Lua_Mob::*)(double,double))&Lua_Mob::CalculateHeadingToTarget)
@@ -3808,6 +3918,8 @@ luabind::scope lua_register_mob() {
.def("IsWarriorClass", &Lua_Mob::IsWarriorClass) .def("IsWarriorClass", &Lua_Mob::IsWarriorClass)
.def("IsWisdomCasterClass", &Lua_Mob::IsWisdomCasterClass) .def("IsWisdomCasterClass", &Lua_Mob::IsWisdomCasterClass)
.def("Kill", (void(Lua_Mob::*)(void))&Lua_Mob::Kill) .def("Kill", (void(Lua_Mob::*)(void))&Lua_Mob::Kill)
.def("MassGroupBuff", (void(Lua_Mob::*)(Lua_Mob, uint16))&Lua_Mob::MassGroupBuff)
.def("MassGroupBuff", (void(Lua_Mob::*)(Lua_Mob, uint16, bool))&Lua_Mob::MassGroupBuff)
.def("Mesmerize", (void(Lua_Mob::*)(void))&Lua_Mob::Mesmerize) .def("Mesmerize", (void(Lua_Mob::*)(void))&Lua_Mob::Mesmerize)
.def("Message", &Lua_Mob::Message) .def("Message", &Lua_Mob::Message)
.def("MessageString", &Lua_Mob::MessageString) .def("MessageString", &Lua_Mob::MessageString)
+17
View File
@@ -10,6 +10,7 @@ class Lua_Item;
class Lua_ItemInst; class Lua_ItemInst;
class Lua_StatBonuses; class Lua_StatBonuses;
class Lua_Bot; class Lua_Bot;
class Lua_Merc;
class Lua_NPC; class Lua_NPC;
class Lua_Client; class Lua_Client;
struct Lua_Mob_List; struct Lua_Mob_List;
@@ -594,6 +595,22 @@ public:
int GetExtraHaste(); int GetExtraHaste();
void SetExtraHaste(int haste); void SetExtraHaste(int haste);
void SetExtraHaste(int haste, bool need_to_save); void SetExtraHaste(int haste, bool need_to_save);
void AreaAttack(float distance);
void AreaAttack(float distance, int16 slot_id);
void AreaAttack(float distance, int16 slot_id, int count);
void AreaAttack(float distance, int16 slot_id, int count, bool is_from_spell);
void AreaAttack(float distance, int16 slot_id, int count, bool is_from_spell, int attack_rounds);
void AreaSpell(Lua_Mob center, uint16 spell_id);
void AreaSpell(Lua_Mob center, uint16 spell_id, bool affect_caster);
void AreaSpell(Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust);
void AreaSpell(Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int max_targets);
void MassGroupBuff(Lua_Mob center, uint16 spell_id);
void MassGroupBuff(Lua_Mob center, uint16 spell_id, bool affect_caster);
void BuffFadeBeneficial();
void BuffFadeDetrimental();
void BuffFadeDetrimentalByCaster(Lua_Mob caster);
void BuffFadeNonPersistDeath();
void BuffFadeSongs();
}; };
#endif #endif
+192 -1
View File
@@ -32,6 +32,7 @@
#include "lua_inventory.h" #include "lua_inventory.h"
#include "lua_item.h" #include "lua_item.h"
#include "lua_iteminst.h" #include "lua_iteminst.h"
#include "lua_merc.h"
#include "lua_mob.h" #include "lua_mob.h"
#include "lua_npc.h" #include "lua_npc.h"
#include "lua_object.h" #include "lua_object.h"
@@ -184,7 +185,8 @@ const char *LuaEvents[_LargestEventID] = {
"event_entity_variable_set", "event_entity_variable_set",
"event_entity_variable_update", "event_entity_variable_update",
"event_aa_loss", "event_aa_loss",
"event_spell_blocked" "event_spell_blocked",
"event_read_item"
}; };
extern Zone *zone; extern Zone *zone;
@@ -348,6 +350,7 @@ LuaParser::LuaParser() {
PlayerArgumentDispatch[EVENT_ENTITY_VARIABLE_UPDATE] = handle_player_entity_variable; PlayerArgumentDispatch[EVENT_ENTITY_VARIABLE_UPDATE] = handle_player_entity_variable;
PlayerArgumentDispatch[EVENT_AA_LOSS] = handle_player_aa_loss; PlayerArgumentDispatch[EVENT_AA_LOSS] = handle_player_aa_loss;
PlayerArgumentDispatch[EVENT_SPELL_BLOCKED] = handle_player_spell_blocked; PlayerArgumentDispatch[EVENT_SPELL_BLOCKED] = handle_player_spell_blocked;
PlayerArgumentDispatch[EVENT_READ_ITEM] = handle_player_read_item;
ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click; ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click;
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click; ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;
@@ -1277,6 +1280,7 @@ void LuaParser::MapFunctions(lua_State *L) {
lua_register_npc(), lua_register_npc(),
lua_register_client(), lua_register_client(),
lua_register_bot(), lua_register_bot(),
lua_register_merc(),
lua_register_inventory(), lua_register_inventory(),
lua_register_inventory_where(), lua_register_inventory_where(),
lua_register_iteminst(), lua_register_iteminst(),
@@ -1833,3 +1837,190 @@ void LuaParser::LoadBotScript(std::string filename) {
void LuaParser::LoadGlobalBotScript(std::string filename) { void LuaParser::LoadGlobalBotScript(std::string filename) {
LoadScript(filename, "global_bot"); LoadScript(filename, "global_bot");
} }
int LuaParser::EventMerc(
QuestEventID evt,
Merc *merc,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
if (!merc) {
return 0;
}
if (!MercHasQuestSub(evt)) {
return 0;
}
return _EventMerc("merc", evt, merc, init, data, extra_data, extra_pointers);
}
int LuaParser::EventGlobalMerc(
QuestEventID evt,
Merc *merc,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
if (!merc) {
return 0;
}
if (!GlobalMercHasQuestSub(evt)) {
return 0;
}
return _EventMerc("global_merc", evt, merc, init, data, extra_data, extra_pointers);
}
int LuaParser::_EventMerc(
std::string package_name,
QuestEventID evt,
Merc *merc,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers,
luabind::adl::object *l_func
) {
const char *sub_name = LuaEvents[evt];
int start = lua_gettop(L);
try {
int npop = 2;
PushErrorHandler(L);
if(l_func != nullptr) {
l_func->push(L);
} else {
lua_getfield(L, LUA_REGISTRYINDEX, package_name.c_str());
lua_getfield(L, -1, sub_name);
npop = 3;
}
lua_createtable(L, 0, 0);
//push self
Lua_Merc l_merc(merc);
luabind::adl::object l_merc_o = luabind::adl::object(L, l_merc);
l_merc_o.push(L);
lua_setfield(L, -2, "self");
auto arg_function = NPCArgumentDispatch[evt];
arg_function(this, L, merc, init, data, extra_data, extra_pointers);
auto* c = (init && init->IsClient()) ? init->CastToClient() : nullptr;
quest_manager.StartQuest(merc, c);
if(lua_pcall(L, 1, 1, start + 1)) {
std::string error = lua_tostring(L, -1);
AddError(error);
quest_manager.EndQuest();
lua_pop(L, npop);
return 0;
}
quest_manager.EndQuest();
if(lua_isnumber(L, -1)) {
int ret = static_cast<int>(lua_tointeger(L, -1));
lua_pop(L, npop);
return ret;
}
lua_pop(L, npop);
} catch(std::exception &ex) {
AddError(
fmt::format(
"Lua Exception | [{}] for Merc [{}] in [{}]: {}",
sub_name,
merc->GetID(),
package_name,
ex.what()
)
);
//Restore our stack to the best of our ability
int end = lua_gettop(L);
int n = end - start;
if(n > 0) {
lua_pop(L, n);
}
}
return 0;
}
int LuaParser::DispatchEventMerc(
QuestEventID evt,
Merc *merc,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
std::string package_name = "merc";
auto iter = lua_encounter_events_registered.find(package_name);
if (iter == lua_encounter_events_registered.end()) {
return 0;
}
int ret = 0;
auto riter = iter->second.begin();
while (riter != iter->second.end()) {
if (riter->event_id == evt) {
package_name = fmt::format("encounter_{}", riter->encounter_name);
int i = _EventMerc(package_name, evt, merc, init, data, extra_data, extra_pointers, &riter->lua_reference);
if (i != 0) {
ret = i;
}
}
++riter;
}
return ret;
}
bool LuaParser::MercHasQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "merc");
}
bool LuaParser::GlobalMercHasQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "global_merc");
}
void LuaParser::LoadMercScript(std::string filename) {
LoadScript(filename, "merc");
}
void LuaParser::LoadGlobalMercScript(std::string filename) {
LoadScript(filename, "global_merc");
}
+38
View File
@@ -109,6 +109,22 @@ public:
uint32 extra_data, uint32 extra_data,
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
); );
virtual int EventMerc(
QuestEventID evt,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual int EventGlobalMerc(
QuestEventID evt,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt); virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt);
virtual bool HasGlobalQuestSub(QuestEventID evt); virtual bool HasGlobalQuestSub(QuestEventID evt);
@@ -120,6 +136,8 @@ public:
virtual bool HasEncounterSub(const std::string& package_name, QuestEventID evt); virtual bool HasEncounterSub(const std::string& package_name, QuestEventID evt);
virtual bool BotHasQuestSub(QuestEventID evt); virtual bool BotHasQuestSub(QuestEventID evt);
virtual bool GlobalBotHasQuestSub(QuestEventID evt); virtual bool GlobalBotHasQuestSub(QuestEventID evt);
virtual bool MercHasQuestSub(QuestEventID evt);
virtual bool GlobalMercHasQuestSub(QuestEventID evt);
virtual void LoadNPCScript(std::string filename, int npc_id); virtual void LoadNPCScript(std::string filename, int npc_id);
virtual void LoadGlobalNPCScript(std::string filename); virtual void LoadGlobalNPCScript(std::string filename);
@@ -130,6 +148,8 @@ public:
virtual void LoadEncounterScript(std::string filename, std::string encounter_name); virtual void LoadEncounterScript(std::string filename, std::string encounter_name);
virtual void LoadBotScript(std::string filename); virtual void LoadBotScript(std::string filename);
virtual void LoadGlobalBotScript(std::string filename); virtual void LoadGlobalBotScript(std::string filename);
virtual void LoadMercScript(std::string filename);
virtual void LoadGlobalMercScript(std::string filename);
virtual void AddVar(std::string name, std::string val); virtual void AddVar(std::string name, std::string val);
virtual std::string GetVar(std::string name); virtual std::string GetVar(std::string name);
@@ -179,6 +199,14 @@ public:
uint32 extra_data, uint32 extra_data,
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
); );
virtual int DispatchEventMerc(
QuestEventID evt,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers
);
static LuaParser* Instance() { static LuaParser* Instance() {
static LuaParser inst; static LuaParser inst;
@@ -269,6 +297,16 @@ private:
std::vector<std::any> *extra_pointers, std::vector<std::any> *extra_pointers,
luabind::adl::object *l_func = nullptr luabind::adl::object *l_func = nullptr
); );
int _EventMerc(
std::string package_name,
QuestEventID evt,
Merc* merc,
Mob* init,
std::string data,
uint32 extra_data,
std::vector<std::any>* extra_pointers,
luabind::adl::object* l_func = nullptr
);
void LoadScript(std::string filename, std::string package_name); void LoadScript(std::string filename, std::string package_name);
void MapFunctions(lua_State *L); void MapFunctions(lua_State *L);
+55
View File
@@ -734,6 +734,18 @@ void handle_player_death(
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[4])); lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[4]));
lua_setfield(L, -2, "killed_entity_id"); lua_setfield(L, -2, "killed_entity_id");
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[5]));
lua_setfield(L, -2, "combat_start_time");
lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[6]));
lua_setfield(L, -2, "combat_end_time");
lua_pushinteger(L, Strings::ToBigInt(sep.arg[7]));
lua_setfield(L, -2, "damage_received");
lua_pushinteger(L, Strings::ToBigInt(sep.arg[8]));
lua_setfield(L, -2, "healing_received");
} }
void handle_player_timer( void handle_player_timer(
@@ -1745,6 +1757,49 @@ void handle_player_spell_blocked(
lua_setfield(L, -2, "cast_spell"); lua_setfield(L, -2, "cast_spell");
} }
void handle_player_read_item(
QuestInterface *parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
)
{
lua_pushstring(L, data.c_str());
lua_setfield(L, -2, "text_file");
lua_pushinteger(L, extra_data);
lua_setfield(L, -2, "item_id");
if (extra_pointers) {
if (extra_pointers->size() == 7) {
lua_pushstring(L, std::any_cast<std::string>(extra_pointers->at(0)).c_str());
lua_setfield(L, -2, "book_text");
lua_pushboolean(L, std::any_cast<int8>(extra_pointers->at(1)));
lua_setfield(L, -2, "can_cast");
lua_pushboolean(L, std::any_cast<int8>(extra_pointers->at(2)));
lua_setfield(L, -2, "can_scribe");
lua_pushinteger(L, std::any_cast<int16>(extra_pointers->at(3)));
lua_setfield(L, -2, "slot_id");
lua_pushinteger(L, std::any_cast<int>(extra_pointers->at(4)));
lua_setfield(L, -2, "target_id");
lua_pushinteger(L, std::any_cast<uint8>(extra_pointers->at(5)));
lua_setfield(L, -2, "type");
Lua_ItemInst l_item(std::any_cast<EQ::ItemInstance*>(extra_pointers->at(6)));
luabind::adl::object l_item_o = luabind::adl::object(L, l_item);
l_item_o.push(L);
lua_setfield(L, -2, "item");
}
}
}
// Item // Item
void handle_item_click( void handle_item_click(
QuestInterface *parse, QuestInterface *parse,
+10
View File
@@ -8,6 +8,7 @@ typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, EQ::Ite
typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, Mob*, Client*, uint32, std::string, uint32, std::vector<std::any>*); typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, Mob*, Client*, uint32, std::string, uint32, std::vector<std::any>*);
typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector<std::any>*); typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector<std::any>*);
typedef void(*BotArgumentHandler)(QuestInterface*, lua_State*, Bot*, Mob*, std::string, uint32, std::vector<std::any>*); typedef void(*BotArgumentHandler)(QuestInterface*, lua_State*, Bot*, Mob*, std::string, uint32, std::vector<std::any>*);
typedef void(*MercArgumentHandler)(QuestInterface*, lua_State*, Merc*, Mob*, std::string, uint32, std::vector<std::any>*);
// NPC // NPC
void handle_npc_event_say( void handle_npc_event_say(
@@ -855,6 +856,15 @@ void handle_player_spell_blocked(
std::vector<std::any> *extra_pointers std::vector<std::any> *extra_pointers
); );
void handle_player_read_item(
QuestInterface *parse,
lua_State* L,
Client* client,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
);
// Item // Item
void handle_item_click( void handle_item_click(
QuestInterface *parse, QuestInterface *parse,
+17 -10
View File
@@ -5,6 +5,7 @@
#include "entity.h" #include "entity.h"
#include "groups.h" #include "groups.h"
#include "mob.h" #include "mob.h"
#include "quest_parser_collection.h"
#include "zone.h" #include "zone.h"
#include "string_ids.h" #include "string_ids.h"
@@ -4078,12 +4079,6 @@ bool Merc::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillT
Save(); Save();
//no corpse, no exp if we're a merc.
//We'll suspend instead, since that's what live does.
//Not actually sure live supports 'depopping' merc corpses.
//if(entity_list.GetCorpseByID(GetID()))
// entity_list.GetCorpseByID(GetID())->Depop();
// If client is in zone, suspend merc, else depop it. // If client is in zone, suspend merc, else depop it.
if (!Suspend()) { if (!Suspend()) {
Depop(); Depop();
@@ -4671,7 +4666,6 @@ bool Merc::Spawn(Client *owner) {
//UpdateMercAppearance(); //UpdateMercAppearance();
return true; return true;
} }
@@ -5189,9 +5183,6 @@ void Client::SpawnMerc(Merc* merc, bool setMaxStats) {
merc->SetStance(GetMercInfo().Stance); merc->SetStance(GetMercInfo().Stance);
Log(Logs::General, Logs::Mercenaries, "SpawnMerc Success for %s.", GetName()); Log(Logs::General, Logs::Mercenaries, "SpawnMerc Success for %s.", GetName());
return;
} }
bool Merc::Suspend() { bool Merc::Suspend() {
@@ -5914,3 +5905,19 @@ uint32 Merc::CalcUpkeepCost(uint32 templateID , uint8 level, uint8 currency_type
return cost; return cost;
} }
void Merc::Signal(int signal_id)
{
if (parse->MercHasQuestSub(EVENT_SIGNAL)) {
parse->EventMerc(EVENT_SIGNAL, this, nullptr, std::to_string(signal_id), 0);
}
}
void Merc::SendPayload(int payload_id, std::string payload_value)
{
if (parse->MercHasQuestSub(EVENT_PAYLOAD)) {
const auto& export_string = fmt::format("{} {}", payload_id, payload_value);
parse->EventMerc(EVENT_PAYLOAD, this, nullptr, export_string, 0);
}
}
+3
View File
@@ -146,6 +146,9 @@ public:
bool IsMedding() { return _medding; }; bool IsMedding() { return _medding; };
bool IsSuspended() { return _suspended; }; bool IsSuspended() { return _suspended; };
void Signal(int signal_id);
void SendPayload(int payload_id, std::string payload_value);
static uint32 CalcPurchaseCost( uint32 templateID , uint8 level, uint8 currency_type = 0); static uint32 CalcPurchaseCost( uint32 templateID , uint8 level, uint8 currency_type = 0);
static uint32 CalcUpkeepCost( uint32 templateID , uint8 level, uint8 currency_type = 0); static uint32 CalcUpkeepCost( uint32 templateID , uint8 level, uint8 currency_type = 0);
+100 -86
View File
@@ -128,8 +128,9 @@ Mob::Mob(
attack_anim_timer(500), attack_anim_timer(500),
position_update_melee_push_timer(500), position_update_melee_push_timer(500),
hate_list_cleanup_timer(6000), hate_list_cleanup_timer(6000),
mob_close_scan_timer(6000), m_scan_close_mobs_timer(6000),
mob_check_moving_timer(1000) m_mob_check_moving_timer(1000),
bot_attack_flag_timer(10000)
{ {
mMovementManager = &MobMovementManager::Get(); mMovementManager = &MobMovementManager::Get();
mMovementManager->AddMob(this); mMovementManager->AddMob(this);
@@ -400,6 +401,10 @@ Mob::Mob(
pet_owner_npc = false; pet_owner_npc = false;
pet_targetlock_id = 0; pet_targetlock_id = 0;
//bot attack flag
bot_attack_flags.clear();
bot_attack_flag_timer.Disable();
attacked_count = 0; attacked_count = 0;
mezzed = false; mezzed = false;
stunned = false; stunned = false;
@@ -512,7 +517,7 @@ Mob::Mob(
m_manual_follow = false; m_manual_follow = false;
mob_close_scan_timer.Trigger(); m_scan_close_mobs_timer.Trigger();
SetCanOpenDoors(true); SetCanOpenDoors(true);
@@ -565,7 +570,7 @@ Mob::~Mob()
entity_list.RemoveMobFromCloseLists(this); entity_list.RemoveMobFromCloseLists(this);
entity_list.RemoveAuraFromMobs(this); entity_list.RemoveAuraFromMobs(this);
close_mobs.clear(); m_close_mobs.clear();
LeaveHealRotationTargetPool(); LeaveHealRotationTargetPool();
} }
@@ -4063,7 +4068,8 @@ uint8 Mob::GetDefaultGender(uint16 in_race, uint8 in_gender) {
in_race == Race::Human2 || in_race == Race::Human2 ||
in_race == Race::ElvenGhost || in_race == Race::ElvenGhost ||
in_race == Race::HumanGhost || in_race == Race::HumanGhost ||
in_race == Race::Coldain2 in_race == Race::Coldain2 ||
in_race == Race::Akheva
) { ) {
if (in_gender >= Gender::Neuter) { // Male default for PC Races if (in_gender >= Gender::Neuter) { // Male default for PC Races
return Gender::Male; return Gender::Male;
@@ -5012,7 +5018,7 @@ void Mob::Say(const char *format, ...)
int16 distance = 200; int16 distance = 200;
if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) { if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) {
for (auto &e : entity_list.GetCloseMobList(talker, (distance * distance))) { for (auto &e : talker->GetCloseMobList(distance)) {
Mob *mob = e.second; Mob *mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -5508,37 +5514,13 @@ void Mob::SetTarget(Mob *mob)
target = mob; target = mob;
entity_list.UpdateHoTT(this); entity_list.UpdateHoTT(this);
const auto has_target_change_event = (
parse->HasQuestSub(GetNPCTypeID(), EVENT_TARGET_CHANGE) ||
parse->PlayerHasQuestSub(EVENT_TARGET_CHANGE) ||
parse->BotHasQuestSub(EVENT_TARGET_CHANGE)
);
if (IsClient() && CastToClient()->admin > AccountStatus::GMMgmt) { if (IsClient() && CastToClient()->admin > AccountStatus::GMMgmt) {
DisplayInfo(mob); DisplayInfo(mob);
} }
if (has_target_change_event) { std::vector<std::any> args = { mob };
std::vector<std::any> args;
args.emplace_back(mob); parse->EventMob(EVENT_TARGET_CHANGE, this, mob, [&]() { return ""; }, 0, &args);
if (IsNPC()) {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_TARGET_CHANGE)) {
parse->EventNPC(EVENT_TARGET_CHANGE, CastToNPC(), mob, "", 0, &args);
}
} else if (IsClient()) {
if (parse->PlayerHasQuestSub(EVENT_TARGET_CHANGE)) {
parse->EventPlayer(EVENT_TARGET_CHANGE, CastToClient(), "", 0, &args);
}
CastToClient()->SetBotPrecombat(false); // Any change in target will nullify this flag (target == mob checked above)
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_TARGET_CHANGE)) {
parse->EventBot(EVENT_TARGET_CHANGE, CastToBot(), mob, "", 0, &args);
}
}
}
if (IsPet() && GetOwner() && GetOwner()->IsClient()) { if (IsPet() && GetOwner() && GetOwner()->IsClient()) {
GetOwner()->CastToClient()->UpdateXTargetType(MyPetTarget, mob); GetOwner()->CastToClient()->UpdateXTargetType(MyPetTarget, mob);
@@ -5711,22 +5693,10 @@ bool Mob::ClearEntityVariables()
return false; return false;
} }
if (
(IsBot() && parse->BotHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) ||
(IsClient() && parse->PlayerHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) ||
(IsNPC() && parse->HasQuestSub(GetNPCTypeID(), EVENT_ENTITY_VARIABLE_DELETE))
) {
for (const auto& e : m_EntityVariables) { for (const auto& e : m_EntityVariables) {
std::vector<std::any> args = { e.first, e.second }; std::vector<std::any> args = { e.first, e.second };
if (IsBot()) { parse->EventMob(EVENT_ENTITY_VARIABLE_DELETE, this, nullptr, [&]() { return ""; }, 0, &args);
parse->EventBot(EVENT_ENTITY_VARIABLE_DELETE, CastToBot(), nullptr, "", 0, &args);
} else if (IsClient()) {
parse->EventPlayer(EVENT_ENTITY_VARIABLE_DELETE, CastToClient(), "", 0, &args);
} else if (IsNPC()) {
parse->EventNPC(EVENT_ENTITY_VARIABLE_DELETE, CastToNPC(), nullptr, "", 0, &args);
}
}
} }
m_EntityVariables.clear(); m_EntityVariables.clear();
@@ -5744,23 +5714,10 @@ bool Mob::DeleteEntityVariable(std::string variable_name)
return false; return false;
} }
m_EntityVariables.erase(v);
if (
(IsBot() && parse->BotHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) ||
(IsClient() && parse->PlayerHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) ||
(IsNPC() && parse->HasQuestSub(GetNPCTypeID(), EVENT_ENTITY_VARIABLE_DELETE))
) {
std::vector<std::any> args = { v->first, v->second }; std::vector<std::any> args = { v->first, v->second };
parse->EventMob(EVENT_ENTITY_VARIABLE_DELETE, this, nullptr, [&]() { return ""; }, 0, &args);
if (IsBot()) { m_EntityVariables.erase(v);
parse->EventBot(EVENT_ENTITY_VARIABLE_DELETE, CastToBot(), nullptr, "", 0, &args);
} else if (IsClient()) {
parse->EventPlayer(EVENT_ENTITY_VARIABLE_DELETE, CastToClient(), "", 0, &args);
} else if (IsNPC()) {
parse->EventNPC(EVENT_ENTITY_VARIABLE_DELETE, CastToNPC(), nullptr, "", 0, &args);
}
}
return true; return true;
} }
@@ -5808,32 +5765,16 @@ void Mob::SetEntityVariable(std::string variable_name, std::string variable_valu
return; return;
} }
const QuestEventID event_id = (
!EntityVariableExists(variable_name) ?
EVENT_ENTITY_VARIABLE_SET :
EVENT_ENTITY_VARIABLE_UPDATE
);
if (
(IsBot() && parse->BotHasQuestSub(event_id)) ||
(IsClient() && parse->PlayerHasQuestSub(event_id)) ||
(IsNPC() && parse->HasQuestSub(GetNPCTypeID(), event_id))
) {
std::vector<std::any> args; std::vector<std::any> args;
if (event_id != EVENT_ENTITY_VARIABLE_UPDATE) { if (!EntityVariableExists(variable_name)) {
args = { variable_name, variable_value }; args = { variable_name, variable_value };
parse->EventMob(EVENT_ENTITY_VARIABLE_SET, this, nullptr, [&]() { return ""; }, 0, &args);
} else { } else {
args = { variable_name, GetEntityVariable(variable_name), variable_value }; args = { variable_name, GetEntityVariable(variable_name), variable_value };
}
if (IsBot()) { parse->EventMob(EVENT_ENTITY_VARIABLE_UPDATE, this, nullptr, [&]() { return ""; }, 0, &args);
parse->EventBot(event_id, CastToBot(), nullptr, "", 0, &args);
} else if (IsClient()) {
parse->EventPlayer(event_id, CastToClient(), "", 0, &args);
} else if (IsNPC()) {
parse->EventNPC(event_id, CastToNPC(), nullptr, "", 0, &args);
}
} }
m_EntityVariables[variable_name] = variable_value; m_EntityVariables[variable_name] = variable_value;
@@ -6171,9 +6112,7 @@ int32 Mob::GetPositionalDmgTakenAmt(Mob *attacker)
void Mob::SetBottomRampageList() void Mob::SetBottomRampageList()
{ {
auto &mob_list = entity_list.GetCloseMobList(this); for (auto &e : GetCloseMobList()) {
for (auto &e : mob_list) {
auto mob = e.second; auto mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -6198,9 +6137,7 @@ void Mob::SetBottomRampageList()
void Mob::SetTopRampageList() void Mob::SetTopRampageList()
{ {
auto &mob_list = entity_list.GetCloseMobList(this); for (auto &e : GetCloseMobList()) {
for (auto &e : mob_list) {
auto mob = e.second; auto mob = e.second;
if (!mob) { if (!mob) {
continue; continue;
@@ -8611,3 +8548,80 @@ void Mob::SetExtraHaste(int haste, bool need_to_save)
} }
} }
} }
bool Mob::IsCloseToBanker()
{
for (auto &e: GetCloseMobList()) {
auto mob = e.second;
if (mob && mob->IsNPC() && mob->GetClass() == Class::Banker) {
return true;
}
}
return false;
}
bool Mob::HasBotAttackFlag(Mob* tar) {
if (!tar) {
return false;
}
std::vector<uint32> l = tar->GetBotAttackFlags();
for (uint32 e : l) {
if (IsBot() && e == CastToBot()->GetBotOwnerCharacterID()) {
return true;
}
if (IsClient() && e == CastToClient()->CharacterID()) {
return true;
}
}
return false;
}
const uint16 scan_close_mobs_timer_moving = 6000; // 6 seconds
const uint16 scan_close_mobs_timer_idle = 60000; // 60 seconds
void Mob::CheckScanCloseMobsMovingTimer()
{
LogAIScanCloseDetail(
"Mob [{}] {}moving, scan timer [{}]",
GetCleanName(),
IsMoving() ? "" : "NOT ",
m_scan_close_mobs_timer.GetRemainingTime()
);
// If the moving timer triggers, lets see if we are moving or idle to restart the appropriate
// dynamic timer
if (m_mob_check_moving_timer.Check()) {
// If the mob is still moving, restart the moving timer
if (moving) {
if (m_scan_close_mobs_timer.GetRemainingTime() > scan_close_mobs_timer_moving) {
LogAIScanCloseDetail("Mob [{}] Restarting with moving timer", GetCleanName());
m_scan_close_mobs_timer.Disable();
m_scan_close_mobs_timer.Start(scan_close_mobs_timer_moving);
m_scan_close_mobs_timer.Trigger();
}
}
// If the mob is not moving, restart the idle timer
else if (m_scan_close_mobs_timer.GetDuration() == scan_close_mobs_timer_moving) {
LogAIScanCloseDetail("Mob [{}] Restarting with idle timer", GetCleanName());
m_scan_close_mobs_timer.Disable();
m_scan_close_mobs_timer.Start(scan_close_mobs_timer_idle);
}
}
}
void Mob::ScanCloseMobProcess()
{
if (m_scan_close_mobs_timer.Check()) {
entity_list.ScanCloseMobs(this);
}
}
std::unordered_map<uint16, Mob *> &Mob::GetCloseMobList(float distance)
{
return entity_list.GetCloseMobList(this, distance);
}
+21 -3
View File
@@ -201,9 +201,12 @@ public:
void DisplayInfo(Mob *mob); void DisplayInfo(Mob *mob);
std::unordered_map<uint16, Mob *> close_mobs; std::unordered_map<uint16, Mob *> m_close_mobs;
Timer mob_close_scan_timer; Timer m_scan_close_mobs_timer;
Timer mob_check_moving_timer; Timer m_mob_check_moving_timer;
// Bot attack flag
Timer bot_attack_flag_timer;
//Somewhat sorted: needs documenting! //Somewhat sorted: needs documenting!
@@ -444,6 +447,7 @@ public:
void BuffFadeBySlot(int slot, bool iRecalcBonuses = true); void BuffFadeBySlot(int slot, bool iRecalcBonuses = true);
void BuffFadeDetrimentalByCaster(Mob *caster); void BuffFadeDetrimentalByCaster(Mob *caster);
void BuffFadeBySitModifier(); void BuffFadeBySitModifier();
void BuffFadeSongs();
void BuffDetachCaster(Mob *caster); void BuffDetachCaster(Mob *caster);
bool IsAffectedByBuffByGlobalGroup(GlobalGroup group); bool IsAffectedByBuffByGlobalGroup(GlobalGroup group);
void BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration); void BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration);
@@ -1102,6 +1106,11 @@ public:
bool invulnerable; bool invulnerable;
bool qglobal; bool qglobal;
inline std::vector<uint32> GetBotAttackFlags() { return bot_attack_flags; }
inline void SetBotAttackFlag(uint32 value) { bot_attack_flags.push_back(value); }
inline void ClearBotAttackFlags() { bot_attack_flags.clear(); }
bool HasBotAttackFlag(Mob* tar);
virtual void SetAttackTimer(); virtual void SetAttackTimer();
inline void SetInvul(bool invul) { invulnerable=invul; } inline void SetInvul(bool invul) { invulnerable=invul; }
inline bool GetInvul(void) { return invulnerable; } inline bool GetInvul(void) { return invulnerable; }
@@ -1477,6 +1486,12 @@ public:
DataBucketKey GetScopedBucketKeys(); DataBucketKey GetScopedBucketKeys();
bool IsCloseToBanker();
void ScanCloseMobProcess();
std::unordered_map<uint16, Mob *> &GetCloseMobList(float distance = 0.0f);
void CheckScanCloseMobsMovingTimer();
protected: protected:
void CommonDamage(Mob* other, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None); void CommonDamage(Mob* other, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None);
static uint16 GetProcID(uint16 spell_id, uint8 effect_index); static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
@@ -1861,6 +1876,9 @@ protected:
bool pet_owner_npc; // Flags pets as belonging to an NPC bool pet_owner_npc; // Flags pets as belonging to an NPC
uint32 pet_targetlock_id; uint32 pet_targetlock_id;
//bot attack flags
std::vector<uint32> bot_attack_flags;
glm::vec3 m_TargetRing; glm::vec3 m_TargetRing;
GravityBehavior flymode; GravityBehavior flymode;
+6 -58
View File
@@ -1391,29 +1391,8 @@ void Mob::AI_Process() {
StopNavigation(); StopNavigation();
} }
} }
else if (zone->CanDoCombat() && CastToNPC()->GetNPCAggro() && AI_scan_area_timer->Check()) { else if (zone->CanDoCombat() && IsNPC() && CastToNPC()->GetNPCAggro() && AI_scan_area_timer->Check()) {
CastToNPC()->DoNpcToNpcAggroScan();
/**
* NPC to NPC aggro (npc_aggro flag set)
*/
for (auto &close_mob : close_mobs) {
Mob *mob = close_mob.second;
if (mob->IsClient()) {
continue;
}
if (CheckWillAggro(mob)) {
AddToHateList(mob);
}
}
AI_scan_area_timer->Disable();
AI_scan_area_timer->Start(
RandomTimer(RuleI(NPC, NPCToNPCAggroTimerMin), RuleI(NPC, NPCToNPCAggroTimerMax)),
false
);
} }
else if (AI_movement_timer->Check() && !IsRooted()) { else if (AI_movement_timer->Check() && !IsRooted()) {
if (IsPet()) { if (IsPet()) {
@@ -1776,6 +1755,8 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
SetAppearance(eaStanding); SetAppearance(eaStanding);
parse->EventBotMerc(EVENT_COMBAT, this, attacker, [&] { return "1"; });
if (IsNPC()) { if (IsNPC()) {
CastToNPC()->AIautocastspell_timer->Start(300, false); CastToNPC()->AIautocastspell_timer->Start(300, false);
@@ -1814,12 +1795,6 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
} }
} }
} }
if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_COMBAT)) {
parse->EventBot(EVENT_COMBAT, CastToBot(), attacker, "1", 0);
}
}
} }
// Note: Hate list may not be actually clear until after this function call completes // Note: Hate list may not be actually clear until after this function call completes
@@ -1840,15 +1815,12 @@ void Mob::AI_Event_NoLongerEngaged() {
StopNavigation(); StopNavigation();
ClearRampage(); ClearRampage();
parse->EventBotMercNPC(EVENT_COMBAT, this, nullptr, [&]() { return "0"; });
if (IsNPC()) { if (IsNPC()) {
SetPrimaryAggro(false); SetPrimaryAggro(false);
SetAssistAggro(false); SetAssistAggro(false);
if (CastToNPC()->GetCombatEvent() && GetHP() > 0) { if (CastToNPC()->GetCombatEvent() && GetHP() > 0) {
if (entity_list.GetNPCByID(GetID())) {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_COMBAT)) {
parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0);
}
const uint32 emote_id = CastToNPC()->GetEmoteID(); const uint32 emote_id = CastToNPC()->GetEmoteID();
if (emote_id) { if (emote_id) {
CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emote_id); CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emote_id);
@@ -1858,11 +1830,6 @@ void Mob::AI_Event_NoLongerEngaged() {
CastToNPC()->SetCombatEvent(false); CastToNPC()->SetCombatEvent(false);
} }
} }
} else if (IsBot()) {
if (parse->BotHasQuestSub(EVENT_COMBAT)) {
parse->EventBot(EVENT_COMBAT, CastToBot(), nullptr, "0", 0);
}
}
} }
//this gets called from InterruptSpell() for failure or SpellFinished() for success //this gets called from InterruptSpell() for failure or SpellFinished() for success
@@ -2931,25 +2898,6 @@ DBnpcspells_Struct *ZoneDatabase::GetNPCSpells(uint32 npc_spells_id)
return nullptr; return nullptr;
} }
uint32 ZoneDatabase::GetMaxNPCSpellsID() {
std::string query = "SELECT max(id) from npc_spells";
auto results = QueryDatabase(query);
if (!results.Success()) {
return 0;
}
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
if (!row[0])
return 0;
return Strings::ToInt(row[0]);
}
DBnpcspellseffects_Struct *ZoneDatabase::GetNPCSpellsEffects(uint32 iDBSpellsEffectsID) DBnpcspellseffects_Struct *ZoneDatabase::GetNPCSpellsEffects(uint32 iDBSpellsEffectsID)
{ {
if (iDBSpellsEffectsID == 0) if (iDBSpellsEffectsID == 0)

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