Compare commits

..

41 Commits

Author SHA1 Message Date
Akkadius 7e94f0ac72 [Release] 22.47.0 2024-03-05 22:18:16 -06:00
Chris Miles 2aa19f4cae [Release] 22.47.0 (#4164) 2024-03-05 22:04:35 -06:00
Mitch Freeman 805829f15d [Crash Fix] Added a guild_mgr check (#4163)
* CrashFix and Cleanup

* Formatting, add safe_delete

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
2024-03-05 22:00:08 -06:00
Alex King 2c2a8cdb63 [Bug Fix] Fix Character EXP Modifiers default (#4161)
# Notes
- The `-1.0f` was causing these modifiers to be set to `0.0` and when people would enable them they would get no experience.
- We now use the zone based grabbed methods when setting which will default to a value of `1.0f` if there is not a value found.
2024-03-05 21:54:07 -06:00
Chris Miles ee3d02bac6 [Zoning] Zone routing adjustment (#4162) 2024-03-05 21:53:07 -06:00
Alex King b90139bd9a [Feature] Adjust String-based Rules Length (#4138) 2024-03-05 21:52:34 -06:00
catapultam-habeo e6a3d5e1c5 [Bug Fix] Prevent NPE when creating DZ using ad-hoc version IDs (#4141)
* initial commit

* corrected based on hgtw feedback
2024-03-05 18:21:04 -05:00
Alex King 74f1eac401 [Bug Fix] Fix Spawns Not Parsing Quest on Zone Bootup (#4149)
* Update zone.cpp

* Fix

---------

Co-authored-by: Natedog2012 <jwalters_06@yahoo.com>
2024-03-05 18:20:42 -05:00
JJ 1be9b2cdfd [Bug Fix] Fix typo when updating spawn events in spawn condition manager (#4160) 2024-03-05 05:44:38 -05:00
Mitch Freeman add0a8dddf [Bug Fix] Add id to the guild_bank table (#4155)
* Add id to guild_bank table

Add id as a primary key to guild_bank

* Remove content schema update flag
2024-03-04 19:02:35 -05:00
catapultam-habeo 8c226054e7 [Feature] Adds rules to control level requirements for Double Backstab, Assassinate, and Double Bowshot (#29) (#4159)
Co-authored-by: mute <natanx@gmail.com>
2024-03-04 18:41:25 -05:00
Mitch Freeman b4605f77e3 [Crash Fix] Goto Command could crash using Developer Tools (#4158) 2024-03-03 22:33:12 -05:00
Mitch Freeman 74a63daf7e [Crash Fix] Groundspawn Memory Corruption (#4157) 2024-03-03 22:32:29 -05:00
Alex King 8ee7910569 [Quest API] Add IsAlwaysAggro() to Perl/Lua (#4152)
- Add `$mob->IsAlwaysAggro()`.

- Add `mob:IsAlwaysAggro()`.

- Allows operators to determine if a mob is set to always aggro.
2024-03-03 20:40:20 -05:00
Alex King b766a79c11 [Quest API] Add GetHeroicStrikethrough() to Perl/Lua (#4150)
- Add `$mob->GetHeroicStrikethrough()`.

- Add `mob:GetHeroicStrikethrough()`.

- Allows operators to get a mob's Heroic Strikethrough.
2024-03-03 13:05:01 -05:00
Alex King 5e3b6d363a [Quest API] Add IsBoat()/IsControllableBoat() to Perl/Lua (#4151)
- Add `$mob->IsBoat()`.
- Add `$mob->IsControllableBoat()`.

- Add `mob:IsBoat()`.
- Add `mob:IsControllableBoat()`.

- Allows operators to determine if a mob is a boat or a controllable boat.
2024-03-03 12:34:21 -05:00
Alex King b2fc59878a [Quest API] Add IsDestructibleObject() to Perl/Lua (#4153)
# Perl
- Add `$mob-.IsDestructibleObject()`.

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

# Notes
- Allows operators to determine if a mob is a destructible object.
2024-03-03 11:43:54 -05:00
JJ 4896688ac5 [Release] 22.46.1 (#4148)
### Fixes

* Change `UnburyCorpse` to use repository methods ([#4147](https://github.com/EQEmu/Server/pull/4147)) @joligario 2024-03-03
2024-03-02 19:56:29 -05:00
JJ 0385ed8526 [Bug Fix] Change UnburyCorpse to use repository methods (#4147) 2024-03-02 19:23:43 -05:00
Chris Miles 9974aaff57 [Release] 22.46.0 (#4146) 2024-03-02 15:21:32 -06:00
Chris Miles b6c3e549da [Zone] Zone Routing Improvements (#4142)
* Routing changes

* Update world_content_service.cpp

* Cleanup routing logic

* Tweaks
2024-03-02 15:19:42 -06:00
Alex King 70ee95efc0 [Quest API] Add Bot Special Attacks for Immune Aggro/Damage (#4108)
* [Quest API] Add Bot Special Attacks for Immune Aggro/Damage

# Notes
- Adds `IMMUNE_AGGRO_BOT` and `IMMUNE_DAMAGE_BOT` for uses in special abilities.

* Cleanup

* Update attack.cpp
2024-03-02 15:19:31 -06:00
Alex King 1d38e473d7 [Bug Fix] GetBotNameByID Temporary Reference Warning (#4145)
# Notes
- We were getting a warning for returning `std::string()` from this method as it's a temporary reference.
- Change from `const std::string&` to `const std::string` to avoid this.
```
/home/eqemu/source/zone/bot_database.cpp: In member function ‘const std::string& BotDatabase::GetBotNameByID(uint32)’:
/home/eqemu/source/zone/bot_database.cpp:2374:25: warning: returning reference to temporary [-Wreturn-local-addr]
 2374 |         return e.bot_id ? e.name : std::string();
```
2024-03-02 15:18:57 -06:00
Alex King 1aa3a4b11a [Bug Fix] Fix Bots/Bot Pets ending up on XTargets (#4132)
* [XTargets]

* Update eqemu_logsys.h

* Update client.cpp

* Update table column

* Undo unnecessary commit
2024-03-02 15:18:37 -06:00
Mitch Freeman 398ecbc8cf [Bug Fix] Update FreeGuildID Routine (#4143)
Updates the routine to determine a free guild id on guild creation
2024-03-02 15:17:28 -06:00
Mitch Freeman c4613e1b0f [Crash Fix] Update to location of qGlobals initialization (#4144) 2024-03-02 15:16:51 -06:00
catapultam-habeo 3003a59955 [Feature] Exempt a zone from IP-limit checks. (#4137)
* Exempt zone from IP checks

* [Feature] Add Support for String-based Rules

# Notes
- Add support for string-based rules.

# Images

* convert to comma-seperated list

* Forgot to convert the zone to a string

* Update lua_general.cpp

* fixed rule name

* use the local string methods instead

* I think this will work as desired without the extra condition

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
2024-03-01 23:11:34 -05:00
Paul Coene 0c582cc4f9 [Bug Fix] Cleanup NPC Mana Tap Logic (#4134)
* [Bug Fix] Cleanup NPC Mana Tap Logic

Mana Tap rule logic was invalid - Cleaned up and simplified, either we care about npc mana or we dont.

* Updated for bypass all rule

* Change so melee targets get blocked even with requiremana rule off

---------

Co-authored-by: Trust <fryguy503@gmail.com>
2024-03-01 22:48:50 -05:00
Mitch Freeman 69c42510ca [Crash Fix] Raid::UpdateGroupAAs (#4139)
Added checks to potentially resolve a crash situation with raids and group AAs.
2024-03-01 22:46:16 -05:00
JJ 79c8858ec8 [Bug Fix] Use std::clamp for Mob::ChangeSize (#4140)
Helper template was not deducing float for lower/upper values allowing invalid sizes
Limit to sane values of 1-255 unrestricted and 3-15 for clients and pets
2024-03-01 22:46:00 -05:00
Alex King c3d8d423fe [Commands] Add #fish Command (#4136)
* [Commands] Add #fish Command

# Notes
- Adds `#fish` command.
- Allows operators to simulate fishing to see what they would get, consumes no bait.

* `use_bait`

* Update fish.cpp
2024-02-29 21:24:16 -05:00
Alex King 1cbda61891 [Bug Fix] Fix issue with NPC Secondary Textures (#4129)
* [Bug Fix] Fix issue with NPC heads

# Notes
- We were overwriting head material within this secondary loop which caused NPC's heads to show their body texture in some places or no texture if their `showhelm` was not flagged.

# Images

* Update mob.cpp

* Update mob.cpp

* Update mob.cpp
2024-02-29 20:45:54 -05:00
JJ 8d12a5b1b1 [Release] 22.45.1 (#4135)
### Character Creation

* Improved Random Name Generator ([#4081](https://github.com/EQEmu/Server/pull/4081)) @catapultam-habeo 2024-02-27

### Code

* Fix Server Rules Documentation Generation ([#4125](https://github.com/EQEmu/Server/pull/4125)) @Kinglykrab 2024-02-26
* Remove unnecessary stoptimer logs ([#4128](https://github.com/EQEmu/Server/pull/4128)) @Kinglykrab 2024-02-28

### Commands

* Add `#forage` command ([#4133](https://github.com/EQEmu/Server/pull/4133)) @joligario 2024-02-29

### Crash

* Fix crash issue during database dump ([#4127](https://github.com/EQEmu/Server/pull/4127)) @Akkadius 2024-02-29

### Crash Fix

* D20 crash if mitigation average resulted in 0 ([#4131](https://github.com/EQEmu/Server/pull/4131)) @nytmyr 2024-02-29

### Fixes

* Fix forage returning first result from table ([#4130](https://github.com/EQEmu/Server/pull/4130)) @nytmyr 2024-02-29
* Who /all displays incorrect guild name ([#4123](https://github.com/EQEmu/Server/pull/4123)) @neckkola 2024-02-25

### Quest API

* Add Pet Owner Methods to Perl/Lua ([#4115](https://github.com/EQEmu/Server/pull/4115)) @Kinglykrab 2024-02-25
2024-02-29 18:22:40 -05:00
JJ 182327b385 [Commands] Add #forage command (#4133) 2024-02-28 20:43:14 -05:00
Chris Miles aa0ca88d9d [Crash] Fix crash issue during database dump (#4127) 2024-02-28 20:36:06 -05:00
nytmyr 103a37e762 [Crash Fix] D20 crash if mitigation average resulted in 0 (#4131) 2024-02-28 20:35:56 -05:00
nytmyr 34f19489d0 [Hotfix] Fix forage returning first result from table (#4130)
Forage was not properly incrementing the total chance of items when more than one was found and would result in the final chance roll being based off the last item found rather than the total. This would cause the first item returned to be chosen in most cases when the chances are the same in the table.
2024-02-28 20:34:40 -05:00
Alex King c001060429 [Cleanup] Remove unnecessary stoptimer logs (#4128)
# Notes
- These logs were for testing and are unnecessary.
- They cause a lot of spam for servers using `Info` logs.
2024-02-27 21:35:51 -05:00
catapultam-habeo 89be55254e [Character Creation] Improved Random Name Generator (#4081)
* test against vanilla branch

* use existing methods to validate name instead of raw sql

* Revert "use existing methods to validate name instead of raw sql"

This reverts commit 43750c6f4f.

* ReserveName doesn't work like that. Oops. Well, check against Name Filter at least.

* That db access condition was wrong.

* that isn't how CheckNameFilter works, either.

* apply editorconfig w/ trivial change

* Actually apply editorconfig changes.
2024-02-26 23:15:40 -06:00
Alex King 2da6190950 [Cleanup] Fix Server Rules Documentation Generation (#4125)
# Notes
- These spaces prevented the docs server rules generator from parsing these rules.
2024-02-26 18:36:12 -05:00
Mitch Freeman d5e024cc02 [Bug Fix] Who /all displays incorrect guild name (#4123)
Issue: guild creation routine does not update world memory holding guild details until the player zones.

Between the creation of a new guild, and a guild member zones, a /who all displays 'Invalid Guild' instead of the actual guild name.
2024-02-25 18:14:28 -05:00
58 changed files with 1011 additions and 364 deletions
+112
View File
@@ -1,3 +1,115 @@
## [22.47.0] 3/5/2024
### Crash Fix
* Added a guild_mgr check ([#4163](https://github.com/EQEmu/Server/pull/4163)) @neckkola 2024-03-06
* Goto Command could crash using Developer Tools ([#4158](https://github.com/EQEmu/Server/pull/4158)) @neckkola 2024-03-04
* Groundspawn Memory Corruption ([#4157](https://github.com/EQEmu/Server/pull/4157)) @neckkola 2024-03-04
* Update to location of qGlobals initialization ([#4144](https://github.com/EQEmu/Server/pull/4144)) @neckkola 2024-03-02
### Feature
* Adds rules to control level requirements for Double Backstab, Assassinate, and Double Bowshot (#4159) ([#29](https://github.com/EQEmu/Server/pull/29)) @catapultam-habeo 2024-03-04
* Adjust String-based Rules Length ([#4138](https://github.com/EQEmu/Server/pull/4138)) @Kinglykrab 2024-03-06
* Exempt a zone from IP-limit checks. ([#4137](https://github.com/EQEmu/Server/pull/4137)) @catapultam-habeo 2024-03-02
### Fixes
* Add id to the guild_bank table ([#4155](https://github.com/EQEmu/Server/pull/4155)) @neckkola 2024-03-05
* Fix Bots/Bot Pets ending up on XTargets ([#4132](https://github.com/EQEmu/Server/pull/4132)) @Kinglykrab 2024-03-02
* Fix Character EXP Modifiers default ([#4161](https://github.com/EQEmu/Server/pull/4161)) @Kinglykrab 2024-03-06
* Fix Spawns Not Parsing Quest on Zone Bootup ([#4149](https://github.com/EQEmu/Server/pull/4149)) @Kinglykrab 2024-03-05
* Fix typo when updating spawn events in spawn condition manager ([#4160](https://github.com/EQEmu/Server/pull/4160)) @joligario 2024-03-05
* GetBotNameByID Temporary Reference Warning ([#4145](https://github.com/EQEmu/Server/pull/4145)) @Kinglykrab 2024-03-02
* Prevent NPE when creating DZ using ad-hoc version IDs ([#4141](https://github.com/EQEmu/Server/pull/4141)) @catapultam-habeo 2024-03-05
* Update FreeGuildID Routine ([#4143](https://github.com/EQEmu/Server/pull/4143)) @neckkola 2024-03-02
### Quest API
* Add Bot Special Attacks for Immune Aggro/Damage ([#4108](https://github.com/EQEmu/Server/pull/4108)) @Kinglykrab 2024-03-02
* Add GetHeroicStrikethrough() to Perl/Lua ([#4150](https://github.com/EQEmu/Server/pull/4150)) @Kinglykrab 2024-03-03
* Add IsAlwaysAggro() to Perl/Lua ([#4152](https://github.com/EQEmu/Server/pull/4152)) @Kinglykrab 2024-03-04
* Add IsBoat()/IsControllableBoat() to Perl/Lua ([#4151](https://github.com/EQEmu/Server/pull/4151)) @Kinglykrab 2024-03-03
* Add IsDestructibleObject() to Perl/Lua ([#4153](https://github.com/EQEmu/Server/pull/4153)) @Kinglykrab 2024-03-03
### Zone
* Zone Routing Improvements ([#4142](https://github.com/EQEmu/Server/pull/4142)) @Akkadius 2024-03-02
### Zoning
* Zone routing adjustment ([#4162](https://github.com/EQEmu/Server/pull/4162)) @Akkadius 2024-03-06
## [22.46.1] 3/2/2024
### Fixes
* Change `UnburyCorpse` to use repository methods ([#4147](https://github.com/EQEmu/Server/pull/4147)) @joligario 2024-03-03
## [22.46.0] 3/2/2024
### Commands
* Add #fish Command ([#4136](https://github.com/EQEmu/Server/pull/4136)) @Kinglykrab 2024-03-01
### Crash Fix
* Raid::UpdateGroupAAs ([#4139](https://github.com/EQEmu/Server/pull/4139)) @neckkola 2024-03-02
* Update to location of qGlobals initialization ([#4144](https://github.com/EQEmu/Server/pull/4144)) @neckkola 2024-03-02
### Feature
* Exempt a zone from IP-limit checks. ([#4137](https://github.com/EQEmu/Server/pull/4137)) @catapultam-habeo 2024-03-02
### Fixes
* Cleanup NPC Mana Tap Logic ([#4134](https://github.com/EQEmu/Server/pull/4134)) @noudess 2024-03-02
* Fix Bots/Bot Pets ending up on XTargets ([#4132](https://github.com/EQEmu/Server/pull/4132)) @Kinglykrab 2024-03-02
* Fix issue with NPC Secondary Textures ([#4129](https://github.com/EQEmu/Server/pull/4129)) @Kinglykrab 2024-03-01
* GetBotNameByID Temporary Reference Warning ([#4145](https://github.com/EQEmu/Server/pull/4145)) @Kinglykrab 2024-03-02
* Update FreeGuildID Routine ([#4143](https://github.com/EQEmu/Server/pull/4143)) @neckkola 2024-03-02
* Use std::clamp for Mob::ChangeSize ([#4140](https://github.com/EQEmu/Server/pull/4140)) @joligario 2024-03-02
### Quest API
* Add Bot Special Attacks for Immune Aggro/Damage ([#4108](https://github.com/EQEmu/Server/pull/4108)) @Kinglykrab 2024-03-02
### Zone
* Zone Routing Improvements ([#4142](https://github.com/EQEmu/Server/pull/4142)) @Akkadius 2024-03-02
## [22.45.1] 2/29/2024
### Character Creation
* Improved Random Name Generator ([#4081](https://github.com/EQEmu/Server/pull/4081)) @catapultam-habeo 2024-02-27
### Code
* Fix Server Rules Documentation Generation ([#4125](https://github.com/EQEmu/Server/pull/4125)) @Kinglykrab 2024-02-26
* Remove unnecessary stoptimer logs ([#4128](https://github.com/EQEmu/Server/pull/4128)) @Kinglykrab 2024-02-28
### Commands
* Add `#forage` command ([#4133](https://github.com/EQEmu/Server/pull/4133)) @joligario 2024-02-29
### Crash
* Fix crash issue during database dump ([#4127](https://github.com/EQEmu/Server/pull/4127)) @Akkadius 2024-02-29
### Crash Fix
* D20 crash if mitigation average resulted in 0 ([#4131](https://github.com/EQEmu/Server/pull/4131)) @nytmyr 2024-02-29
### Fixes
* Fix forage returning first result from table ([#4130](https://github.com/EQEmu/Server/pull/4130)) @nytmyr 2024-02-29
* Who /all displays incorrect guild name ([#4123](https://github.com/EQEmu/Server/pull/4123)) @neckkola 2024-02-25
### Quest API
* Add Pet Owner Methods to Perl/Lua ([#4115](https://github.com/EQEmu/Server/pull/4115)) @Kinglykrab 2024-02-25
## [22.45.0] 2/24/2024 ## [22.45.0] 2/24/2024
### Beacon ### Beacon
+77 -36
View File
@@ -178,12 +178,13 @@ void WorldContentService::ReloadContentFlags()
LogInfo( LogInfo(
"Loaded content flag [{}] [{}]", "Loaded content flag [{}] [{}]",
f.flag_name, f.flag_name,
(f.enabled ? "Enabled" : "Disabled") (f.enabled ? "enabled" : "disabled")
); );
} }
SetContentFlags(set_content_flags); SetContentFlags(set_content_flags);
SetContentZones(ZoneRepository::All(*m_content_database)); LoadZones();
LoadStaticGlobalZoneInstances();
} }
Database *WorldContentService::GetDatabase() const Database *WorldContentService::GetDatabase() const
@@ -235,19 +236,6 @@ void WorldContentService::SetContentFlag(const std::string &content_flag_name, b
ReloadContentFlags(); ReloadContentFlags();
} }
// SetZones sets the zones for the world content service
// this is used for zone routing middleware
// we pull the zone list from the zone repository and feed from the zone store for now
// we're holding a copy in the content service - but we're talking 250kb of data in memory to handle routing of zoning
WorldContentService *WorldContentService::SetContentZones(const std::vector<BaseZoneRepository::Zone>& zones)
{
m_zones = zones;
LogInfo("Loaded [{}] zones", m_zones.size());
return this;
}
// HandleZoneRoutingMiddleware is meant to handle content and context aware zone routing // HandleZoneRoutingMiddleware is meant to handle content and context aware zone routing
// //
// example # 1 // example # 1
@@ -260,16 +248,57 @@ WorldContentService *WorldContentService::SetContentZones(const std::vector<Base
// scripts handle all the same way, you don't have to think about instances, the middleware will handle the magic // scripts handle all the same way, you don't have to think about instances, the middleware will handle the magic
// the versions of zones are represented by two zone entries that have potentially different min/max expansion and/or different content flags // the versions of zones are represented by two zone entries that have potentially different min/max expansion and/or different content flags
// we decide to route the client to the correct version of the zone based on the current server side expansion // we decide to route the client to the correct version of the zone based on the current server side expansion
// example # 2
void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc) void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
{ {
// if we're already in an instance, we don't want to route the player to another instance auto r = FindZone(zc->zoneID, zc->instanceID);
if (zc->instanceID > 0) { if (r.zone_id == 0) {
return; return;
} }
zc->instanceID = r.instance.id;
}
// LoadStaticGlobalZoneInstances loads all static global zone instances
// these are zones that are never set to expire and are global
// these are used commonly in v1/v2/v3 versions of the same zone for expansion routing
WorldContentService * WorldContentService::LoadStaticGlobalZoneInstances()
{
m_zone_instances = InstanceListRepository::GetWhere(*GetDatabase(), fmt::format("never_expires = 1 AND is_global = 1"));
LogInfo("Loaded [{}] zone_instances", m_zone_instances.size());
return this;
}
// LoadZones sets the zones for the world content service
// this is used for zone routing middleware
// we pull the zone list from the zone repository and feed from the zone store for now
// we're holding a copy in the content service - but we're talking 250kb of data in memory to handle routing of zoning
WorldContentService * WorldContentService::LoadZones()
{
m_zones = ZoneRepository::All(*GetContentDatabase());
LogInfo("Loaded [{}] zones", m_zones.size());
return this;
}
// FindZone is critical to the zone routing middleware and any logic that needs to route players to the correct zone
// era contextual routing, multiple version of zones, etc
WorldContentService::FindZoneResult WorldContentService::FindZone(uint32 zone_id, uint32 instance_id)
{
// if there's an active dynamic instance, we don't need to route
if (instance_id > 0) {
auto inst = InstanceListRepository::FindOne(*GetDatabase(), instance_id);
if (inst.id != 0 && !inst.is_global && !inst.never_expires) {
return WorldContentService::FindZoneResult{
.zone_id = 0,
};
}
}
for (auto &z: m_zones) { for (auto &z: m_zones) {
if (z.zoneidnumber == zc->zoneID) { if (z.zoneidnumber == zone_id) {
auto f = ContentFlags{ auto f = ContentFlags{
.min_expansion = z.min_expansion, .min_expansion = z.min_expansion,
.max_expansion = z.max_expansion, .max_expansion = z.max_expansion,
@@ -286,33 +315,45 @@ void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
z.long_name z.long_name
); );
auto instances = InstanceListRepository::GetWhere( // first pass, explicit match on public static global zone instances
*GetDatabase(), for (auto &i: m_zone_instances) {
fmt::format( if (i.zone == zone_id && i.version == z.version) {
"zone = {} AND version = {} AND never_expires = 1 AND is_global = 1",
z.zoneidnumber,
z.version
)
);
if (!instances.empty()) {
auto instance = instances.front();
zc->instanceID = instance.id;
LogInfo( LogInfo(
"Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]", "Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
instance.id, i.id,
z.short_name, z.short_name,
z.zoneidnumber, z.zoneidnumber,
z.version, z.version,
z.long_name, z.long_name,
instance.notes i.notes
); );
break; return WorldContentService::FindZoneResult{
} .zone_id = static_cast<uint32>(z.zoneidnumber),
.instance = i,
.zone = z
};
}
}
LogInfo(
"Routed player to non-instance zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
z.short_name,
z.zoneidnumber,
z.version,
z.long_name,
z.note
);
return WorldContentService::FindZoneResult{
.zone_id = static_cast<uint32>(z.zoneidnumber),
.instance = InstanceListRepository::NewEntity(),
.zone = z
};
} }
} }
} }
return WorldContentService::FindZoneResult{.zone_id = 0};
} }
+12 -1
View File
@@ -5,6 +5,7 @@
#include <vector> #include <vector>
#include "../repositories/content_flags_repository.h" #include "../repositories/content_flags_repository.h"
#include "../repositories/zone_repository.h" #include "../repositories/zone_repository.h"
#include "../repositories/instance_list_repository.h"
class Database; class Database;
@@ -169,7 +170,14 @@ public:
void SetContentFlag(const std::string &content_flag_name, bool enabled); void SetContentFlag(const std::string &content_flag_name, bool enabled);
void HandleZoneRoutingMiddleware(ZoneChange_Struct *zc); void HandleZoneRoutingMiddleware(ZoneChange_Struct *zc);
WorldContentService * SetContentZones(const std::vector<ZoneRepository::Zone>& zones);
struct FindZoneResult {
uint32 zone_id = 0;
InstanceListRepository::InstanceList instance;
ZoneRepository::Zone zone;
};
FindZoneResult FindZone(uint32 zone_id, uint32 instance_id);
private: private:
int current_expansion{}; int current_expansion{};
std::vector<ContentFlagsRepository::ContentFlags> content_flags; std::vector<ContentFlagsRepository::ContentFlags> content_flags;
@@ -180,6 +188,9 @@ private:
// holds a record of the zone table from the database // holds a record of the zone table from the database
std::vector<ZoneRepository::Zone> m_zones = {}; std::vector<ZoneRepository::Zone> m_zones = {};
WorldContentService *LoadStaticGlobalZoneInstances();
std::vector<InstanceListRepository::InstanceList> m_zone_instances;
WorldContentService * LoadZones();
}; };
extern WorldContentService content_service; extern WorldContentService content_service;
@@ -575,8 +575,13 @@ void DatabaseDumpService::RemoveSqlBackup()
{ {
std::string file = fmt::format("{}.sql", GetDumpFileNameWithPath()); std::string file = fmt::format("{}.sql", GetDumpFileNameWithPath());
if (File::Exists(file)) { if (File::Exists(file)) {
try {
std::filesystem::remove(file); std::filesystem::remove(file);
} }
catch (std::exception &e) {
LogError("std::filesystem::remove err [{}]", e.what());
}
}
RemoveCredentialsFile(); RemoveCredentialsFile();
} }
@@ -5410,6 +5410,29 @@ ADD COLUMN `augment_five` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_fou
ADD COLUMN `augment_six` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_five`; ADD COLUMN `augment_six` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_five`;
)", )",
.content_schema_update = true .content_schema_update = true
},
ManifestEntry{
.version = 9265,
.description = "2024_03_03_add_id_to_guild_bank.sql",
.check = "SHOW COLUMNS FROM `guild_bank` LIKE 'id'",
.condition = "empty",
.match = "",
.sql = R"(
ALTER TABLE `guild_bank`
ADD COLUMN `id` INT UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
ADD PRIMARY KEY (`id`);
)",
},
ManifestEntry{
.version = 9266,
.description = "2024_03_02_rule_values_rule_value_length.sql",
.check = "SHOW COLUMNS FROM `rule_values` LIKE 'rule_value'",
.condition = "contains",
.match = "varchar(30)",
.sql = R"(
ALTER TABLE `rule_values`
MODIFY COLUMN `rule_value` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `rule_name`;
)"
} }
// -- template; copy/paste this when you need to create a new entry // -- template; copy/paste this when you need to create a new entry
// ManifestEntry{ // ManifestEntry{
+2
View File
@@ -698,6 +698,8 @@ const std::map<uint32, std::string>& EQ::constants::GetSpecialAbilityMap()
{ IMMUNE_OPEN, "Immune to Open" }, { IMMUNE_OPEN, "Immune to Open" },
{ IMMUNE_ASSASSINATE, "Immune to Assassinate" }, { IMMUNE_ASSASSINATE, "Immune to Assassinate" },
{ IMMUNE_HEADSHOT, "Immune to Headshot" }, { IMMUNE_HEADSHOT, "Immune to Headshot" },
{ IMMUNE_AGGRO_BOT, "Immune to Bot Aggro" },
{ IMMUNE_DAMAGE_BOT, "Immune to Bot Damage" },
}; };
return special_ability_map; return special_ability_map;
+3 -1
View File
@@ -656,7 +656,9 @@ enum {
IMMUNE_OPEN = 53, IMMUNE_OPEN = 53,
IMMUNE_ASSASSINATE = 54, IMMUNE_ASSASSINATE = 54,
IMMUNE_HEADSHOT = 55, IMMUNE_HEADSHOT = 55,
MAX_SPECIAL_ATTACK = 56 IMMUNE_AGGRO_BOT = 56,
IMMUNE_DAMAGE_BOT = 57,
MAX_SPECIAL_ATTACK = 58
}; };
+2
View File
@@ -141,6 +141,7 @@ namespace Logs {
Zoning, Zoning,
EqTime, EqTime,
Corpses, Corpses,
XTargets,
MaxCategoryID /* Don't Remove this */ MaxCategoryID /* Don't Remove this */
}; };
@@ -241,6 +242,7 @@ namespace Logs {
"Zoning", "Zoning",
"EqTime", "EqTime",
"Corpses", "Corpses",
"XTargets"
}; };
} }
+10
View File
@@ -834,6 +834,16 @@
OutF(LogSys, Logs::Detail, Logs::Corpses, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ OutF(LogSys, Logs::Detail, Logs::Corpses, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0) } while (0)
#define LogXTargets(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::General, Logs::XTargets))\
OutF(LogSys, Logs::General, Logs::XTargets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogXTargetsDetail(message, ...) do {\
if (LogSys.IsLogEnabled(Logs::Detail, Logs::XTargets))\
OutF(LogSys, Logs::Detail, Logs::XTargets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define Log(debug_level, log_category, message, ...) do {\ #define Log(debug_level, log_category, message, ...) do {\
if (LogSys.IsLogEnabled(debug_level, log_category))\ if (LogSys.IsLogEnabled(debug_level, log_category))\
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
+2 -29
View File
@@ -342,39 +342,12 @@ bool BaseGuildManager::_StoreGuildDB(uint32 guild_id)
return true; return true;
} }
uint32 BaseGuildManager::_GetFreeGuildID()
{
GuildsRepository::DeleteWhere(*m_db, "`name` = ''");
GuildsRepository::Guilds out;
out.id = 0;
out.leader = 0;
out.minstatus = 0;
out.tribute = 0;
out.name = "";
out.motd = "";
out.motd_setter = "";
out.url = "";
out.channel = "";
auto last_insert_id = GuildsRepository::InsertOne(*m_db, out);
if (last_insert_id.id > 0) {
LogGuilds("Located a free guild ID [{}] in the database", last_insert_id.id);
return last_insert_id.id;
}
LogGuilds("Unable to find a free guild ID in the database");
return GUILD_NONE;
}
uint32 BaseGuildManager::CreateGuild(std::string name, uint32 leader_char_id) uint32 BaseGuildManager::CreateGuild(std::string name, uint32 leader_char_id)
{ {
uint32 guild_id = UpdateDbCreateGuild(name, leader_char_id); uint32 guild_id = UpdateDbCreateGuild(name, leader_char_id);
if (guild_id == GUILD_NONE) { if (guild_id == GUILD_NONE) {
return (GUILD_NONE); return (GUILD_NONE);
} }
//RefreshGuild(guild_id);
//SendGuildRefresh(guild_id, true, false, false, false);
//SendCharRefresh(GUILD_NONE, guild_id, leader_char_id);
return guild_id; return guild_id;
} }
@@ -539,8 +512,8 @@ bool BaseGuildManager::SetPublicNote(uint32 charid, std::string public_note)
uint32 BaseGuildManager::UpdateDbCreateGuild(std::string name, uint32 leader) uint32 BaseGuildManager::UpdateDbCreateGuild(std::string name, uint32 leader)
{ {
auto new_id = _GetFreeGuildID(); auto new_id = GuildsRepository::GetMaxId(*m_db) + 1;
if (new_id == GUILD_NONE) { if (!new_id) {
return GUILD_NONE; return GUILD_NONE;
} }
-1
View File
@@ -208,7 +208,6 @@ class BaseGuildManager
bool _StoreGuildDB(uint32 guild_id); bool _StoreGuildDB(uint32 guild_id);
GuildInfo* _CreateGuild(uint32 guild_id, std::string guild_name, uint32 leader_char_id, uint8 minstatus, std::string guild_motd, std::string motd_setter, std::string Channel, std::string URL, uint32 favour); GuildInfo* _CreateGuild(uint32 guild_id, std::string guild_name, uint32 leader_char_id, uint8 minstatus, std::string guild_motd, std::string motd_setter, std::string Channel, std::string URL, uint32 favour);
uint32 _GetFreeGuildID();
GuildsRepository::Guilds CreateGuildRepoFromGuildInfo(uint32 guild_id, BaseGuildManager::GuildInfo& in); GuildsRepository::Guilds CreateGuildRepoFromGuildInfo(uint32 guild_id, BaseGuildManager::GuildInfo& in);
}; };
#endif /*GUILD_BASE_H_*/ #endif /*GUILD_BASE_H_*/
@@ -213,23 +213,23 @@ public:
const glm::vec4& position const glm::vec4& position
) )
{ {
auto results = db.QueryDatabase( auto corpse = FindOne(db, corpse_id);
fmt::format(
"UPDATE `{}` SET `is_buried` = 0, `zone_id` = {}, `instance_id` = {}, `x` = {:.2f}, `y` = {:.2f}, `z` = {:.2f}, `heading` = {:.2f}, `time_of_death` = {}, `was_at_graveyard` = 0 WHERE `{}` = {}",
TableName(),
zone_id,
instance_id,
position.x,
position.y,
position.z,
position.w,
std::time(nullptr),
PrimaryKey(),
corpse_id
)
);
return results.Success() ? results.RowsAffected() : 0; if (corpse.id == 0) {
return 0;
}
corpse.is_buried = 0;
corpse.zone_id = zone_id;
corpse.instance_id = instance_id;
corpse.x = position.x;
corpse.y = position.y;
corpse.z = position.z;
corpse.heading = position.w;
corpse.time_of_death = time(nullptr);
corpse.was_at_graveyard = 0;
return UpdateOne(db, corpse);
} }
}; };
@@ -74,9 +74,11 @@ public:
}; };
} }
const auto& m = l.front();
return EXPModifier{ return EXPModifier{
.aa_modifier = l[0].aa_modifier, .aa_modifier = m.aa_modifier,
.exp_modifier = l[0].exp_modifier .exp_modifier = m.exp_modifier
}; };
} }
+35 -5
View File
@@ -34,8 +34,8 @@ const char *RuleManager::s_categoryNames[_CatCount + 1] = {
"InvalidCategory" "InvalidCategory"
}; };
const RuleManager::RuleInfo RuleManager::s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount + 1] = { const RuleManager::RuleInfo RuleManager::s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount + StringRuleCount + 1] = {
/* this is done in three steps so we can reliably get to them by index*/ /* this is done in three steps, so we can reliably get to them by index*/
#define RULE_INT(category_name, rule_name, default_value, notes) \ #define RULE_INT(category_name, rule_name, default_value, notes) \
{ #category_name ":" #rule_name, Category__##category_name, IntRule, Int__##rule_name, notes }, { #category_name ":" #rule_name, Category__##category_name, IntRule, Int__##rule_name, notes },
#include "ruletypes.h" #include "ruletypes.h"
@@ -45,6 +45,9 @@ const RuleManager::RuleInfo RuleManager::s_RuleInfo[IntRuleCount + RealRuleCount
#define RULE_BOOL(category_name, rule_name, default_value, notes) \ #define RULE_BOOL(category_name, rule_name, default_value, notes) \
{ #category_name ":" #rule_name, Category__##category_name, BoolRule, Bool__##rule_name, notes }, { #category_name ":" #rule_name, Category__##category_name, BoolRule, Bool__##rule_name, notes },
#include "ruletypes.h" #include "ruletypes.h"
#define RULE_STRING(category_name, rule_name, default_value, notes) \
{ #category_name ":" #rule_name, Category__##category_name, StringRule, String__##rule_name, notes },
#include "ruletypes.h"
{ "Invalid Rule", _CatCount, IntRule } { "Invalid Rule", _CatCount, IntRule }
}; };
@@ -114,6 +117,9 @@ bool RuleManager::GetRule(const std::string &rule_name, std::string &rule_value)
case BoolRule: case BoolRule:
rule_value = m_RuleBoolValues[index] ? "true" : "false"; rule_value = m_RuleBoolValues[index] ? "true" : "false";
break; break;
case StringRule:
rule_value = m_RuleStringValues[index];
break;
} }
return true; return true;
@@ -152,6 +158,10 @@ bool RuleManager::SetRule(const std::string &rule_name, const std::string &rule_
m_RuleBoolValues[index] = static_cast<uint32>(Strings::ToBool(rule_value)); m_RuleBoolValues[index] = static_cast<uint32>(Strings::ToBool(rule_value));
LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleBoolValues[index] == 1 ? "true" : "false"); LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleBoolValues[index] == 1 ? "true" : "false");
break; break;
case StringRule:
m_RuleStringValues[index] = rule_value;
LogRules("Set rule [{}] to value [{}]", rule_name, rule_value);
break;
} }
if (db_save) { if (db_save) {
@@ -215,11 +225,13 @@ std::string RuleManager::_GetRuleName(RuleType type, uint16 index) {
return s_RuleInfo[index + IntRuleCount].name; return s_RuleInfo[index + IntRuleCount].name;
case BoolRule: case BoolRule:
return s_RuleInfo[index + IntRuleCount + RealRuleCount].name; return s_RuleInfo[index + IntRuleCount + RealRuleCount].name;
case StringRule:
return s_RuleInfo[index + IntRuleCount + RealRuleCount + StringRuleCount].name;
default: default:
break; break;
} }
return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount].name; return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount + StringRuleCount].name;
} }
//assumes index is valid! //assumes index is valid!
@@ -231,11 +243,13 @@ const std::string &RuleManager::_GetRuleNotes(RuleType type, uint16 index) {
return s_RuleInfo[index + IntRuleCount].notes; return s_RuleInfo[index + IntRuleCount].notes;
case BoolRule: case BoolRule:
return s_RuleInfo[index + IntRuleCount + RealRuleCount].notes; return s_RuleInfo[index + IntRuleCount + RealRuleCount].notes;
case StringRule:
return s_RuleInfo[index + IntRuleCount + RealRuleCount + StringRuleCount].notes;
default: default:
break; break;
} }
return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount].notes; return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount + StringRuleCount].notes;
} }
bool RuleManager::LoadRules(Database *db, const std::string &rule_set_name, bool reload) { bool RuleManager::LoadRules(Database *db, const std::string &rule_set_name, bool reload) {
@@ -343,6 +357,10 @@ void RuleManager::SaveRules(Database *db, const std::string &rule_set_name) {
for (i = 0; i < BoolRuleCount; i++) { for (i = 0; i < BoolRuleCount; i++) {
_SaveRule(db, BoolRule, i); _SaveRule(db, BoolRule, i);
} }
for (i = 0; i < StringRuleCount; i++) {
_SaveRule(db, StringRule, i);
}
} }
void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) { void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) {
@@ -367,6 +385,9 @@ void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) {
case BoolRule: case BoolRule:
rule_value = m_RuleBoolValues[index] ? "true" : "false"; rule_value = m_RuleBoolValues[index] ? "true" : "false";
break; break;
case StringRule:
rule_value = m_RuleStringValues[index];
break;
} }
const auto& rule_notes = _GetRuleNotes(type, index); const auto& rule_notes = _GetRuleNotes(type, index);
@@ -446,6 +467,10 @@ bool RuleManager::UpdateInjectedRules(Database *db, const std::string &rule_set_
rule_data[r.name].first = fmt::format("{}", m_RuleBoolValues[r.rule_index] ? "true" : "false"); rule_data[r.name].first = fmt::format("{}", m_RuleBoolValues[r.rule_index] ? "true" : "false");
rule_data[r.name].second = &r.notes; rule_data[r.name].second = &r.notes;
break; break;
case StringRule:
rule_data[r.name].first = m_RuleStringValues[r.rule_index];
rule_data[r.name].second = &r.notes;
break;
default: default:
break; break;
} }
@@ -552,7 +577,7 @@ bool RuleManager::RestoreRuleNotes(Database *db)
} }
} }
return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount]; return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount + StringRuleCount];
}(e.rule_name); }(e.rule_name);
if (Strings::Contains(rule.name, e.rule_name)) { if (Strings::Contains(rule.name, e.rule_name)) {
@@ -617,3 +642,8 @@ bool RuleManager::GetBoolRule(RuleManager::BoolType t) const
{ {
return m_RuleBoolValues[t] == 1; return m_RuleBoolValues[t] == 1;
} }
std::string RuleManager::GetStringRule(RuleManager::StringType t) const
{
return m_RuleStringValues[t];
}
+42 -22
View File
@@ -23,6 +23,7 @@
* - RuleI(category, rule) -> fetch an integer rule's value * - RuleI(category, rule) -> fetch an integer rule's value
* - RuleR(category, rule) -> fetch a real (float) rule's value * - RuleR(category, rule) -> fetch a real (float) rule's value
* - RuleB(category, rule) -> fetch a boolean/flag rule's value * - RuleB(category, rule) -> fetch a boolean/flag rule's value
* - RuleS(category, rule) -> fetch a string rule's value
* *
*/ */
@@ -35,6 +36,8 @@
RuleManager::Instance()->GetRealRule( RuleManager::Real__##rule_name ) RuleManager::Instance()->GetRealRule( RuleManager::Real__##rule_name )
#define RuleB(category_name, rule_name) \ #define RuleB(category_name, rule_name) \
RuleManager::Instance()->GetBoolRule( RuleManager::Bool__##rule_name ) RuleManager::Instance()->GetBoolRule( RuleManager::Bool__##rule_name )
#define RuleS(category_name, rule_name) \
RuleManager::Instance()->GetStringRule( RuleManager::String__##rule_name )
#include <vector> #include <vector>
@@ -81,6 +84,17 @@ public:
static const int BoolRuleCount = static_cast<int>(_BoolRuleCount); static const int BoolRuleCount = static_cast<int>(_BoolRuleCount);
typedef enum {
#define RULE_STRING(category_name, rule_name, default_value, notes) \
String__##rule_name,
#include "ruletypes.h"
_StringRuleCount
} StringType;
static const int StringRuleCount = static_cast<int>(_StringRuleCount);
typedef enum { typedef enum {
#define RULE_CATEGORY(category_name) \ #define RULE_CATEGORY(category_name) \
Category__##category_name, Category__##category_name,
@@ -99,45 +113,49 @@ public:
static const IntType InvalidInt = _IntRuleCount; static const IntType InvalidInt = _IntRuleCount;
static const RealType InvalidReal = _RealRuleCount; static const RealType InvalidReal = _RealRuleCount;
static const BoolType InvalidBool = _BoolRuleCount; static const BoolType InvalidBool = _BoolRuleCount;
static const StringType InvalidString = _StringRuleCount;
static const CategoryType InvalidCategory = _CatCount; static const CategoryType InvalidCategory = _CatCount;
static const uint32 RulesCount = IntRuleCount + RealRuleCount + BoolRuleCount; static const uint32 RulesCount = IntRuleCount + RealRuleCount + BoolRuleCount + StringRuleCount;
//fetch routines, you should generally use the Rule* macros instead of this //fetch routines, you should generally use the Rule* macros instead of this
int GetIntRule(IntType t) const; int GetIntRule(IntType t) const;
float GetRealRule(RealType t) const; float GetRealRule(RealType t) const;
bool GetBoolRule(BoolType t) const; bool GetBoolRule(BoolType t) const;
std::string GetStringRule(StringType t) const;
//management routines //management routines
static std::string GetRuleName(IntType t) { return s_RuleInfo[static_cast<int>(t)].name; } static std::string GetRuleName(IntType t) { return s_RuleInfo[static_cast<int>(t)].name; }
static std::string GetRuleName(RealType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount].name; } static std::string GetRuleName(RealType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount].name; }
static std::string GetRuleName(BoolType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount + RealRuleCount].name; } static std::string GetRuleName(BoolType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount + RealRuleCount].name; }
static const std::string &GetRuleNotes(IntType t) { return s_RuleInfo[static_cast<int>(t)].notes; } static std::string GetRuleName(StringType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount + RealRuleCount + StringRuleCount].name; }
static const std::string &GetRuleNotes(RealType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount].notes; } static const std::string& GetRuleNotes(IntType t) { return s_RuleInfo[static_cast<int>(t)].notes; }
static const std::string &GetRuleNotes(BoolType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount + RealRuleCount].notes; } static const std::string& GetRuleNotes(RealType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount].notes; }
static const std::string& GetRuleNotes(BoolType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount + RealRuleCount].notes; }
static const std::string& GetRuleNotes(StringType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount + RealRuleCount + StringRuleCount].notes; }
static uint32 CountRules() { return RulesCount; } static uint32 CountRules() { return RulesCount; }
static CategoryType FindCategory(const std::string &category_name); static CategoryType FindCategory(const std::string& category_name);
bool ListRules(const std::string &category_name, std::vector <std::string> &l); bool ListRules(const std::string& category_name, std::vector<std::string>& l);
bool ListCategories(std::vector <std::string> &l); bool ListCategories(std::vector<std::string>& l);
bool GetRule(const std::string &rule_name, std::string &rule_value); bool GetRule(const std::string& rule_name, std::string& rule_value);
bool SetRule( bool SetRule(
const std::string &rule_name, const std::string& rule_name,
const std::string &rule_value, const std::string& rule_value,
Database *db = nullptr, Database* db = nullptr,
bool db_save = false, bool db_save = false,
bool reload = false bool reload = false
); );
int GetActiveRulesetID() const { return m_activeRuleset; } int GetActiveRulesetID() const { return m_activeRuleset; }
std::string GetActiveRuleset() const { return m_activeName; } std::string GetActiveRuleset() const { return m_activeName; }
static bool ListRulesets(Database *db, std::map<int, std::string> &l); static bool ListRulesets(Database* db, std::map<int, std::string>& l);
void ResetRules(bool reload = false); void ResetRules(bool reload = false);
bool LoadRules(Database *db, const std::string &rule_set_name, bool reload = false); bool LoadRules(Database* db, const std::string& rule_set_name, bool reload = false);
void SaveRules(Database *db, const std::string &rule_set_name); void SaveRules(Database* db, const std::string& rule_set_name);
bool UpdateInjectedRules(Database *db, const std::string &rule_set_name, bool quiet_update = false); bool UpdateInjectedRules(Database* db, const std::string& rule_set_name, bool quiet_update = false);
bool UpdateOrphanedRules(Database *db, bool quiet_update = false); bool UpdateOrphanedRules(Database* db, bool quiet_update = false);
bool RestoreRuleNotes(Database *db); bool RestoreRuleNotes(Database* db);
private: private:
RuleManager(); RuleManager();
@@ -150,18 +168,20 @@ private:
int m_RuleIntValues[IntRuleCount]; int m_RuleIntValues[IntRuleCount];
float m_RuleRealValues[RealRuleCount]; float m_RuleRealValues[RealRuleCount];
uint32 m_RuleBoolValues[BoolRuleCount]; uint32 m_RuleBoolValues[BoolRuleCount];
std::string m_RuleStringValues[StringRuleCount];
typedef enum { typedef enum {
IntRule, IntRule,
RealRule, RealRule,
BoolRule BoolRule,
StringRule
} RuleType; } RuleType;
static bool _FindRule(const std::string &rule_name, RuleType &type_into, uint16 &index_into); static bool _FindRule(const std::string& rule_name, RuleType& type_into, uint16& index_into);
static std::string _GetRuleName(RuleType type, uint16 index); static std::string _GetRuleName(RuleType type, uint16 index);
static const std::string &_GetRuleNotes(RuleType type, uint16 index); static const std::string& _GetRuleNotes(RuleType type, uint16 index);
static int _FindOrCreateRuleset(Database *db, const std::string &rule_set_name); static int _FindOrCreateRuleset(Database* db, const std::string& rule_set_name);
void _SaveRule(Database *db, RuleType type, uint16 index); void _SaveRule(Database* db, RuleType type, uint16 index);
static const char* s_categoryNames[]; static const char* s_categoryNames[];
typedef struct { typedef struct {
+12 -3
View File
@@ -28,6 +28,9 @@
#ifndef RULE_BOOL #ifndef RULE_BOOL
#define RULE_BOOL(cat, rule, default_value, notes) #define RULE_BOOL(cat, rule, default_value, notes)
#endif #endif
#ifndef RULE_STRING
#define RULE_STRING(cat, rule, default_value, notes)
#endif
#ifndef RULE_CATEGORY_END #ifndef RULE_CATEGORY_END
#define RULE_CATEGORY_END() #define RULE_CATEGORY_END()
#endif #endif
@@ -48,10 +51,10 @@ RULE_BOOL(Character, DeathKeepLevel, false, "Players can not drop below 0% exper
RULE_BOOL(Character, UseDeathExpLossMult, false, "Setting to control whether DeathExpLossMultiplier or the code default is used: (Level x Level / 18.0) x 12000") RULE_BOOL(Character, UseDeathExpLossMult, false, "Setting to control whether DeathExpLossMultiplier or the code default is used: (Level x Level / 18.0) x 12000")
RULE_BOOL(Character, UseOldRaceRezEffects, false, "Older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore") RULE_BOOL(Character, UseOldRaceRezEffects, false, "Older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore")
RULE_INT(Character, CorpseDecayTime, 604800000, "Time after which the corpse decays (milliseconds) DEFAULT: 604800000 (7 Days)") RULE_INT(Character, CorpseDecayTime, 604800000, "Time after which the corpse decays (milliseconds) DEFAULT: 604800000 (7 Days)")
RULE_INT( Character, EmptyCorpseDecayTime, 10800000, "Time after which an empty corpse decays (milliseconds) DEFAULT: 10800000 (3 Hours)") RULE_INT(Character, EmptyCorpseDecayTime, 10800000, "Time after which an empty corpse decays (milliseconds) DEFAULT: 10800000 (3 Hours)")
RULE_INT(Character, CorpseResTime, 10800000, "Time after which the corpse can no longer be resurrected (milliseconds) DEFAULT: 10800000 (3 Hours)") RULE_INT(Character, CorpseResTime, 10800000, "Time after which the corpse can no longer be resurrected (milliseconds) DEFAULT: 10800000 (3 Hours)")
RULE_INT( Character, DuelCorpseResTime, 600000, "Time before cant res corpse after a duel (milliseconds) DEFAULT: 600000 (10 Minutes)") RULE_INT(Character, DuelCorpseResTime, 600000, "Time before cant res corpse after a duel (milliseconds) DEFAULT: 600000 (10 Minutes)")
RULE_INT( Character, CorpseOwnerOnlineTime, 30000, "How often corpse will check if its owner is online DEFAULT: 30000 (30 Seconds)") RULE_INT(Character, CorpseOwnerOnlineTime, 30000, "How often corpse will check if its owner is online DEFAULT: 30000 (30 Seconds)")
RULE_BOOL(Character, LeaveCorpses, true, "Setting whether you leave a corpse behind") RULE_BOOL(Character, LeaveCorpses, true, "Setting whether you leave a corpse behind")
RULE_BOOL(Character, LeaveNakedCorpses, false, "Setting whether you leave a corpse without items") RULE_BOOL(Character, LeaveNakedCorpses, false, "Setting whether you leave a corpse without items")
RULE_INT(Character, MaxDraggedCorpses, 2, "Maximum number of corpses you can drag at once") RULE_INT(Character, MaxDraggedCorpses, 2, "Maximum number of corpses you can drag at once")
@@ -324,6 +327,8 @@ RULE_INT(World, MaximumQuestErrors, 30, "Changes the maximum number of quest err
RULE_INT(World, BootHour, 0, "Sets the in-game hour world will set when it first boots. 0-24 are valid options, where 0 disables this rule") RULE_INT(World, BootHour, 0, "Sets the in-game hour world will set when it first boots. 0-24 are valid options, where 0 disables this rule")
RULE_BOOL(World, UseItemLinksForKeyRing, false, "Uses item links for Key Ring Listing instead of item name") RULE_BOOL(World, UseItemLinksForKeyRing, false, "Uses item links for Key Ring Listing instead of item name")
RULE_BOOL(World, UseOldShadowKnightClassExport, true, "Disable to have Shadowknight show as Shadow Knight (live-like)") RULE_BOOL(World, UseOldShadowKnightClassExport, true, "Disable to have Shadowknight show as Shadow Knight (live-like)")
RULE_STRING(World, IPExemptionZones, "", "Comma-delimited list of zones to exclude from IP-limit checks. Empty string to disable.")
RULE_STRING(World, MOTD, "", "Server MOTD sent on login, change from empty to have this be used instead of variables table 'motd' value")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Zone) RULE_CATEGORY(Zone)
@@ -522,6 +527,7 @@ RULE_REAL(Combat, BaseProcChance, 0.035, "Base chance for procs")
RULE_REAL(Combat, ProcDexDivideBy, 11000, "Divisor for the probability of a proc increased by dexterity") RULE_REAL(Combat, ProcDexDivideBy, 11000, "Divisor for the probability of a proc increased by dexterity")
RULE_INT(Combat, MinRangedAttackDist, 25, "Minimum Distance to use Ranged Attacks") RULE_INT(Combat, MinRangedAttackDist, 25, "Minimum Distance to use Ranged Attacks")
RULE_BOOL(Combat, ArcheryBonusRequiresStationary, true, "does the 2x archery bonus chance require a stationary npc") RULE_BOOL(Combat, ArcheryBonusRequiresStationary, true, "does the 2x archery bonus chance require a stationary npc")
RULE_INT(Combat, ArcheryBonusLevelRequirement, 51, "Level requirement when the 2x archery bonus will be enabled. The default is 51.")
RULE_REAL(Combat, ArcheryNPCMultiplier, 1.0, "Value is multiplied by the regular dmg to get the archery dmg") RULE_REAL(Combat, ArcheryNPCMultiplier, 1.0, "Value is multiplied by the regular dmg to get the archery dmg")
RULE_BOOL(Combat, AssistNoTargetSelf, true, "When assisting a target that does not have a target: true = target self, false = leave target as was before assist (false = live like)") RULE_BOOL(Combat, AssistNoTargetSelf, true, "When assisting a target that does not have a target: true = target self, false = leave target as was before assist (false = live like)")
RULE_INT(Combat, MaxRampageTargets, 3, "Maximum number of people hit with rampage") RULE_INT(Combat, MaxRampageTargets, 3, "Maximum number of people hit with rampage")
@@ -570,10 +576,12 @@ RULE_BOOL(Combat, NPCsUseFrontalStunImmunityClasses, false, "Enable or disable N
RULE_INT(Combat, FrontalStunImmunityRaces, 512, "Bitmask for Races than have frontal stun immunity, Ogre (512) only by default.") RULE_INT(Combat, FrontalStunImmunityRaces, 512, "Bitmask for Races than have frontal stun immunity, Ogre (512) only by default.")
RULE_BOOL(Combat, NPCsUseFrontalStunImmunityRaces, true, "Enable or disable NPCs using frontal stun immunity Races from Combat:FrontalStunImmunityRaces, true by default.") RULE_BOOL(Combat, NPCsUseFrontalStunImmunityRaces, true, "Enable or disable NPCs using frontal stun immunity Races from Combat:FrontalStunImmunityRaces, true by default.")
RULE_BOOL(Combat, AssassinateOnlyHumanoids, true, "Enable or disable Assassinate only being allowed on Humanoids, true by default.") RULE_BOOL(Combat, AssassinateOnlyHumanoids, true, "Enable or disable Assassinate only being allowed on Humanoids, true by default.")
RULE_INT(Combat, AssassinateLevelRequirement, 60, "Level requirement to enable assassinate attempts on backstabs. The default is 60.")
RULE_BOOL(Combat, HeadshotOnlyHumanoids, true, "Enable or disable Headshot only being allowed on Humanoids, true by default.") RULE_BOOL(Combat, HeadshotOnlyHumanoids, true, "Enable or disable Headshot only being allowed on Humanoids, true by default.")
RULE_BOOL(Combat, EnableWarriorShielding, true, "Enable or disable Warrior Shielding Ability (/shield), true by default.") RULE_BOOL(Combat, EnableWarriorShielding, true, "Enable or disable Warrior Shielding Ability (/shield), true by default.")
RULE_BOOL(Combat, BackstabIgnoresElemental, false, "Enable or disable Elemental weapon damage affecting backstab damage, false by default.") RULE_BOOL(Combat, BackstabIgnoresElemental, false, "Enable or disable Elemental weapon damage affecting backstab damage, false by default.")
RULE_BOOL(Combat, BackstabIgnoresBane, false, "Enable or disable Bane weapon damage affecting backstab damage, false by default.") RULE_BOOL(Combat, BackstabIgnoresBane, false, "Enable or disable Bane weapon damage affecting backstab damage, false by default.")
RULE_INT(Combat, DoubleBackstabLevelRequirement, 55, "Level requirement to enable double backstab attempts. The default is 55.")
RULE_BOOL(Combat, SummonMeleeRange, true, "Enable or disable summoning of a player when already in melee range of the summoner.") RULE_BOOL(Combat, SummonMeleeRange, true, "Enable or disable summoning of a player when already in melee range of the summoner.")
RULE_BOOL(Combat, WaterMatchRequiredForAutoFireLoS, true, "Enable/Disable the requirement of both the attacker/victim being both in or out of water for AutoFire LoS to pass.") RULE_BOOL(Combat, WaterMatchRequiredForAutoFireLoS, true, "Enable/Disable the requirement of both the attacker/victim being both in or out of water for AutoFire LoS to pass.")
RULE_INT(Combat, ExtraAllowedKickClassesBitmask, 0, "Bitmask for allowing extra classes beyond Warrior, Ranger, Beastlord, and Berserker to kick, No Extra Classes (0) by default") RULE_INT(Combat, ExtraAllowedKickClassesBitmask, 0, "Bitmask for allowing extra classes beyond Warrior, Ranger, Beastlord, and Berserker to kick, No Extra Classes (0) by default")
@@ -951,4 +959,5 @@ RULE_CATEGORY_END()
#undef RULE_INT #undef RULE_INT
#undef RULE_REAL #undef RULE_REAL
#undef RULE_BOOL #undef RULE_BOOL
#undef RULE_STRING
#undef RULE_CATEGORY_END #undef RULE_CATEGORY_END
+2 -2
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.45.0-dev" // always append -dev to the current version for custom-builds #define CURRENT_VERSION "22.47.0-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,7 +42,7 @@
* 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 9264 #define CURRENT_BINARY_DATABASE_VERSION 9266
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9043 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9043
#endif #endif
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "eqemu-server", "name": "eqemu-server",
"version": "22.45.0", "version": "22.47.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/EQEmu/Server.git" "url": "https://github.com/EQEmu/Server.git"
+70 -68
View File
@@ -52,6 +52,7 @@
#include "../common/repositories/player_event_logs_repository.h" #include "../common/repositories/player_event_logs_repository.h"
#include "../common/repositories/inventory_repository.h" #include "../common/repositories/inventory_repository.h"
#include "../common/events/player_event_logs.h" #include "../common/events/player_event_logs.h"
#include "../common/content/world_content_service.h"
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
@@ -599,79 +600,63 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app)
} }
bool Client::HandleGenerateRandomNamePacket(const EQApplicationPacket *app) { bool Client::HandleGenerateRandomNamePacket(const EQApplicationPacket *app) {
// creates up to a 10 char name char newName[17] = {0};
char vowels[18]="aeiouyaeiouaeioe"; bool unique = false;
char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd";
char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; while (!unique) {
char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr"; std::string cons = "bcdfghjklmnpqrstvwxyz";
int rndnum=emu_random.Int(0, 75),n=1; std::string vows = "aeou";
bool dlc=false; std::string allVows = "aeiou";
bool vwl=false; std::vector<std::string> endPhon = {"a", "e", "i", "o", "u", "os", "as", "us", "is", "y", "an", "en", "in", "on", "un"};
bool dbl=false;
if (rndnum>63) std::random_device rd;
{ // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel std::mt19937 gen(rd());
rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk"
rndname[0]=paircons[rndnum]; std::uniform_int_distribution<int> lenDist(5, 10);
rndname[1]=paircons[rndnum+1]; std::uniform_int_distribution<int> firstCharDist(0, 1);
n=2; std::uniform_int_distribution<int> consDist(0, cons.size() - 1);
std::uniform_int_distribution<int> vowDist(0, vows.size() - 1);
std::uniform_int_distribution<int> allVowDist(0, allVows.size() - 1);
std::uniform_int_distribution<int> endPhonDist(0, endPhon.size() - 1);
int len = 0;
memset(newName, 0, sizeof(newName));
if (firstCharDist(gen) == 0) {
newName[len++] = vows[vowDist(gen)];
newName[len++] = cons[consDist(gen)];
} else {
newName[len++] = cons[consDist(gen)];
newName[len++] = allVows[allVowDist(gen)];
} }
else if (rndnum>16)
{ newName[0] = toupper(newName[0]);
rndnum-=17;
rndname[0]=cons[rndnum]; while (len < lenDist(gen) - 1) {
} if (len % 2 == 0) {
else newName[len++] = cons[consDist(gen)];
{ } else {
rndname[0]=vowels[rndnum]; newName[len++] = allVows[allVowDist(gen)];
vwl=true;
}
int namlen=emu_random.Int(5, 10);
for (int i=n;i<namlen;i++)
{
dlc=false;
if (vwl) //last char was a vowel
{ // so pick a cons or cons pair
rndnum=emu_random.Int(0, 62);
if (rndnum>46)
{ // pick a cons pair
if (i>namlen-3) // last 2 chars in name?
{ // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng"
rndnum=emu_random.Int(0, 7)*2;
}
else
{ // pick any from the set
rndnum=(rndnum-47)*2;
}
rndname[i]=paircons[rndnum];
rndname[i+1]=paircons[rndnum+1];
dlc=true; // flag keeps second letter from being doubled below
i+=1;
}
else
{ // select a single cons
rndname[i]=cons[rndnum];
} }
} }
else
{ // select a vowel std::string end = endPhon[endPhonDist(gen)];
rndname[i]=vowels[emu_random.Int(0, 16)]; for (char c : end) {
if (len < 10) newName[len++] = c;
} }
vwl=!vwl;
if (!dbl && !dlc) if (database.CheckNameFilter(newName)) {
{ // one chance at double letters in name std::string query = StringFormat("SELECT `name` FROM `character_data` WHERE `name` = '%s'", newName);
if (!emu_random.Int(0, i+9)) // chances decrease towards end of name auto res = database.QueryDatabase(query);
{ if (res.Success() && res.RowCount() == 0) {
rndname[i+1]=rndname[i]; unique = true;
dbl=true;
i+=1;
} }
} }
} }
rndname[0]=toupper(rndname[0]);
NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer;
memset(ngs->name,0,64); memset(ngs->name, 0, 64);
strcpy(ngs->name,rndname); strcpy(ngs->name, newName);
QueuePacket(app); QueuePacket(app);
return true; return true;
@@ -787,6 +772,18 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
return true; return true;
} }
auto r = content_service.FindZone(zone_id, instance_id);
if (r.zone_id && r.instance.id != instance_id) {
LogInfo(
"Zone [{}] has been remapped to instance_id [{}] from instance_id [{}] for client [{}]",
r.zone.short_name,
r.instance.id,
instance_id,
char_name
);
instance_id = r.instance.id;
}
// Make sure this account owns this character // Make sure this account owns this character
if (temporary_account_id != account_id) { if (temporary_account_id != account_id) {
LogInfo("Account [{}] does not own the character named [{}] from account [{}]", account_id, char_name, temporary_account_id); LogInfo("Account [{}] does not own the character named [{}] from account [{}]", account_id, char_name, temporary_account_id);
@@ -903,12 +900,17 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
} }
auto outapp = new EQApplicationPacket(OP_MOTD); auto outapp = new EQApplicationPacket(OP_MOTD);
std::string motd_message; std::string motd = RuleS(World, MOTD);
if (database.GetVariable("MOTD", motd_message)) { if (!motd.empty()) {
outapp->size = motd_message.length() + 1; outapp->size = motd.length() + 1;
outapp->pBuffer = new uchar[outapp->size]; outapp->pBuffer = new uchar[outapp->size];
memset(outapp->pBuffer, 0, outapp->size); memset(outapp->pBuffer, 0, outapp->size);
strcpy((char*)outapp->pBuffer, motd_message.c_str()); strcpy((char*) outapp->pBuffer, motd.c_str());
} else if (database.GetVariable("MOTD", motd)) {
outapp->size = motd.length() + 1;
outapp->pBuffer = new uchar[outapp->size];
memset(outapp->pBuffer, 0, outapp->size);
strcpy((char*) outapp->pBuffer, motd.c_str());
} else { // Null Message of the Day. :) } else { // Null Message of the Day. :)
outapp->size = 1; outapp->size = 1;
outapp->pBuffer = new uchar[outapp->size]; outapp->pBuffer = new uchar[outapp->size];
+9
View File
@@ -96,6 +96,15 @@ void ClientList::GetCLEIP(uint32 in_ip) {
while (iterator.MoreElements()) { while (iterator.MoreElements()) {
cle = iterator.GetData(); cle = iterator.GetData();
const auto zones = Strings::Split(RuleS(World, IPExemptionZones), ",");
for (const auto &z : zones) {
if (Strings::ToUnsignedInt(z) == cle->zone()) {
iterator.Advance();
continue;
}
}
if ( if (
cle->GetIP() == in_ip && cle->GetIP() == in_ip &&
( (
+1
View File
@@ -142,6 +142,7 @@ std::vector<Reload> reload_types = {
Reload{.command = "base_data", .opcode = ServerOP_ReloadBaseData, .desc = "Base Data"}, Reload{.command = "base_data", .opcode = ServerOP_ReloadBaseData, .desc = "Base Data"},
Reload{.command = "blocked_spells", .opcode = ServerOP_ReloadBlockedSpells, .desc = "Blocked Spells"}, Reload{.command = "blocked_spells", .opcode = ServerOP_ReloadBlockedSpells, .desc = "Blocked Spells"},
Reload{.command = "commands", .opcode = ServerOP_ReloadCommands, .desc = "Commands"}, Reload{.command = "commands", .opcode = ServerOP_ReloadCommands, .desc = "Commands"},
Reload{.command = "content_flags", .opcode = ServerOP_ReloadContentFlags, .desc = "Content Flags"},
Reload{.command = "data_buckets_cache", .opcode = ServerOP_ReloadDataBucketsCache, .desc = "Data Buckets Cache"}, Reload{.command = "data_buckets_cache", .opcode = ServerOP_ReloadDataBucketsCache, .desc = "Data Buckets Cache"},
Reload{.command = "doors", .opcode = ServerOP_ReloadDoors, .desc = "Doors"}, Reload{.command = "doors", .opcode = ServerOP_ReloadDoors, .desc = "Doors"},
Reload{.command = "dztemplates", .opcode = ServerOP_ReloadDzTemplates, .desc = "Dynamic Zone Templates"}, Reload{.command = "dztemplates", .opcode = ServerOP_ReloadDzTemplates, .desc = "Dynamic Zone Templates"},
+5
View File
@@ -188,6 +188,11 @@ int main(int argc, char **argv)
RegisterConsoleFunctions(console); RegisterConsoleFunctions(console);
} }
content_service.SetDatabase(&database)
->SetContentDatabase(&content_db)
->SetExpansionContext()
->ReloadContentFlags();
std::unique_ptr<EQ::Net::ServertalkServer> server_connection; std::unique_ptr<EQ::Net::ServertalkServer> server_connection;
server_connection = std::make_unique<EQ::Net::ServertalkServer>(); server_connection = std::make_unique<EQ::Net::ServertalkServer>();
+11 -1
View File
@@ -230,13 +230,23 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
case ServerOP_GuildChannel: case ServerOP_GuildChannel:
case ServerOP_GuildURL: case ServerOP_GuildURL:
case ServerOP_GuildMemberRemove: case ServerOP_GuildMemberRemove:
case ServerOP_GuildMemberAdd:
case ServerOP_GuildSendGuildList: case ServerOP_GuildSendGuildList:
case ServerOP_GuildMembersList: case ServerOP_GuildMembersList:
{ {
zoneserver_list.SendPacketToBootedZones(pack); zoneserver_list.SendPacketToBootedZones(pack);
break; break;
} }
case ServerOP_GuildMemberAdd:
{
auto in = (ServerOP_GuildMessage_Struct *)pack->pBuffer;
auto guild = GetGuildByGuildID(in->guild_id);
if (!guild) {
BaseGuildManager::RefreshGuild(in->guild_id);
}
zoneserver_list.SendPacketToBootedZones(pack);
break;
}
default: default:
LogGuilds("Unknown packet {:#04x} received from zone??", pack->opcode); LogGuilds("Unknown packet {:#04x} received from zone??", pack->opcode);
break; break;
+1 -1
View File
@@ -1100,7 +1100,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
} }
auto smotd = (ServerMotd_Struct*) pack->pBuffer; auto smotd = (ServerMotd_Struct*) pack->pBuffer;
database.SetVariable("MOTD", smotd->motd); RuleManager::Instance()->SetRule("MOTD", smotd->motd, &database, true, true);
zoneserver_list.SendPacket(pack); zoneserver_list.SendPacket(pack);
break; break;
} }
+25 -15
View File
@@ -621,28 +621,38 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
// NPC *npc1, *npc2; // NPC *npc1, *npc2;
int reverse; int reverse;
if(!zone->CanDoCombat()) if (!zone->CanDoCombat()) {
return false;
// some special cases
if(!target)
return false;
if(this == target) // you can attack yourself
return true;
if(target->GetSpecialAbility(NO_HARM_FROM_CLIENT)){
return false; return false;
} }
if (target->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT) && IsClient()) // some special cases
if (!target) {
return false; return false;
}
if (target->GetSpecialAbility(IMMUNE_DAMAGE_NPC) && IsNPC()) if (this == target) { // you can attack yourself
return false; return true;
}
if (target->IsHorse()) if (target->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
return false; return false;
}
if (IsBot() && target->GetSpecialAbility(IMMUNE_DAMAGE_BOT)) {
return false;
}
if (IsClient() && target->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT)) {
return false;
}
if (IsNPC() && target->GetSpecialAbility(IMMUNE_DAMAGE_NPC)) {
return false;
}
if (target->IsHorse()) {
return false;
}
// can't damage own pet (applies to everthing) // can't damage own pet (applies to everthing)
Mob *target_owner = target->GetOwner(); Mob *target_owner = target->GetOwner();
+32 -17
View File
@@ -1005,7 +1005,7 @@ double Mob::RollD20(int offense, int mitigation)
auto atk_roll = zone->random.Roll0(offense + 5); auto atk_roll = zone->random.Roll0(offense + 5);
auto def_roll = zone->random.Roll0(mitigation + 5); auto def_roll = zone->random.Roll0(mitigation + 5);
int avg = (offense + mitigation + 10) / 2; int avg = std::max(1, (offense + mitigation + 10) / 2);
int index = std::max(0, (atk_roll - def_roll) + (avg / 2)); int index = std::max(0, (atk_roll - def_roll) + (avg / 2));
index = EQ::Clamp((index * 20) / avg, 0, 19); index = EQ::Clamp((index * 20) / avg, 0, 19);
@@ -3091,26 +3091,37 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
TryTriggerOnCastRequirement(); TryTriggerOnCastRequirement();
} }
if (IsClient() && !IsAIControlled()) if (IsClient() && !IsAIControlled()) {
return; return;
}
if (IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO)) if (IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO)) {
return; return;
}
if (GetSpecialAbility(IMMUNE_AGGRO_NPC) && other->IsNPC()) if (other->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) {
return; return;
}
if (GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && other->IsClient()) if (other->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) {
return; return;
}
if (IsValidSpell(spell_id) && IsNoDetrimentalSpellAggroSpell(spell_id)) if (other->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC)) {
return; return;
}
if (other == myowner) if (IsValidSpell(spell_id) && IsNoDetrimentalSpellAggroSpell(spell_id)) {
return; return;
}
if (other->GetSpecialAbility(IMMUNE_AGGRO_ON)) if (other == myowner) {
return; return;
}
if (other->GetSpecialAbility(IMMUNE_AGGRO_ON)) {
return;
}
if (GetSpecialAbility(NPC_TUNNELVISION)) { if (GetSpecialAbility(NPC_TUNNELVISION)) {
int tv_mod = GetSpecialAbilityParam(NPC_TUNNELVISION, 0); int tv_mod = GetSpecialAbilityParam(NPC_TUNNELVISION, 0);
@@ -3194,8 +3205,9 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
// owner must get on list, but he's not actually gained any hate yet // owner must get on list, but he's not actually gained any hate yet
if ( if (
!owner->GetSpecialAbility(IMMUNE_AGGRO) && !owner->GetSpecialAbility(IMMUNE_AGGRO) &&
!(GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && owner->IsClient()) && !(owner->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(GetSpecialAbility(IMMUNE_AGGRO_NPC) && owner->IsNPC()) !(owner->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(owner->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC))
) { ) {
if (owner->IsClient() && !CheckAggro(owner)) { if (owner->IsClient() && !CheckAggro(owner)) {
owner->CastToClient()->AddAutoXTarget(this); owner->CastToClient()->AddAutoXTarget(this);
@@ -3209,8 +3221,9 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
if ( if (
!mypet->IsFamiliar() && !mypet->IsFamiliar() &&
!mypet->GetSpecialAbility(IMMUNE_AGGRO) && !mypet->GetSpecialAbility(IMMUNE_AGGRO) &&
!(mypet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && IsClient()) && !(IsBot() && mypet->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(mypet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && IsNPC()) !(IsClient() && mypet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(IsNPC() && mypet->GetSpecialAbility(IMMUNE_AGGRO_NPC))
) { ) {
mypet->hate_list.AddEntToHateList(other, 0, 0, bFrenzy); mypet->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
} }
@@ -3219,8 +3232,9 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
if ( if (
myowner->IsAIControlled() && myowner->IsAIControlled() &&
!myowner->GetSpecialAbility(IMMUNE_AGGRO) && !myowner->GetSpecialAbility(IMMUNE_AGGRO) &&
!(GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && myowner->IsClient()) && !(myowner->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(GetSpecialAbility(IMMUNE_AGGRO_NPC) && myowner->IsNPC()) !(myowner->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(myowner->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC))
) { ) {
myowner->hate_list.AddEntToHateList(other, 0, 0, bFrenzy); myowner->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
} }
@@ -4060,8 +4074,9 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
!pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->GetSpecialAbility(IMMUNE_AGGRO) &&
!pet->IsEngaged() && !pet->IsEngaged() &&
attacker && attacker &&
!(pet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) && !(attacker->IsBot() && pet->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(pet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) && !(attacker->IsClient() && pet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(attacker->IsNPC() && pet->GetSpecialAbility(IMMUNE_AGGRO_NPC)) &&
attacker != this && attacker != this &&
!attacker->IsCorpse() && !attacker->IsCorpse() &&
!pet->IsGHeld() && !pet->IsGHeld() &&
@@ -6194,7 +6209,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
if (headshot > 0) { if (headshot > 0) {
hit.damage_done = headshot; hit.damage_done = headshot;
} }
else if (GetClass() == Class::Ranger && GetLevel() > 50) { // no double dmg on headshot else if (GetClass() == Class::Ranger && GetLevel() >= RuleI(Combat, ArcheryBonusLevelRequirement)) { // no double dmg on headshot
if ((defender->IsNPC() && !defender->IsMoving() && !defender->IsRooted()) || !RuleB(Combat, ArcheryBonusRequiresStationary)) { if ((defender->IsNPC() && !defender->IsMoving() && !defender->IsRooted()) || !RuleB(Combat, ArcheryBonusRequiresStationary)) {
hit.damage_done *= 2; hit.damage_done *= 2;
MessageString(Chat::MeleeCrit, BOW_DOUBLE_DAMAGE); MessageString(Chat::MeleeCrit, BOW_DOUBLE_DAMAGE);
+1 -1
View File
@@ -2367,7 +2367,7 @@ const uint8 BotDatabase::GetBotLevelByID(const uint32 bot_id)
return e.bot_id ? e.level : 0; return e.bot_id ? e.level : 0;
} }
const std::string& BotDatabase::GetBotNameByID(const uint32 bot_id) const std::string BotDatabase::GetBotNameByID(const uint32 bot_id)
{ {
const auto& e = BotDataRepository::FindOne(database, bot_id); const auto& e = BotDataRepository::FindOne(database, bot_id);
+1 -1
View File
@@ -164,7 +164,7 @@ public:
const uint8 GetBotGenderByID(const uint32 bot_id); const uint8 GetBotGenderByID(const uint32 bot_id);
std::vector<uint32> GetBotIDsByCharacterID(const uint32 character_id, uint8 class_id = Class::None); std::vector<uint32> GetBotIDsByCharacterID(const uint32 character_id, uint8 class_id = Class::None);
const uint8 GetBotLevelByID(const uint32 bot_id); const uint8 GetBotLevelByID(const uint32 bot_id);
const std::string& GetBotNameByID(const uint32 bot_id); const std::string GetBotNameByID(const uint32 bot_id);
const uint16 GetBotRaceByID(const uint32 bot_id); const uint16 GetBotRaceByID(const uint32 bot_id);
class fail { class fail {
+40 -9
View File
@@ -284,6 +284,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
PendingSacrifice = false; PendingSacrifice = false;
controlling_boat_id = 0; controlling_boat_id = 0;
controlled_mob_id = 0; controlled_mob_id = 0;
qGlobals = nullptr;
if (!RuleB(Character, PerCharacterQglobalMaxLevel) && !RuleB(Character, PerCharacterBucketMaxLevel)) { if (!RuleB(Character, PerCharacterQglobalMaxLevel) && !RuleB(Character, PerCharacterBucketMaxLevel)) {
SetClientMaxLevel(0); SetClientMaxLevel(0);
@@ -311,7 +312,6 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
aa_los_them_mob = nullptr; aa_los_them_mob = nullptr;
los_status = false; los_status = false;
los_status_facing = false; los_status_facing = false;
qGlobals = nullptr;
HideCorpseMode = HideCorpseNone; HideCorpseMode = HideCorpseNone;
PendingGuildInvitation = false; PendingGuildInvitation = false;
@@ -6285,7 +6285,17 @@ void Client::SendZonePoints()
zp->zpe[i].z = data->target_z; zp->zpe[i].z = data->target_z;
zp->zpe[i].heading = data->target_heading; zp->zpe[i].heading = data->target_heading;
zp->zpe[i].zoneid = data->target_zone_id; zp->zpe[i].zoneid = data->target_zone_id;
zp->zpe[i].zoneinstance = data->target_zone_instance;
// if the target zone is the same as the current zone, use the instance of the current zone
// if we don't use the same instance_id that the client was sent, the client will forcefully
// issue a zone change request when they should be simply moving to a different point in the same zone
// because the client will think the zone point target is different from the current instance
auto target_instance = data->target_zone_instance;
if (data->target_zone_id == zone->GetZoneID() && data->target_zone_instance == 0) {
target_instance = zone->GetInstanceID();
}
zp->zpe[i].zoneinstance = target_instance;
i++; i++;
} }
iterator.Advance(); iterator.Advance();
@@ -6765,23 +6775,36 @@ void Client::UpdateClientXTarget(Client *c)
// IT IS NOT SAFE TO CALL THIS IF IT'S NOT INITIAL AGGRO // IT IS NOT SAFE TO CALL THIS IF IT'S NOT INITIAL AGGRO
void Client::AddAutoXTarget(Mob *m, bool send) void Client::AddAutoXTarget(Mob *m, bool send)
{ {
if (m->IsBot() || (m->IsPet() && m->IsPetOwnerBot())) {
return;
}
m_activeautohatermgr->increment_count(m); m_activeautohatermgr->increment_count(m);
if (!XTargettingAvailable() || !XTargetAutoAddHaters || IsXTarget(m)) if (!XTargettingAvailable() || !XTargetAutoAddHaters || IsXTarget(m)) {
return; return;
}
for(int i = 0; i < GetMaxXTargets(); ++i) for (int i = 0; i < GetMaxXTargets(); ++i) {
{ if (XTargets[i].Type == Auto && XTargets[i].ID == 0) {
if((XTargets[i].Type == Auto) && (XTargets[i].ID == 0))
{
XTargets[i].ID = m->GetID(); XTargets[i].ID = m->GetID();
if (send) // if we don't send we're bulk sending updates later on
if (send) { // if we don't send we're bulk sending updates later on
SendXTargetPacket(i, m); SendXTargetPacket(i, m);
else } else {
XTargets[i].dirty = true; XTargets[i].dirty = true;
}
break; break;
} }
} }
LogXTargets(
"Adding [{}] to [{}] ({}) XTargets",
m->GetCleanName(),
GetCleanName(),
GetID()
);
} }
void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots) void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots)
@@ -6795,10 +6818,18 @@ void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots)
XTargets[i].dirty = true; XTargets[i].dirty = true;
} }
} }
auto r = GetRaid(); auto r = GetRaid();
if (r) { if (r) {
r->UpdateRaidXTargets(); r->UpdateRaidXTargets();
} }
LogXTargets(
"Removing [{}] from [{}] ({}) XTargets",
m->GetCleanName(),
GetCleanName(),
GetID()
);
} }
void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name) void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name)
+1 -1
View File
@@ -1090,7 +1090,7 @@ public:
void SetPEQZoneFlag(uint32 zone_id); void SetPEQZoneFlag(uint32 zone_id);
bool CanFish(); bool CanFish();
void GoFish(); void GoFish(bool guarantee = false, bool use_bait = true);
void ForageItem(bool guarantee = false); void ForageItem(bool guarantee = false);
//Calculate vendor price modifier based on CHA: (reverse==selling) //Calculate vendor price modifier based on CHA: (reverse==selling)
float CalcPriceMod(Mob* other = 0, bool reverse = false); float CalcPriceMod(Mob* other = 0, bool reverse = false);
+4
View File
@@ -131,8 +131,10 @@ int command_init(void)
command_add("feature", "Change your or your target's feature's temporarily", AccountStatus::QuestTroupe, command_feature) || command_add("feature", "Change your or your target's feature's temporarily", AccountStatus::QuestTroupe, command_feature) ||
command_add("size", "Change your targets size (alias of #feature size)", AccountStatus::QuestTroupe, command_feature) || command_add("size", "Change your targets size (alias of #feature size)", AccountStatus::QuestTroupe, command_feature) ||
command_add("find", "Search command used to find various things", AccountStatus::Guide, command_find) || command_add("find", "Search command used to find various things", AccountStatus::Guide, command_find) ||
command_add("fish", "Fish for an item", AccountStatus::QuestTroupe, command_fish) ||
command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", AccountStatus::QuestTroupe, command_fixmob) || command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", AccountStatus::QuestTroupe, command_fixmob) ||
command_add("flagedit", "Edit zone flags on your target. Use #flagedit help for more info.", AccountStatus::GMAdmin, command_flagedit) || command_add("flagedit", "Edit zone flags on your target. Use #flagedit help for more info.", AccountStatus::GMAdmin, command_flagedit) ||
command_add("forage", "Forage an item", AccountStatus::QuestTroupe, command_forage) ||
command_add("gearup", "Developer tool to quickly equip yourself or your target", AccountStatus::GMMgmt, command_gearup) || command_add("gearup", "Developer tool to quickly equip yourself or your target", AccountStatus::GMMgmt, command_gearup) ||
command_add("giveitem", "[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.", AccountStatus::GMMgmt, command_giveitem) || command_add("giveitem", "[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.", AccountStatus::GMMgmt, command_giveitem) ||
command_add("givemoney", "[Platinum] [Gold] [Silver] [Copper] - Gives specified amount of money to you or your player target", AccountStatus::GMMgmt, command_givemoney) || command_add("givemoney", "[Platinum] [Gold] [Silver] [Copper] - Gives specified amount of money to you or your player target", AccountStatus::GMMgmt, command_givemoney) ||
@@ -822,8 +824,10 @@ void command_bot(Client *c, const Seperator *sep)
#include "gm_commands/faction.cpp" #include "gm_commands/faction.cpp"
#include "gm_commands/feature.cpp" #include "gm_commands/feature.cpp"
#include "gm_commands/find.cpp" #include "gm_commands/find.cpp"
#include "gm_commands/fish.cpp"
#include "gm_commands/fixmob.cpp" #include "gm_commands/fixmob.cpp"
#include "gm_commands/flagedit.cpp" #include "gm_commands/flagedit.cpp"
#include "gm_commands/forage.cpp"
#include "gm_commands/gearup.cpp" #include "gm_commands/gearup.cpp"
#include "gm_commands/giveitem.cpp" #include "gm_commands/giveitem.cpp"
#include "gm_commands/givemoney.cpp" #include "gm_commands/givemoney.cpp"
+2
View File
@@ -83,8 +83,10 @@ void command_faction(Client *c, const Seperator *sep);
void command_faction_association(Client *c, const Seperator *sep); void command_faction_association(Client *c, const Seperator *sep);
void command_feature(Client *c, const Seperator *sep); void command_feature(Client *c, const Seperator *sep);
void command_find(Client *c, const Seperator *sep); void command_find(Client *c, const Seperator *sep);
void command_fish(Client* c, const Seperator* sep);
void command_fixmob(Client *c, const Seperator *sep); void command_fixmob(Client *c, const Seperator *sep);
void command_flagedit(Client *c, const Seperator *sep); void command_flagedit(Client *c, const Seperator *sep);
void command_forage(Client* c, const Seperator* sep);
void command_gearup(Client *c, const Seperator *sep); void command_gearup(Client *c, const Seperator *sep);
void command_giveitem(Client *c, const Seperator *sep); void command_giveitem(Client *c, const Seperator *sep);
void command_givemoney(Client *c, const Seperator *sep); void command_givemoney(Client *c, const Seperator *sep);
+9
View File
@@ -73,6 +73,15 @@ Doors::Doors(const DoorsRepository::Doors &door) :
m_door_param = door.door_param; m_door_param = door.door_param;
m_size = door.size; m_size = door.size;
m_invert_state = door.invert_state; m_invert_state = door.invert_state;
// if the target zone is the same as the current zone, use the instance of the current zone
// if we don't use the same instance_id that the client was sent, the client will forcefully
// issue a zone change request when they should be simply moving to a different point in the same zone
// because the client will think the zone point target is different from the current instance
if (door.dest_zone == zone->GetShortName() && m_destination_instance_id == 0) {
m_destination_instance_id = zone->GetInstanceID();
}
m_destination_instance_id = door.dest_instance; m_destination_instance_id = door.dest_instance;
m_is_ldon_door = door.is_ldon_door; m_is_ldon_door = door.is_ldon_door;
m_dz_switch_id = door.dz_switch_id; m_dz_switch_id = door.dz_switch_id;
+6 -4
View File
@@ -4379,8 +4379,9 @@ void EntityList::AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy)
if (n->GetSwarmInfo()->owner_id == owner->GetID()) { if (n->GetSwarmInfo()->owner_id == owner->GetID()) {
if ( if (
!n->GetSpecialAbility(IMMUNE_AGGRO) && !n->GetSpecialAbility(IMMUNE_AGGRO) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && other->IsClient()) && !(other->IsBot() && n->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_NPC) && other->IsNPC()) !(other->IsClient() && n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(other->IsNPC() && n->GetSpecialAbility(IMMUNE_AGGRO_NPC))
) { ) {
n->hate_list.AddEntToHateList(other, 0, 0, bFrenzy); n->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
} }
@@ -4405,8 +4406,9 @@ void EntityList::AddTempPetsToHateListOnOwnerDamage(Mob *owner, Mob* attacker, i
attacker != n && attacker != n &&
!n->IsEngaged() && !n->IsEngaged() &&
!n->GetSpecialAbility(IMMUNE_AGGRO) && !n->GetSpecialAbility(IMMUNE_AGGRO) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) && !(attacker->IsBot() && n->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
!(n->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) && !(attacker->IsClient() && n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
!(attacker->IsNPC() && n->GetSpecialAbility(IMMUNE_AGGRO_NPC)) &&
!attacker->IsTrap() && !attacker->IsTrap() &&
!attacker->IsCorpse() !attacker->IsCorpse()
) { ) {
+5 -3
View File
@@ -89,7 +89,7 @@ uint32 ZoneDatabase::LoadForage(uint32 zone_id, uint8 skill_level)
} }
forage_items[count] = e.Itemid; forage_items[count] = e.Itemid;
forage_chances[count] = e.chance; forage_chances[count] = e.chance + current_chance;
current_chance = forage_chances[count]; current_chance = forage_chances[count];
@@ -257,7 +257,7 @@ bool Client::CanFish() {
return true; return true;
} }
void Client::GoFish() void Client::GoFish(bool guarantee, bool use_bait)
{ {
//TODO: generate a message if we're already fishing //TODO: generate a message if we're already fishing
@@ -306,7 +306,7 @@ void Client::GoFish()
fishing_skill = 100+((fishing_skill-100)/2); fishing_skill = 100+((fishing_skill-100)/2);
} }
if (zone->random.Int(0,175) < fishing_skill) { if (guarantee || zone->random.Int(0,175) < fishing_skill) {
uint32 food_id = 0; uint32 food_id = 0;
//25% chance to fish an item. //25% chance to fish an item.
@@ -343,8 +343,10 @@ void Client::GoFish()
} }
} }
if (use_bait) {
//consume bait, should we always consume bait on success? //consume bait, should we always consume bait on success?
DeleteItemInInventory(bslot, 1, true); //do we need client update? DeleteItemInInventory(bslot, 1, true); //do we need client update?
}
if(food_id == 0) { if(food_id == 0) {
int index = zone->random.Int(0, MAX_COMMON_FISH_IDS-1); int index = zone->random.Int(0, MAX_COMMON_FISH_IDS-1);
+6
View File
@@ -0,0 +1,6 @@
#include "../client.h"
void command_fish(Client *c, const Seperator *sep)
{
c->GoFish(true, false);
}
+6
View File
@@ -0,0 +1,6 @@
#include "../client.h"
void command_forage(Client *c, const Seperator *sep)
{
c->ForageItem(true);
}
+2 -1
View File
@@ -3,6 +3,7 @@
void command_goto(Client *c, const Seperator *sep) void command_goto(Client *c, const Seperator *sep)
{ {
std::string arg1 = sep->arg[1]; std::string arg1 = sep->arg[1];
std::string arg4 = sep->arg[4];
bool goto_via_target_no_args = sep->arg[1][0] == '\0' && c->GetTarget(); bool goto_via_target_no_args = sep->arg[1][0] == '\0' && c->GetTarget();
bool goto_via_player_name = !sep->IsNumber(1) && !arg1.empty(); bool goto_via_player_name = !sep->IsNumber(1) && !arg1.empty();
@@ -52,7 +53,7 @@ void command_goto(Client *c, const Seperator *sep)
Strings::ToFloat(sep->arg[1]), Strings::ToFloat(sep->arg[1]),
Strings::ToFloat(sep->arg[2]), Strings::ToFloat(sep->arg[2]),
Strings::ToFloat(sep->arg[3]), Strings::ToFloat(sep->arg[3]),
(sep->arg[4] ? Strings::ToFloat(sep->arg[4]) : c->GetHeading()) (!arg4.empty() ? Strings::ToFloat(sep->arg[4]) : c->GetHeading())
); );
} }
else { else {
+18 -1
View File
@@ -5653,6 +5653,10 @@ bool get_ruleb(int rule) {
return RuleManager::Instance()->GetBoolRule((RuleManager::BoolType)rule); return RuleManager::Instance()->GetBoolRule((RuleManager::BoolType)rule);
} }
std::string get_rules(int rule) {
return RuleManager::Instance()->GetStringRule((RuleManager::StringType)rule);
}
luabind::scope lua_register_general() { luabind::scope lua_register_general() {
return luabind::namespace_("eq") return luabind::namespace_("eq")
[( [(
@@ -7244,7 +7248,13 @@ luabind::scope lua_register_rules_const() {
#define RULE_BOOL(cat, rule, default_value, notes) \ #define RULE_BOOL(cat, rule, default_value, notes) \
luabind::value(#rule, RuleManager::Bool__##rule), luabind::value(#rule, RuleManager::Bool__##rule),
#include "../common/ruletypes.h" #include "../common/ruletypes.h"
luabind::value("_BoolRuleCount", RuleManager::_BoolRuleCount) luabind::value("_BoolRuleCount", RuleManager::_BoolRuleCount),
#undef RULE_BOOL
#define RULE_STRING(cat, rule, default_value, notes) \
luabind::value(#rule, RuleManager::String__##rule),
#include "../common/ruletypes.h"
luabind::value("_StringRuleCount", RuleManager::_StringRuleCount)
#undef RULE_STRING
)]; )];
} }
@@ -7269,6 +7279,13 @@ luabind::scope lua_register_ruleb() {
]; ];
} }
luabind::scope lua_register_rules() {
return luabind::namespace_("RuleS")
[
luabind::def("Get", &get_rules)
];
}
luabind::scope lua_register_journal_speakmode() { luabind::scope lua_register_journal_speakmode() {
return luabind::class_<Journal_SpeakMode>("SpeakMode") return luabind::class_<Journal_SpeakMode>("SpeakMode")
.enum_("constants") .enum_("constants")
+38 -1
View File
@@ -3267,6 +3267,36 @@ bool Lua_Mob::IsPetOwnerNPC()
return self->IsPetOwnerNPC(); return self->IsPetOwnerNPC();
} }
bool Lua_Mob::IsDestructibleObject()
{
Lua_Safe_Call_Bool();
return self->IsDestructibleObject();
}
bool Lua_Mob::IsBoat()
{
Lua_Safe_Call_Bool();
return self->IsBoat();
}
bool Lua_Mob::IsControllableBoat()
{
Lua_Safe_Call_Bool();
return self->IsControllableBoat();
}
int Lua_Mob::GetHeroicStrikethrough()
{
Lua_Safe_Call_Int();
return self->GetHeroicStrikethrough();
}
bool Lua_Mob::IsAlwaysAggro()
{
Lua_Safe_Call_Bool();
return self->AlwaysAggro();
}
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<>())
@@ -3546,6 +3576,7 @@ luabind::scope lua_register_mob() {
.def("GetHateTopNPC", (Lua_NPC(Lua_Mob::*)(void))&Lua_Mob::GetHateTopNPC) .def("GetHateTopNPC", (Lua_NPC(Lua_Mob::*)(void))&Lua_Mob::GetHateTopNPC)
.def("GetHeading", &Lua_Mob::GetHeading) .def("GetHeading", &Lua_Mob::GetHeading)
.def("GetHelmTexture", &Lua_Mob::GetHelmTexture) .def("GetHelmTexture", &Lua_Mob::GetHelmTexture)
.def("GetHeroicStrikethrough", &Lua_Mob::GetHeroicStrikethrough)
.def("GetHerosForgeModel", (int32(Lua_Mob::*)(uint8))&Lua_Mob::GetHerosForgeModel) .def("GetHerosForgeModel", (int32(Lua_Mob::*)(uint8))&Lua_Mob::GetHerosForgeModel)
.def("GetINT", &Lua_Mob::GetINT) .def("GetINT", &Lua_Mob::GetINT)
.def("GetInvisibleLevel", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetInvisibleLevel) .def("GetInvisibleLevel", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetInvisibleLevel)
@@ -3642,6 +3673,7 @@ luabind::scope lua_register_mob() {
.def("InterruptSpell", (void(Lua_Mob::*)(int))&Lua_Mob::InterruptSpell) .def("InterruptSpell", (void(Lua_Mob::*)(int))&Lua_Mob::InterruptSpell)
.def("InterruptSpell", (void(Lua_Mob::*)(void))&Lua_Mob::InterruptSpell) .def("InterruptSpell", (void(Lua_Mob::*)(void))&Lua_Mob::InterruptSpell)
.def("IsAIControlled", (bool(Lua_Mob::*)(void))&Lua_Mob::IsAIControlled) .def("IsAIControlled", (bool(Lua_Mob::*)(void))&Lua_Mob::IsAIControlled)
.def("IsAlwaysAggro", &Lua_Mob::IsAlwaysAggro)
.def("IsAmnesiad", (bool(Lua_Mob::*)(void))&Lua_Mob::IsAmnesiad) .def("IsAmnesiad", (bool(Lua_Mob::*)(void))&Lua_Mob::IsAmnesiad)
.def("IsAnimation", &Lua_Mob::IsAnimation) .def("IsAnimation", &Lua_Mob::IsAnimation)
.def("IsAttackAllowed", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsAttackAllowed) .def("IsAttackAllowed", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsAttackAllowed)
@@ -3649,8 +3681,11 @@ luabind::scope lua_register_mob() {
.def("IsBeneficialAllowed", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsBeneficialAllowed) .def("IsBeneficialAllowed", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsBeneficialAllowed)
.def("IsBerserk", &Lua_Mob::IsBerserk) .def("IsBerserk", &Lua_Mob::IsBerserk)
.def("IsBlind", (bool(Lua_Mob::*)(void))&Lua_Mob::IsBlind) .def("IsBlind", (bool(Lua_Mob::*)(void))&Lua_Mob::IsBlind)
.def("IsBoat", &Lua_Mob::IsBoat)
.def("IsCasting", &Lua_Mob::IsCasting) .def("IsCasting", &Lua_Mob::IsCasting)
.def("IsCharmed", &Lua_Mob::IsCharmed) .def("IsCharmed", &Lua_Mob::IsCharmed)
.def("IsControllableBoat", &Lua_Mob::IsControllableBoat)
.def("IsDestructibleObject", &Lua_Mob::IsDestructibleObject)
.def("IsEliteMaterialItem", (uint32(Lua_Mob::*)(uint8))&Lua_Mob::IsEliteMaterialItem) .def("IsEliteMaterialItem", (uint32(Lua_Mob::*)(uint8))&Lua_Mob::IsEliteMaterialItem)
.def("IsEngaged", (bool(Lua_Mob::*)(void))&Lua_Mob::IsEngaged) .def("IsEngaged", (bool(Lua_Mob::*)(void))&Lua_Mob::IsEngaged)
.def("IsEnraged", (bool(Lua_Mob::*)(void))&Lua_Mob::IsEnraged) .def("IsEnraged", (bool(Lua_Mob::*)(void))&Lua_Mob::IsEnraged)
@@ -3866,7 +3901,9 @@ luabind::scope lua_register_special_abilities() {
luabind::value("modify_avoid_damage", static_cast<int>(MODIFY_AVOID_DAMAGE)), luabind::value("modify_avoid_damage", static_cast<int>(MODIFY_AVOID_DAMAGE)),
luabind::value("immune_open", static_cast<int>(IMMUNE_OPEN)), luabind::value("immune_open", static_cast<int>(IMMUNE_OPEN)),
luabind::value("immune_assassinate", static_cast<int>(IMMUNE_ASSASSINATE)), luabind::value("immune_assassinate", static_cast<int>(IMMUNE_ASSASSINATE)),
luabind::value("immune_headshot", static_cast<int>(IMMUNE_HEADSHOT)) luabind::value("immune_headshot", static_cast<int>(IMMUNE_HEADSHOT)),
luabind::value("immune_aggro_bot", static_cast<int>(IMMUNE_AGGRO_BOT)),
luabind::value("immune_damage_bot", static_cast<int>(IMMUNE_DAMAGE_BOT))
)]; )];
} }
+5
View File
@@ -577,6 +577,11 @@ public:
bool IsPetOwnerBot(); bool IsPetOwnerBot();
bool IsPetOwnerClient(); bool IsPetOwnerClient();
bool IsPetOwnerNPC(); bool IsPetOwnerNPC();
bool IsDestructibleObject();
bool IsBoat();
bool IsControllableBoat();
int GetHeroicStrikethrough();
bool IsAlwaysAggro();
}; };
#endif #endif
-1
View File
@@ -403,7 +403,6 @@ int main(int argc, char **argv)
content_service.SetDatabase(&database) content_service.SetDatabase(&database)
->SetContentDatabase(&content_db) ->SetContentDatabase(&content_db)
->SetContentZones(zone_store.GetZones())
->SetExpansionContext() ->SetExpansionContext()
->ReloadContentFlags(); ->ReloadContentFlags();
+126 -43
View File
@@ -1220,10 +1220,7 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
UpdateActiveLight(); UpdateActiveLight();
ns->spawn.light = m_Light.Type[EQ::lightsource::LightActive]; ns->spawn.light = m_Light.Type[EQ::lightsource::LightActive];
if (IsNPC() && race == ERUDITE) ns->spawn.showhelm = helmtexture != std::numeric_limits<uint8>::max() ? 1 : 0;
ns->spawn.showhelm = 1;
else
ns->spawn.showhelm = (helmtexture && helmtexture != 0xFF) ? 1 : 0;
ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players
ns->spawn.NPC = IsClient() ? 0 : 1; ns->spawn.NPC = IsClient() ? 0 : 1;
@@ -1272,10 +1269,8 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
strn0cpy(ns->spawn.lastName, lastname, sizeof(ns->spawn.lastName)); strn0cpy(ns->spawn.lastName, lastname, sizeof(ns->spawn.lastName));
//for (i = 0; i < _MaterialCount; i++) for (i = 0; i < EQ::textures::materialCount; i++) {
for (i = 0; i < 9; i++) { if (IsPlayerRace(race) || i > EQ::textures::armorFeet) {
// Only Player Races Wear Armor
if (IsPlayerRace(race) || i > 6) {
ns->spawn.equipment.Slot[i].Material = GetEquipmentMaterial(i); ns->spawn.equipment.Slot[i].Material = GetEquipmentMaterial(i);
ns->spawn.equipment.Slot[i].EliteModel = IsEliteMaterialItem(i); ns->spawn.equipment.Slot[i].EliteModel = IsEliteMaterialItem(i);
ns->spawn.equipment.Slot[i].HerosForgeModel = GetHerosForgeModel(i); ns->spawn.equipment.Slot[i].HerosForgeModel = GetHerosForgeModel(i);
@@ -1283,13 +1278,42 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
} }
} }
if (texture > 0) { for (i = 0; i < EQ::textures::weaponPrimary; i++) {
for (i = 0; i < 9; i++) { if (texture == std::numeric_limits<uint8>::max()) {
if (i == EQ::textures::weaponPrimary || i == EQ::textures::weaponSecondary || texture == 255) {
continue; continue;
} }
ns->spawn.equipment.Slot[i].Material = texture;
if (i == EQ::textures::armorHead && helmtexture != texture) {
ns->spawn.equipment.Slot[i].Material = helmtexture;
continue;
} }
if (i == EQ::textures::armorArms && armtexture != 0) {
ns->spawn.equipment.Slot[i].Material = armtexture;
continue;
}
if (i == EQ::textures::armorWrist && bracertexture != 0) {
ns->spawn.equipment.Slot[i].Material = bracertexture;
continue;
}
if (i == EQ::textures::armorHands && handtexture != 0) {
ns->spawn.equipment.Slot[i].Material = handtexture;
continue;
}
if (i == EQ::textures::armorLegs && legtexture != 0) {
ns->spawn.equipment.Slot[i].Material = legtexture;
continue;
}
if (i == EQ::textures::armorFeet && feettexture != 0) {
ns->spawn.equipment.Slot[i].Material = feettexture;
continue;
}
ns->spawn.equipment.Slot[i].Material = texture;
} }
memset(ns->spawn.set_to_0xFF, 0xFF, sizeof(ns->spawn.set_to_0xFF)); memset(ns->spawn.set_to_0xFF, 0xFF, sizeof(ns->spawn.set_to_0xFF));
@@ -4360,17 +4384,15 @@ void Mob::SendWearChangeAndLighting(int8 last_texture) {
void Mob::ChangeSize(float in_size = 0, bool unrestricted) void Mob::ChangeSize(float in_size = 0, bool unrestricted)
{ {
size = std::clamp(in_size, 1.0f, 255.0f);
if (!unrestricted) { if (!unrestricted) {
if (IsClient() || petid != 0) { if (IsClient() || petid != 0) {
EQ::Clamp(in_size, 3.0f, 15.0f); size = std::clamp(in_size, 3.0f, 15.0f);
} }
} }
EQ::Clamp(in_size, 1.0f, 255.0f); SendAppearancePacket(AppearanceType::Size, static_cast<uint32>(size));
size = in_size;
SendAppearancePacket(AppearanceType::Size, static_cast<uint32>(in_size));
} }
Mob* Mob::GetOwnerOrSelf() Mob* Mob::GetOwnerOrSelf()
@@ -5182,32 +5204,47 @@ int32 Mob::GetActSpellCasttime(uint16 spell_id, int32 casttime)
} }
void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on, int level_override) { void Mob::ExecWeaponProc(const EQ::ItemInstance* inst, uint16 spell_id, Mob* on, int level_override)
{
// Changed proc targets to look up based on the spells goodEffect flag. // Changed proc targets to look up based on the spells goodEffect flag.
// This should work for the majority of weapons. // This should work for the majority of weapons.
if (!on) { if (!on) {
return; return;
} }
if(!IsValidSpell(spell_id) || on->GetSpecialAbility(NO_HARM_FROM_CLIENT)) { if (!IsValidSpell(spell_id) || on->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
//This is so 65535 doesn't get passed to the client message and to logs because it is not relavant information for debugging. //This is so 65535 doesn't get passed to the client message and to logs because it is not relavant information for debugging.
return; return;
} }
if (on->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT) && IsClient()) if (IsBot() && on->GetSpecialAbility(IMMUNE_DAMAGE_BOT)) {
return; return;
}
if (on->GetSpecialAbility(IMMUNE_DAMAGE_NPC) && IsNPC()) if (IsClient() && on->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT)) {
return; return;
}
if (IsNoCast()) if (IsNPC() && on->GetSpecialAbility(IMMUNE_DAMAGE_NPC)) {
return; return;
}
if(!IsValidSpell(spell_id)) { // Check for a valid spell otherwise it will crash through the function if (IsNoCast()) {
if(IsClient()){ return;
Message(0, "Invalid spell proc %u", spell_id); }
if (!IsValidSpell(spell_id)) { // Check for a valid spell otherwise it will crash through the function
if (IsClient()) {
Message(
Chat::White,
fmt::format(
"Invalid spell ID for proc {}.",
spell_id
).c_str()
);
LogSpells("Player [{}] Weapon Procced invalid spell [{}]", GetName(), spell_id); LogSpells("Player [{}] Weapon Procced invalid spell [{}]", GetName(), spell_id);
} }
return; return;
} }
@@ -5221,7 +5258,7 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on,
return; return;
} }
if(inst && IsClient()) { if (inst && IsClient()) {
//const cast is dirty but it would require redoing a ton of interfaces at this point //const cast is dirty but it would require redoing a ton of interfaces at this point
//It should be safe as we don't have any truly const EQ::ItemInstance floating around anywhere. //It should be safe as we don't have any truly const EQ::ItemInstance floating around anywhere.
//So we'll live with it for now //So we'll live with it for now
@@ -5241,30 +5278,76 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on,
} }
} }
bool twinproc = false; bool twin_proc = false;
int32 twinproc_chance = 0; int32 twin_proc_chance = 0;
if (IsClient() || IsBot()) { if (IsClient() || IsBot()) {
twinproc_chance = GetFocusEffect(focusTwincast, spell_id); twin_proc_chance = GetFocusEffect(focusTwincast, spell_id);
} }
if (twinproc_chance && zone->random.Roll(twinproc_chance)) { if (twin_proc_chance && zone->random.Roll(twin_proc_chance)) {
twinproc = true; twin_proc = true;
} }
if (IsBeneficialSpell(spell_id) && (!IsNPC() || (IsNPC() && CastToNPC()->GetInnateProcSpellID() != spell_id)) && spells[spell_id].target_type != ST_TargetsTarget) { // NPC innate procs don't take this path ever if (
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override); IsBeneficialSpell(spell_id) &&
if (twinproc) { (
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override); !IsNPC() ||
(
IsNPC() &&
CastToNPC()->GetInnateProcSpellID() != spell_id
)
) &&
spells[spell_id].target_type != ST_TargetsTarget
) { // NPC innate procs don't take this path ever
SpellFinished(
spell_id,
this,
EQ::spells::CastingSlot::Item,
0,
-1,
spells[spell_id].resist_difficulty,
true,
level_override
);
if (twin_proc) {
SpellFinished(
spell_id,
this,
EQ::spells::CastingSlot::Item,
0,
-1,
spells[spell_id].resist_difficulty,
true,
level_override
);
}
} else if (!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients
SpellFinished(
spell_id,
on,
EQ::spells::CastingSlot::Item,
0,
-1,
spells[spell_id].resist_difficulty,
true,
level_override
);
if (twin_proc && (!(on->IsClient() && on->CastToClient()->dead))) {
SpellFinished(
spell_id,
on,
EQ::spells::CastingSlot::Item,
0,
-1,
spells[spell_id].resist_difficulty,
true,
level_override
);
} }
} }
else if(!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients
SpellFinished(spell_id, on, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
if (twinproc && (!(on->IsClient() && on->CastToClient()->dead))) {
SpellFinished(spell_id, on, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
}
}
return;
} }
uint32 Mob::GetZoneID() const { uint32 Mob::GetZoneID() const {
+2
View File
@@ -3798,6 +3798,8 @@ void NPC::DescribeSpecialAbilities(Client* c)
IMMUNE_OPEN, IMMUNE_OPEN,
IMMUNE_ASSASSINATE, IMMUNE_ASSASSINATE,
IMMUNE_HEADSHOT, IMMUNE_HEADSHOT,
IMMUNE_AGGRO_BOT,
IMMUNE_DAMAGE_BOT
}; };
// These abilities have parameters that need to be parsed out individually // These abilities have parameters that need to be parsed out individually
+30
View File
@@ -3395,6 +3395,31 @@ bool Perl_Mob_IsPetOwnerNPC(Mob* self)
return self->IsPetOwnerNPC(); return self->IsPetOwnerNPC();
} }
bool Perl_Mob_IsDestructibleObject(Mob* self)
{
return self->IsDestructibleObject();
}
bool Perl_Mob_IsBoat(Mob* self)
{
return self->IsBoat();
}
bool Perl_Mob_IsControllableBoat(Mob* self)
{
return self->IsControllableBoat();
}
int Perl_Mob_GetHeroicStrikethrough(Mob* self)
{
return self->GetHeroicStrikethrough();
}
bool Perl_Mob_IsAlwaysAggro(Mob* self)
{
return self->AlwaysAggro();
}
void perl_register_mob() void perl_register_mob()
{ {
perl::interpreter perl(PERL_GET_THX); perl::interpreter perl(PERL_GET_THX);
@@ -3660,6 +3685,7 @@ void perl_register_mob()
package.add("GetHateTopNPC", &Perl_Mob_GetHateTopNPC); package.add("GetHateTopNPC", &Perl_Mob_GetHateTopNPC);
package.add("GetHeading", &Perl_Mob_GetHeading); package.add("GetHeading", &Perl_Mob_GetHeading);
package.add("GetHelmTexture", &Perl_Mob_GetHelmTexture); package.add("GetHelmTexture", &Perl_Mob_GetHelmTexture);
package.add("GetHeroicStrikethrough", &Perl_Mob_GetHeroicStrikethrough);
package.add("GetHerosForgeModel", &Perl_Mob_GetHerosForgeModel); package.add("GetHerosForgeModel", &Perl_Mob_GetHerosForgeModel);
package.add("GetID", &Perl_Mob_GetID); package.add("GetID", &Perl_Mob_GetID);
package.add("GetINT", &Perl_Mob_GetINT); package.add("GetINT", &Perl_Mob_GetINT);
@@ -3759,6 +3785,7 @@ void perl_register_mob()
package.add("InterruptSpell", (void(*)(Mob*))&Perl_Mob_InterruptSpell); package.add("InterruptSpell", (void(*)(Mob*))&Perl_Mob_InterruptSpell);
package.add("InterruptSpell", (void(*)(Mob*, uint16))&Perl_Mob_InterruptSpell); package.add("InterruptSpell", (void(*)(Mob*, uint16))&Perl_Mob_InterruptSpell);
package.add("IsAIControlled", &Perl_Mob_IsAIControlled); package.add("IsAIControlled", &Perl_Mob_IsAIControlled);
package.add("IsAlwaysAggro", &Perl_Mob_IsAlwaysAggro);
package.add("IsAmnesiad", &Perl_Mob_IsAmnesiad); package.add("IsAmnesiad", &Perl_Mob_IsAmnesiad);
package.add("IsAnimation", &Perl_Mob_IsAnimation); package.add("IsAnimation", &Perl_Mob_IsAnimation);
package.add("IsAttackAllowed", (bool(*)(Mob*, Mob*))&Perl_Mob_IsAttackAllowed); package.add("IsAttackAllowed", (bool(*)(Mob*, Mob*))&Perl_Mob_IsAttackAllowed);
@@ -3768,11 +3795,14 @@ void perl_register_mob()
package.add("IsBeneficialAllowed", &Perl_Mob_IsBeneficialAllowed); package.add("IsBeneficialAllowed", &Perl_Mob_IsBeneficialAllowed);
package.add("IsBerserk", &Perl_Mob_IsBerserk); package.add("IsBerserk", &Perl_Mob_IsBerserk);
package.add("IsBlind", &Perl_Mob_IsBlind); package.add("IsBlind", &Perl_Mob_IsBlind);
package.add("IsBoat", &Perl_Mob_IsBoat);
package.add("IsBot", &Perl_Mob_IsBot); package.add("IsBot", &Perl_Mob_IsBot);
package.add("IsCasting", &Perl_Mob_IsCasting); package.add("IsCasting", &Perl_Mob_IsCasting);
package.add("IsCharmed", &Perl_Mob_IsCharmed); package.add("IsCharmed", &Perl_Mob_IsCharmed);
package.add("IsClient", &Perl_Mob_IsClient); package.add("IsClient", &Perl_Mob_IsClient);
package.add("IsControllableBoat", &Perl_Mob_IsControllableBoat);
package.add("IsCorpse", &Perl_Mob_IsCorpse); package.add("IsCorpse", &Perl_Mob_IsCorpse);
package.add("IsDestructibleObject", &Perl_Mob_IsDestructibleObject);
package.add("IsDoor", &Perl_Mob_IsDoor); package.add("IsDoor", &Perl_Mob_IsDoor);
package.add("IsEliteMaterialItem", &Perl_Mob_IsEliteMaterialItem); package.add("IsEliteMaterialItem", &Perl_Mob_IsEliteMaterialItem);
package.add("IsEncounter", &Perl_Mob_IsEncounter); package.add("IsEncounter", &Perl_Mob_IsEncounter);
+9 -9
View File
@@ -805,7 +805,7 @@ int QuestParserCollection::EventBotGlobal(
QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::string& filename) QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::string& filename)
{ {
if (!zone || !zone->IsLoaded()) { if (!zone) {
return nullptr; return nullptr;
} }
@@ -871,7 +871,7 @@ QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::strin
QuestInterface* QuestParserCollection::GetQIByPlayerQuest(std::string& filename) QuestInterface* QuestParserCollection::GetQIByPlayerQuest(std::string& filename)
{ {
if (!zone || !zone->IsLoaded()) { if (!zone) {
return nullptr; return nullptr;
} }
@@ -922,7 +922,7 @@ QuestInterface* QuestParserCollection::GetQIByPlayerQuest(std::string& filename)
QuestInterface* QuestParserCollection::GetQIByGlobalNPCQuest(std::string& filename) QuestInterface* QuestParserCollection::GetQIByGlobalNPCQuest(std::string& filename)
{ {
if (!zone || !zone->IsLoaded()) { if (!zone) {
return nullptr; return nullptr;
} }
@@ -947,7 +947,7 @@ QuestInterface* QuestParserCollection::GetQIByGlobalNPCQuest(std::string& filena
QuestInterface* QuestParserCollection::GetQIByGlobalPlayerQuest(std::string& filename) QuestInterface* QuestParserCollection::GetQIByGlobalPlayerQuest(std::string& filename)
{ {
if (!zone || !zone->IsLoaded()) { if (!zone) {
return nullptr; return nullptr;
} }
@@ -971,7 +971,7 @@ QuestInterface* QuestParserCollection::GetQIByGlobalPlayerQuest(std::string& fil
QuestInterface* QuestParserCollection::GetQIBySpellQuest(uint32 spell_id, std::string& filename) QuestInterface* QuestParserCollection::GetQIBySpellQuest(uint32 spell_id, std::string& filename)
{ {
if (!zone || !zone->IsLoaded()) { if (!zone) {
return nullptr; return nullptr;
} }
@@ -1023,7 +1023,7 @@ QuestInterface* QuestParserCollection::GetQIBySpellQuest(uint32 spell_id, std::s
QuestInterface* QuestParserCollection::GetQIByItemQuest(std::string item_script, std::string& filename) QuestInterface* QuestParserCollection::GetQIByItemQuest(std::string item_script, std::string& filename)
{ {
if (!zone || !zone->IsLoaded()) { if (!zone) {
return nullptr; return nullptr;
} }
@@ -1075,7 +1075,7 @@ QuestInterface* QuestParserCollection::GetQIByItemQuest(std::string item_script,
QuestInterface* QuestParserCollection::GetQIByEncounterQuest(std::string encounter_name, std::string& filename) QuestInterface* QuestParserCollection::GetQIByEncounterQuest(std::string encounter_name, std::string& filename)
{ {
if (!zone || !zone->IsLoaded()) { if (!zone) {
return nullptr; return nullptr;
} }
@@ -1125,7 +1125,7 @@ QuestInterface* QuestParserCollection::GetQIByEncounterQuest(std::string encount
QuestInterface* QuestParserCollection::GetQIByBotQuest(std::string& filename) QuestInterface* QuestParserCollection::GetQIByBotQuest(std::string& filename)
{ {
if (!zone || !zone->IsLoaded()) { if (!zone) {
return nullptr; return nullptr;
} }
@@ -1176,7 +1176,7 @@ QuestInterface* QuestParserCollection::GetQIByBotQuest(std::string& filename)
QuestInterface* QuestParserCollection::GetQIByGlobalBotQuest(std::string& filename) QuestInterface* QuestParserCollection::GetQIByGlobalBotQuest(std::string& filename)
{ {
if (!zone || !zone->IsLoaded()) { if (!zone) {
return nullptr; return nullptr;
} }
-4
View File
@@ -752,11 +752,7 @@ void QuestManager::stoptimer(const std::string& timer_name)
); );
for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) { for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) {
LogInfo("Current [{}] Timer [{}]", e->name, timer_name);
if (e->mob && e->mob == owner && e->name == timer_name) { if (e->mob && e->mob == owner && e->name == timer_name) {
LogInfo("Matched [{}] Timer [{}]", e->name, timer_name);
if (has_stop_event) { if (has_stop_event) {
if (owner->IsClient()) { if (owner->IsClient()) {
parse->EventPlayer(EVENT_TIMER_STOP, owner->CastToClient(), timer_name, 0); parse->EventPlayer(EVENT_TIMER_STOP, owner->CastToClient(), timer_name, 0);
+2 -6
View File
@@ -40,8 +40,6 @@ extern WorldServer worldserver;
Raid::Raid(uint32 raidID) Raid::Raid(uint32 raidID)
: GroupIDConsumer(raidID) : GroupIDConsumer(raidID)
{ {
memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct));
memset(group_aa, 0, sizeof(GroupLeadershipAA_Struct) * MAX_RAID_GROUPS);
for (auto& gm : group_mentor) { for (auto& gm : group_mentor) {
gm.mentor_percent = 0; gm.mentor_percent = 0;
gm.mentoree = nullptr; gm.mentoree = nullptr;
@@ -65,8 +63,6 @@ Raid::Raid(uint32 raidID)
Raid::Raid(Client* nLeader) Raid::Raid(Client* nLeader)
: GroupIDConsumer() : GroupIDConsumer()
{ {
memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct));
memset(group_aa, 0, sizeof(GroupLeadershipAA_Struct) * MAX_RAID_GROUPS);
for (auto& gm : group_mentor) { for (auto& gm : group_mentor) {
gm.mentor_percent = 0; gm.mentor_percent = 0;
gm.mentoree = nullptr; gm.mentoree = nullptr;
@@ -451,13 +447,13 @@ void Raid::SaveRaidLeaderAA()
void Raid::UpdateGroupAAs(uint32 gid) void Raid::UpdateGroupAAs(uint32 gid)
{ {
if (gid > MAX_RAID_GROUPS) { if (gid > MAX_RAID_GROUPS || gid == RAID_GROUPLESS || gid < 0) {
return; return;
} }
Client *gl = GetGroupLeader(gid); Client *gl = GetGroupLeader(gid);
if (gl) { if (gl && gl->IsClient()) {
gl->GetGroupAAs(&group_aa[gid]); gl->GetGroupAAs(&group_aa[gid]);
} else { } else {
memset(&group_aa[gid], 0, sizeof(GroupLeadershipAA_Struct)); memset(&group_aa[gid], 0, sizeof(GroupLeadershipAA_Struct));
+2 -2
View File
@@ -292,8 +292,8 @@ protected:
bool disbandCheck; bool disbandCheck;
bool forceDisband; bool forceDisband;
std::string motd; std::string motd;
RaidLeadershipAA_Struct raid_aa; RaidLeadershipAA_Struct raid_aa{};
GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS]; GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS]{};
GroupMentor group_mentor[MAX_RAID_GROUPS]; GroupMentor group_mentor[MAX_RAID_GROUPS];
+1 -1
View File
@@ -807,7 +807,7 @@ void SpawnConditionManager::UpdateSpawnEvent(SpawnEvent &event)
e.next_month = event.next.month; e.next_month = event.next.month;
e.next_year = event.next.year; e.next_year = event.next.year;
e.enabled = event.enabled ? 1 : 0; e.enabled = event.enabled ? 1 : 0;
e.next_minute = event.strict; e.strict = event.strict ? 1 : 0;
SpawnEventsRepository::UpdateOne(database, e); SpawnEventsRepository::UpdateOne(database, e);
} }
+2 -2
View File
@@ -707,7 +707,7 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) {
CastToClient()->Message(Chat::White,"Your fierce attack is executed with such grace, your target did not see it coming!"); CastToClient()->Message(Chat::White,"Your fierce attack is executed with such grace, your target did not see it coming!");
RogueBackstab(other,false,ReuseTime); RogueBackstab(other,false,ReuseTime);
if (level > 54) { if (level >= RuleI(Combat, DoubleBackstabLevelRequirement)) {
// TODO: 55-59 doesn't appear to match just checking double attack, 60+ does though // TODO: 55-59 doesn't appear to match just checking double attack, 60+ does though
if(IsClient() && CastToClient()->CheckDoubleAttack()) if(IsClient() && CastToClient()->CheckDoubleAttack())
{ {
@@ -2402,7 +2402,7 @@ int Mob::TryAssassinate(Mob *defender, EQ::skills::SkillType skillInUse)
if ( if (
defender && defender &&
!defender->IsClient() && !defender->IsClient() &&
GetLevel() >= 60 && GetLevel() >= RuleI(Combat, AssassinateLevelRequirement) &&
(skillInUse == EQ::skills::SkillBackstab || skillInUse == EQ::skills::SkillThrowing) && (skillInUse == EQ::skills::SkillBackstab || skillInUse == EQ::skills::SkillThrowing) &&
(defender->GetBodyType() == BT_Humanoid || !RuleB(Combat, AssassinateOnlyHumanoids)) && (defender->GetBodyType() == BT_Humanoid || !RuleB(Combat, AssassinateOnlyHumanoids)) &&
!defender->GetSpecialAbility(IMMUNE_ASSASSINATE) !defender->GetSpecialAbility(IMMUNE_ASSASSINATE)
+5 -2
View File
@@ -221,9 +221,12 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
BuffFadeByEffect(SE_NegateIfCombat); BuffFadeByEffect(SE_NegateIfCombat);
} }
// check to see if target is a caster mob before performing a mana tap
if(GetTarget() && IsManaTapSpell(spell_id)) { if(GetTarget() && IsManaTapSpell(spell_id)) {
if (!RuleB(Spells, ManaTapsOnAnyClass) && GetTarget()->GetCasterClass() != 'N' && RuleB(Spells, ManaTapsRequireNPCMana) && GetTarget()->GetMana() == 0) { // If melee, block if ManaTapsOnAnyClass rule is false
// if caster, block if ManaTapsRequireNPCMana and no mana
bool melee_block = !RuleB(Spells, ManaTapsOnAnyClass);
bool caster_block = (GetTarget()->GetCasterClass() != 'N' && RuleB(Spells, ManaTapsRequireNPCMana) && GetTarget()->GetMana() == 0);
if (melee_block || caster_block) {
InterruptSpell(TARGET_NO_MANA, 0x121, spell_id); InterruptSpell(TARGET_NO_MANA, 0x121, spell_id);
return false; return false;
} }
+2 -2
View File
@@ -2290,8 +2290,8 @@ void ClientTaskState::CreateTaskDynamicZone(Client* client, int task_id, Dynamic
} }
// dz should be named the version-based zone name (used in choose zone window and dz window on live) // dz should be named the version-based zone name (used in choose zone window and dz window on live)
auto zone_info = zone_store.GetZone(dz_request.GetZoneID(), dz_request.GetZoneVersion()); auto zone_info = zone_store.GetZoneWithFallback(dz_request.GetZoneID(), dz_request.GetZoneVersion());
dz_request.SetName(zone_info->long_name.empty() ? task->title : zone_info->long_name); dz_request.SetName(zone_info && !zone_info->long_name.empty() ? zone_info->long_name : task->title);
dz_request.SetMinPlayers(task->min_players); dz_request.SetMinPlayers(task->min_players);
dz_request.SetMaxPlayers(task->max_players); dz_request.SetMaxPlayers(task->max_players);
+12 -10
View File
@@ -3696,29 +3696,31 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
} }
break; break;
} }
case ServerOP_GuildTributeUpdateDonations: case ServerOP_GuildTributeUpdateDonations: {
{ auto in = (GuildTributeUpdate *) pack->pBuffer;
GuildTributeUpdate* in = (GuildTributeUpdate*)pack->pBuffer; auto outapp = new EQApplicationPacket(OP_GuildOptInOut, sizeof(GuildTributeOptInOutReply_Struct));
auto data = (GuildTributeOptInOutReply_Struct *) outapp->pBuffer;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildOptInOut, sizeof(GuildTributeOptInOutReply_Struct));
GuildTributeOptInOutReply_Struct* data = (GuildTributeOptInOutReply_Struct*)outapp->pBuffer;
data->guild_id = in->guild_id; data->guild_id = in->guild_id;
strn0cpy(data->player_name, in->player_name, sizeof(data->player_name));
data->no_donations = in->member_favor; data->no_donations = in->member_favor;
data->tribute_toggle = in->member_enabled ? true : false; data->tribute_toggle = in->member_enabled ? true : false;
data->tribute_trophy_toggle = 0; //not yet implemented data->tribute_trophy_toggle = 0; // not yet implemented
data->time = in->member_time; data->time = in->member_time;
data->command = 1; data->command = 1;
strn0cpy(data->player_name, in->player_name, sizeof(data->player_name));
entity_list.QueueClientsGuild(outapp, in->guild_id); entity_list.QueueClientsGuild(outapp, in->guild_id);
safe_delete(outapp); safe_delete(outapp);
//my new items
outapp = new EQApplicationPacket(OP_GuildTributeToggleReply, sizeof(GuildTributeSendActive_Struct)); outapp = new EQApplicationPacket(OP_GuildTributeToggleReply, sizeof(GuildTributeSendActive_Struct));
GuildTributeSendActive_Struct *out = (GuildTributeSendActive_Struct *) outapp->pBuffer; auto out = (GuildTributeSendActive_Struct *) outapp->pBuffer;
auto guild = guild_mgr.GetGuildByGuildID(in->guild_id); auto guild = guild_mgr.GetGuildByGuildID(in->guild_id);
if (!guild) {
safe_delete(outapp)
return;
}
out->not_used = in->guild_id; out->not_used = in->guild_id;
out->guild_favor = guild->tribute.favor; out->guild_favor = guild->tribute.favor;
out->tribute_enabled = guild->tribute.enabled; out->tribute_enabled = guild->tribute.enabled;
+70 -3
View File
@@ -355,9 +355,7 @@ bool Zone::IsSpecialBindLocation(const glm::vec4& location)
//this also just loads into entity_list, not really into zone //this also just loads into entity_list, not really into zone
bool Zone::LoadGroundSpawns() { bool Zone::LoadGroundSpawns() {
GroundSpawns g; GroundSpawns g{};
memset(&g, 0, sizeof(g));
content_db.LoadGroundSpawns(zoneid, GetInstanceVersion(), &g); content_db.LoadGroundSpawns(zoneid, GetInstanceVersion(), &g);
@@ -3167,6 +3165,11 @@ void Zone::ClearEXPModifier(Client* c)
exp_modifiers.erase(c->CharacterID()); exp_modifiers.erase(c->CharacterID());
} }
void Zone::ClearEXPModifierByCharacterID(const uint32 character_id)
{
exp_modifiers.erase(character_id);
}
float Zone::GetAAEXPModifier(Client* c) float Zone::GetAAEXPModifier(Client* c)
{ {
const auto& l = exp_modifiers.find(c->CharacterID()); const auto& l = exp_modifiers.find(c->CharacterID());
@@ -3179,6 +3182,18 @@ float Zone::GetAAEXPModifier(Client* c)
return v.aa_modifier; return v.aa_modifier;
} }
float Zone::GetAAEXPModifierByCharacterID(const uint32 character_id)
{
const auto& l = exp_modifiers.find(character_id);
if (l == exp_modifiers.end()) {
return 1.0f;
}
const auto& v = l->second;
return v.aa_modifier;
}
float Zone::GetEXPModifier(Client* c) float Zone::GetEXPModifier(Client* c)
{ {
const auto& l = exp_modifiers.find(c->CharacterID()); const auto& l = exp_modifiers.find(c->CharacterID());
@@ -3191,6 +3206,18 @@ float Zone::GetEXPModifier(Client* c)
return v.exp_modifier; return v.exp_modifier;
} }
float Zone::GetEXPModifierByCharacterID(const uint32 character_id)
{
const auto& l = exp_modifiers.find(character_id);
if (l == exp_modifiers.end()) {
return 1.0f;
}
const auto& v = l->second;
return v.exp_modifier;
}
void Zone::SetAAEXPModifier(Client* c, float aa_modifier) void Zone::SetAAEXPModifier(Client* c, float aa_modifier)
{ {
auto l = exp_modifiers.find(c->CharacterID()); auto l = exp_modifiers.find(c->CharacterID());
@@ -3211,6 +3238,26 @@ void Zone::SetAAEXPModifier(Client* c, float aa_modifier)
); );
} }
void Zone::SetAAEXPModifierByCharacterID(const uint32 character_id, float aa_modifier)
{
auto l = exp_modifiers.find(character_id);
if (l == exp_modifiers.end()) {
return;
}
auto& m = l->second;
m.aa_modifier = aa_modifier;
CharacterExpModifiersRepository::SetEXPModifier(
database,
character_id,
GetZoneID(),
GetInstanceVersion(),
m
);
}
void Zone::SetEXPModifier(Client* c, float exp_modifier) void Zone::SetEXPModifier(Client* c, float exp_modifier)
{ {
auto l = exp_modifiers.find(c->CharacterID()); auto l = exp_modifiers.find(c->CharacterID());
@@ -3231,6 +3278,26 @@ void Zone::SetEXPModifier(Client* c, float exp_modifier)
); );
} }
void Zone::SetEXPModifierByCharacterID(const uint32 character_id, float exp_modifier)
{
auto l = exp_modifiers.find(character_id);
if (l == exp_modifiers.end()) {
return;
}
auto& m = l->second;
m.exp_modifier = exp_modifier;
CharacterExpModifiersRepository::SetEXPModifier(
database,
character_id,
GetZoneID(),
GetInstanceVersion(),
m
);
}
bool Zone::IsIdleWhenEmpty() const bool Zone::IsIdleWhenEmpty() const
{ {
return m_idle_when_empty; return m_idle_when_empty;
+5
View File
@@ -269,10 +269,15 @@ public:
void SendReloadMessage(std::string reload_type); void SendReloadMessage(std::string reload_type);
void ClearEXPModifier(Client* c); void ClearEXPModifier(Client* c);
void ClearEXPModifierByCharacterID(const uint32 character_id);
float GetAAEXPModifier(Client* c); float GetAAEXPModifier(Client* c);
float GetAAEXPModifierByCharacterID(const uint32 character_id);
float GetEXPModifier(Client* c); float GetEXPModifier(Client* c);
float GetEXPModifierByCharacterID(const uint32 character_id);
void SetAAEXPModifier(Client* c, float aa_modifier); void SetAAEXPModifier(Client* c, float aa_modifier);
void SetAAEXPModifierByCharacterID(const uint32 character_id, float aa_modifier);
void SetEXPModifier(Client* c, float exp_modifier); void SetEXPModifier(Client* c, float exp_modifier);
void SetEXPModifierByCharacterID(const uint32 character_id, float exp_modifier);
void AddAggroMob() { aggroedmobs++; } void AddAggroMob() { aggroedmobs++; }
void AddAuth(ServerZoneIncomingClient_Struct *szic); void AddAuth(ServerZoneIncomingClient_Struct *szic);
+2 -2
View File
@@ -4270,7 +4270,7 @@ void ZoneDatabase::SetAAEXPModifierByCharID(
instance_version, instance_version,
EXPModifier{ EXPModifier{
.aa_modifier = aa_modifier, .aa_modifier = aa_modifier,
.exp_modifier = -1.0f .exp_modifier = zone->GetEXPModifierByCharacterID(character_id)
} }
); );
} }
@@ -4288,7 +4288,7 @@ void ZoneDatabase::SetEXPModifierByCharID(
zone_id, zone_id,
instance_version, instance_version,
EXPModifier{ EXPModifier{
.aa_modifier = -1.0f, .aa_modifier = zone->GetAAEXPModifierByCharacterID(character_id),
.exp_modifier = exp_modifier .exp_modifier = exp_modifier
} }
); );
+57 -8
View File
@@ -405,11 +405,23 @@ void Client::SendZoneCancel(ZoneChange_Struct *zc) {
zc->success zc->success
); );
strcpy(zc2->char_name, zc->char_name); strn0cpy(zc2->char_name, zc->char_name, 64);
zc2->zoneID = zone->GetZoneID(); zc2->zoneID = zone->GetZoneID();
zc2->success = 1; zc2->success = 1;
outapp->priority = 6; zc2->instanceID = zone->GetInstanceID();
FastQueuePacket(&outapp);
// this fixes an issue where when we do a zone cancel what often ends up happening is we are sending
// the client the wrong coordinates to zone back to. Often times it is the x,y,z of the destination zone
// because we saved the destination x,y,z on the client profile before we rejected the zone request.
// we're using rewind location because it should be where the client relatively was before we rejected the zone request.
// it also prevents the client from getting caught up in a zone loop because if we sent them exactly back to where they
// originated the request we could end up in a situation where the client is caught in a zone loop.
m_Position.x = m_RewindLocation.x;
m_Position.y = m_RewindLocation.y;
m_Position.z = m_RewindLocation.z;
zc2->x = m_Position.x;
zc2->y = m_Position.y;
zc2->z = m_Position.z;
LogZoning( LogZoning(
"(zc2) Client [{}] char_name [{}] zoning to [{}] ({}) cancelled instance_id [{}] x [{}] y [{}] z [{}] zone_reason [{}] success [{}]", "(zc2) Client [{}] char_name [{}] zoning to [{}] ({}) cancelled instance_id [{}] x [{}] y [{}] z [{}] zone_reason [{}] success [{}]",
@@ -425,6 +437,9 @@ void Client::SendZoneCancel(ZoneChange_Struct *zc) {
zc2->success zc2->success
); );
outapp->priority = 6;
FastQueuePacket(&outapp);
//reset to unsolicited. //reset to unsolicited.
zone_mode = ZoneUnsolicited; zone_mode = ZoneUnsolicited;
// reset since we're not zoning anymore // reset since we're not zoning anymore
@@ -740,10 +755,34 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
pZoneName = strcpy(new char[zd->long_name.length() + 1], zd->long_name.c_str()); pZoneName = strcpy(new char[zd->long_name.length() + 1], zd->long_name.c_str());
} }
LogInfo( // If we are zoning to the same zone, we need to use the current instance ID if it is not specified.
"Client [{}] zone_id [{}] x [{}] y [{}] z [{}] heading [{}] ignorerestrictions [{}] zone_mode [{}]", if (zoneID == zone->GetZoneID() && instance_id == 0) {
instance_id = zone->GetInstanceID();
}
auto r = content_service.FindZone(zoneID, instance_id);
if (r.zone_id) {
zoneID = r.zone_id;
instance_id = r.instance.id;
LogZoning(
"Client caught HandleZoneRoutingMiddleware [{}] zone_id [{}] instance_id [{}] x [{}] y [{}] z [{}] heading [{}] ignorerestrictions [{}] zone_mode [{}]",
GetCleanName(), GetCleanName(),
zoneID, zoneID,
instance_id,
x,
y,
z,
heading,
ignorerestrictions,
static_cast<int>(zm)
);
}
LogInfo(
"Client [{}] zone_id [{}] instance_id [{}] x [{}] y [{}] z [{}] heading [{}] ignorerestrictions [{}] zone_mode [{}]",
GetCleanName(),
zoneID,
instance_id,
x, x,
y, y,
z, z,
@@ -867,9 +906,8 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
outapp->priority = 6; outapp->priority = 6;
FastQueuePacket(&outapp); FastQueuePacket(&outapp);
} }
else if(zm == ZoneSolicited || zm == ZoneToSafeCoords) { else if (zm == ZoneSolicited || zm == ZoneToSafeCoords) {
auto outapp = auto outapp = new EQApplicationPacket(OP_RequestClientZoneChange, sizeof(RequestClientZoneChange_Struct));
new EQApplicationPacket(OP_RequestClientZoneChange, sizeof(RequestClientZoneChange_Struct));
RequestClientZoneChange_Struct* gmg = (RequestClientZoneChange_Struct*) outapp->pBuffer; RequestClientZoneChange_Struct* gmg = (RequestClientZoneChange_Struct*) outapp->pBuffer;
gmg->zone_id = zoneID; gmg->zone_id = zoneID;
@@ -880,6 +918,17 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
gmg->instance_id = instance_id; gmg->instance_id = instance_id;
gmg->type = 0x01; //an observed value, not sure of meaning gmg->type = 0x01; //an observed value, not sure of meaning
LogZoning(
"Player [{}] has requested zoning to zone_id [{}] instance_id [{}] x [{}] y [{}] z [{}] heading [{}]",
GetCleanName(),
zoneID,
instance_id,
x,
y,
z,
heading
);
outapp->priority = 6; outapp->priority = 6;
FastQueuePacket(&outapp); FastQueuePacket(&outapp);
} }