mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-23 04:52:29 +00:00
Compare commits
110 Commits
v22.45.0
...
multiquest
| Author | SHA1 | Date | |
|---|---|---|---|
| caae34ac5e | |||
| 56c7db4cbf | |||
| 043eeced6f | |||
| e9a0c79301 | |||
| dc48c45421 | |||
| d7a8fb8691 | |||
| 3a5381d38a | |||
| e64f03fcc0 | |||
| d77966797e | |||
| 048aad437b | |||
| b2d9de8d96 | |||
| 42bfa4bb2e | |||
| 72ce7c8e91 | |||
| e306c86875 | |||
| 3ae7979a67 | |||
| b638795f9b | |||
| 9e3bf91374 | |||
| cd89926435 | |||
| e19f72f021 | |||
| df1dc5d1e4 | |||
| e11286164f | |||
| b946b800fb | |||
| ab8ac81df6 | |||
| 109940fc0c | |||
| a87496b0cf | |||
| f905ee70e4 | |||
| 52417023f8 | |||
| 20d9417628 | |||
| 4692799677 | |||
| cf7f0f4321 | |||
| 823a5956de | |||
| 6a8bd3c5d6 | |||
| 523ba30d81 | |||
| d7ea290b6b | |||
| 036309ebec | |||
| b400700d81 | |||
| 21cec87ac4 | |||
| abdec39cdd | |||
| d2372de982 | |||
| ea9b7841d4 | |||
| 8826d7b927 | |||
| e4157f0221 | |||
| 66cc947b2a | |||
| 5bfd8f5da2 | |||
| 96830b4a19 | |||
| 4bf60a6522 | |||
| 16cb7364e8 | |||
| f5050ab5dc | |||
| e32cbf19ee | |||
| ee12a7ad2e | |||
| e5bdbc4f1e | |||
| f829a99e6d | |||
| 82aa6a1587 | |||
| 161c13f457 | |||
| b29c26becb | |||
| e48dae2392 | |||
| 4b83a96f64 | |||
| 95cc22ffbb | |||
| 6ca11256c6 | |||
| d94493468c | |||
| 957b4f8821 | |||
| 5013459824 | |||
| 94af2843e3 | |||
| 3bfb148bdc | |||
| 96370e0298 | |||
| fef5108b0d | |||
| 6f883566f6 | |||
| 45b1501c8a | |||
| 7e94f0ac72 | |||
| 2aa19f4cae | |||
| 805829f15d | |||
| 2c2a8cdb63 | |||
| ee3d02bac6 | |||
| b90139bd9a | |||
| e6a3d5e1c5 | |||
| 74f1eac401 | |||
| 1be9b2cdfd | |||
| add0a8dddf | |||
| 8c226054e7 | |||
| b4605f77e3 | |||
| 74a63daf7e | |||
| 8ee7910569 | |||
| b766a79c11 | |||
| 5e3b6d363a | |||
| b2fc59878a | |||
| 4896688ac5 | |||
| 0385ed8526 | |||
| 9974aaff57 | |||
| b6c3e549da | |||
| 70ee95efc0 | |||
| 1d38e473d7 | |||
| 1aa3a4b11a | |||
| 398ecbc8cf | |||
| c4613e1b0f | |||
| 3003a59955 | |||
| 0c582cc4f9 | |||
| 69c42510ca | |||
| 79c8858ec8 | |||
| c3d8d423fe | |||
| 1cbda61891 | |||
| 8d12a5b1b1 | |||
| 182327b385 | |||
| aa0ca88d9d | |||
| 103a37e762 | |||
| 34f19489d0 | |||
| c001060429 | |||
| 89be55254e | |||
| 2da6190950 | |||
| d5e024cc02 | |||
| 35fe38cd09 |
+169
@@ -1,3 +1,172 @@
|
||||
## [22.48.0] 3/23/2024
|
||||
|
||||
### Bots
|
||||
|
||||
* IsValidTarget Crash Fix ([#4187](https://github.com/EQEmu/Server/pull/4187)) @nytmyr 2024-03-12
|
||||
* Move BotGroupSay to Pet Response ([#4171](https://github.com/EQEmu/Server/pull/4171)) @nytmyr 2024-03-08
|
||||
|
||||
### Code
|
||||
|
||||
* Cleanup Zone Get Methods ([#4169](https://github.com/EQEmu/Server/pull/4169)) @Kinglykrab 2024-03-09
|
||||
|
||||
### Fixes
|
||||
|
||||
* An Update to Xtarget to exclude Bot owned Temp/Swarm Pets ([#4172](https://github.com/EQEmu/Server/pull/4172)) @MortimerGreenwald 2024-03-08
|
||||
* Fix #serverrules Command ([#4193](https://github.com/EQEmu/Server/pull/4193)) @Kinglykrab 2024-03-20
|
||||
* Fix Bot Cloning ([#4186](https://github.com/EQEmu/Server/pull/4186)) @Kinglykrab 2024-03-17
|
||||
* Fix Crash in ClientList::GetCLEIP ([#4173](https://github.com/EQEmu/Server/pull/4173)) @Kinglykrab 2024-03-10
|
||||
* Fix Default Value in `rule_values` table ([#4166](https://github.com/EQEmu/Server/pull/4166)) @Kinglykrab 2024-03-07
|
||||
* Fix EVENT_KILLED_MERIT firing before NPC removal ([#4185](https://github.com/EQEmu/Server/pull/4185)) @Kinglykrab 2024-03-17
|
||||
* Fix Empty Groups When Removing Bots ([#4178](https://github.com/EQEmu/Server/pull/4178)) @Kinglykrab 2024-03-14
|
||||
* Fix GetLeaderName() for Groups ([#4184](https://github.com/EQEmu/Server/pull/4184)) @Kinglykrab 2024-03-14
|
||||
* Fix Mob::CalculateDistance(mob) Typo ([#4183](https://github.com/EQEmu/Server/pull/4183)) @Kinglykrab 2024-03-10
|
||||
* Fix Proximity Say ([#4189](https://github.com/EQEmu/Server/pull/4189)) @Kinglykrab 2024-03-15
|
||||
* Fix ScaleNPC() in Perl ([#4196](https://github.com/EQEmu/Server/pull/4196)) @Kinglykrab 2024-03-23
|
||||
* Fix range_percent ([#4197](https://github.com/EQEmu/Server/pull/4197)) @Kinglykrab 2024-03-22
|
||||
* Fix reusing timers ([#4199](https://github.com/EQEmu/Server/pull/4199)) @joligario 2024-03-23
|
||||
|
||||
### Hot Fix
|
||||
|
||||
* Add bool return to fix Client::RemoveAAPoints ([#4176](https://github.com/EQEmu/Server/pull/4176)) @Kinglykrab 2024-03-09
|
||||
|
||||
### Loot
|
||||
|
||||
* Fix issue with nested data being loaded multiple times ([#4192](https://github.com/EQEmu/Server/pull/4192)) @Akkadius 2024-03-23
|
||||
|
||||
### Misc
|
||||
|
||||
* Windows preprocessor define in crash.cpp ([#4191](https://github.com/EQEmu/Server/pull/4191)) @joligario 2024-03-23
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add Buff Support to Perl/Lua ([#4182](https://github.com/EQEmu/Server/pull/4182)) @Kinglykrab 2024-03-14
|
||||
* Add DeleteBot() to Perl/Lua ([#4167](https://github.com/EQEmu/Server/pull/4167)) @nytmyr 2024-03-07
|
||||
* Add GetDeityName() to Perl/Lua ([#4180](https://github.com/EQEmu/Server/pull/4180)) @Kinglykrab 2024-03-14
|
||||
* Add RemoveAAPoints() and AA Loss Event to Perl/Lua ([#4174](https://github.com/EQEmu/Server/pull/4174)) @Kinglykrab 2024-03-09
|
||||
* Add RemoveAlternateCurrencyValue() to Perl/Lua ([#4190](https://github.com/EQEmu/Server/pull/4190)) @Kinglykrab 2024-03-17
|
||||
* Add Restore Methods for Health, Mana, and Endurance to Perl/Lua ([#4179](https://github.com/EQEmu/Server/pull/4179)) @Kinglykrab 2024-03-23
|
||||
* Add Silent Saylink Methods to Perl/Lua ([#4177](https://github.com/EQEmu/Server/pull/4177)) @Kinglykrab 2024-03-14
|
||||
|
||||
### Rules
|
||||
|
||||
* Add World:Rules Rule ([#4194](https://github.com/EQEmu/Server/pull/4194)) @Kinglykrab 2024-03-23
|
||||
|
||||
### Tradeskills
|
||||
|
||||
* Implement learning recipes from books ([#4170](https://github.com/EQEmu/Server/pull/4170)) @hgtw 2024-03-23
|
||||
|
||||
## [22.47.0] 3/5/2024
|
||||
|
||||
### Crash Fix
|
||||
|
||||
* 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
|
||||
|
||||
### Beacon
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include "../../common/content/world_content_service.h"
|
||||
#include "../../common/zone_store.h"
|
||||
#include "../../common/path_manager.h"
|
||||
#include "../../common/repositories/skill_caps_repository.h"
|
||||
#include "../../common/file.h"
|
||||
|
||||
EQEmuLogSys LogSys;
|
||||
WorldContentService content_service;
|
||||
@@ -164,81 +166,76 @@ void ExportSpells(SharedDatabase *db)
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
bool SkillUsable(SharedDatabase *db, int skill_id, int class_id)
|
||||
bool SkillUsable(SharedDatabase* db, int skill_id, int class_id)
|
||||
{
|
||||
|
||||
bool res = false;
|
||||
|
||||
std::string query = StringFormat(
|
||||
"SELECT max(cap) FROM skill_caps WHERE class=%d AND skillID=%d",
|
||||
class_id, skill_id
|
||||
const auto& l = SkillCapsRepository::GetWhere(
|
||||
*db,
|
||||
fmt::format(
|
||||
"`class_id` = {} AND `skill_id` = {} ORDER BY `cap` DESC LIMIT 1",
|
||||
class_id,
|
||||
skill_id
|
||||
)
|
||||
);
|
||||
auto results = db->QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (results.RowCount() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto row = results.begin();
|
||||
if (row[0] && Strings::ToInt(row[0]) > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return !l.empty();
|
||||
}
|
||||
|
||||
int GetSkill(SharedDatabase *db, int skill_id, int class_id, int level)
|
||||
uint32 GetSkill(SharedDatabase* db, int skill_id, int class_id, int level)
|
||||
{
|
||||
|
||||
std::string query = StringFormat(
|
||||
"SELECT cap FROM skill_caps WHERE class=%d AND skillID=%d AND level=%d",
|
||||
class_id, skill_id, level
|
||||
const auto& l = SkillCapsRepository::GetWhere(
|
||||
*db,
|
||||
fmt::format(
|
||||
"`class_id` = {} AND `skill_id` = {} AND `level` = {}",
|
||||
class_id,
|
||||
skill_id,
|
||||
level
|
||||
)
|
||||
);
|
||||
auto results = db->QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
|
||||
if (l.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (results.RowCount() == 0) {
|
||||
return 0;
|
||||
}
|
||||
auto e = l.front();
|
||||
|
||||
auto row = results.begin();
|
||||
return Strings::ToInt(row[0]);
|
||||
return e.cap;
|
||||
}
|
||||
|
||||
void ExportSkillCaps(SharedDatabase *db)
|
||||
void ExportSkillCaps(SharedDatabase* db)
|
||||
{
|
||||
LogInfo("Exporting Skill Caps");
|
||||
|
||||
std::string file = fmt::format("{}/export/SkillCaps.txt", path.GetServerPath());
|
||||
FILE *f = fopen(file.c_str(), "w");
|
||||
if (!f) {
|
||||
std::ofstream file(fmt::format("{}/export/SkillCaps.txt", path.GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/SkillCaps.txt to write, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int cl = 1; cl <= 16; ++cl) {
|
||||
for (int skill = 0; skill <= 77; ++skill) {
|
||||
if (SkillUsable(db, skill, cl)) {
|
||||
int previous_cap = 0;
|
||||
for (int level = 1; level <= 100; ++level) {
|
||||
int cap = GetSkill(db, skill, cl, level);
|
||||
const uint8 skill_cap_max_level = (
|
||||
RuleI(Character, SkillCapMaxLevel) > 0 ?
|
||||
RuleI(Character, SkillCapMaxLevel) :
|
||||
RuleI(Character, MaxLevel)
|
||||
);
|
||||
|
||||
for (uint8 class_id = Class::Warrior; class_id <= Class::Berserker; class_id++) {
|
||||
for (uint8 skill_id = EQ::skills::Skill1HBlunt; skill_id <= EQ::skills::Skill2HPiercing; skill_id++) {
|
||||
if (SkillUsable(db, skill_id, class_id)) {
|
||||
uint32 previous_cap = 0;
|
||||
for (uint8 level = 1; level <= skill_cap_max_level; level++) {
|
||||
uint32 cap = GetSkill(db, skill_id, class_id, level);
|
||||
if (cap < previous_cap) {
|
||||
cap = previous_cap;
|
||||
}
|
||||
|
||||
fprintf(f, "%d^%d^%d^%d^0\n", cl, skill, level, cap);
|
||||
file << fmt::format("{}^{}^{}^{}^0", class_id, skill_id, level, cap) << std::endl;
|
||||
|
||||
previous_cap = cap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void ExportBaseData(SharedDatabase *db)
|
||||
|
||||
+11
-9
@@ -83,6 +83,7 @@ SET(common_sources
|
||||
shared_tasks.cpp
|
||||
shareddb.cpp
|
||||
skills.cpp
|
||||
skill_caps.cpp
|
||||
spdat.cpp
|
||||
strings.cpp
|
||||
struct_strategy.cpp
|
||||
@@ -492,7 +493,7 @@ SET(repositories
|
||||
repositories/zone_repository.h
|
||||
repositories/zone_points_repository.h
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
SET(common_headers
|
||||
additive_lagged_fibonacci_engine.h
|
||||
@@ -592,7 +593,7 @@ SET(common_headers
|
||||
ptimer.h
|
||||
queue.h
|
||||
races.h
|
||||
raid.h
|
||||
raid.h
|
||||
random.h
|
||||
rdtsc.h
|
||||
rulesys.h
|
||||
@@ -606,6 +607,7 @@ SET(common_headers
|
||||
shared_tasks.h
|
||||
shareddb.h
|
||||
skills.h
|
||||
skill_caps.h
|
||||
spdat.h
|
||||
strings.h
|
||||
struct_strategy.h
|
||||
@@ -681,13 +683,13 @@ SOURCE_GROUP(Event FILES
|
||||
event/event_loop.h
|
||||
event/timer.h
|
||||
event/task.h
|
||||
)
|
||||
)
|
||||
|
||||
SOURCE_GROUP(Json FILES
|
||||
json/json.h
|
||||
json/jsoncpp.cpp
|
||||
json/json-forwards.h
|
||||
)
|
||||
)
|
||||
|
||||
SOURCE_GROUP(Net FILES
|
||||
net/console_server.cpp
|
||||
@@ -724,7 +726,7 @@ SOURCE_GROUP(Net FILES
|
||||
net/websocket_server.h
|
||||
net/websocket_server_connection.cpp
|
||||
net/websocket_server_connection.h
|
||||
)
|
||||
)
|
||||
|
||||
SOURCE_GROUP(Patches FILES
|
||||
patches/patches.h
|
||||
@@ -768,12 +770,12 @@ SOURCE_GROUP(Patches FILES
|
||||
patches/titanium_limits.cpp
|
||||
patches/uf.cpp
|
||||
patches/uf_limits.cpp
|
||||
)
|
||||
)
|
||||
|
||||
SOURCE_GROUP(StackWalker FILES
|
||||
StackWalker/StackWalker.h
|
||||
StackWalker/StackWalker.cpp
|
||||
)
|
||||
)
|
||||
|
||||
SOURCE_GROUP(Util FILES
|
||||
util/memory_stream.h
|
||||
@@ -781,7 +783,7 @@ SOURCE_GROUP(Util FILES
|
||||
util/directory.h
|
||||
util/uuid.cpp
|
||||
util/uuid.h
|
||||
)
|
||||
)
|
||||
|
||||
INCLUDE_DIRECTORIES(Patches SocketLib StackWalker)
|
||||
|
||||
@@ -794,6 +796,6 @@ ENDIF (UNIX)
|
||||
|
||||
IF (WIN32 AND EQEMU_BUILD_PCH)
|
||||
TARGET_PRECOMPILE_HEADERS(common PRIVATE pch/pch.h)
|
||||
ENDIF()
|
||||
ENDIF ()
|
||||
|
||||
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
|
||||
@@ -178,12 +178,13 @@ void WorldContentService::ReloadContentFlags()
|
||||
LogInfo(
|
||||
"Loaded content flag [{}] [{}]",
|
||||
f.flag_name,
|
||||
(f.enabled ? "Enabled" : "Disabled")
|
||||
(f.enabled ? "enabled" : "disabled")
|
||||
);
|
||||
}
|
||||
|
||||
SetContentFlags(set_content_flags);
|
||||
SetContentZones(ZoneRepository::All(*m_content_database));
|
||||
LoadZones();
|
||||
LoadStaticGlobalZoneInstances();
|
||||
}
|
||||
|
||||
Database *WorldContentService::GetDatabase() const
|
||||
@@ -235,19 +236,6 @@ void WorldContentService::SetContentFlag(const std::string &content_flag_name, b
|
||||
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
|
||||
//
|
||||
// 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
|
||||
// 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
|
||||
// example # 2
|
||||
void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
|
||||
{
|
||||
// if we're already in an instance, we don't want to route the player to another instance
|
||||
if (zc->instanceID > 0) {
|
||||
auto r = FindZone(zc->zoneID, zc->instanceID);
|
||||
if (r.zone_id == 0) {
|
||||
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) {
|
||||
if (z.zoneidnumber == zc->zoneID) {
|
||||
if (z.zoneidnumber == zone_id) {
|
||||
auto f = ContentFlags{
|
||||
.min_expansion = z.min_expansion,
|
||||
.max_expansion = z.max_expansion,
|
||||
@@ -286,33 +315,45 @@ void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
|
||||
z.long_name
|
||||
);
|
||||
|
||||
auto instances = InstanceListRepository::GetWhere(
|
||||
*GetDatabase(),
|
||||
fmt::format(
|
||||
"zone = {} AND version = {} AND never_expires = 1 AND is_global = 1",
|
||||
z.zoneidnumber,
|
||||
z.version
|
||||
)
|
||||
// first pass, explicit match on public static global zone instances
|
||||
for (auto &i: m_zone_instances) {
|
||||
if (i.zone == zone_id && i.version == z.version) {
|
||||
LogInfo(
|
||||
"Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
i.id,
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name,
|
||||
i.notes
|
||||
);
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
if (!instances.empty()) {
|
||||
auto instance = instances.front();
|
||||
zc->instanceID = instance.id;
|
||||
|
||||
LogInfo(
|
||||
"Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
instance.id,
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name,
|
||||
instance.notes
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
return WorldContentService::FindZoneResult{
|
||||
.zone_id = static_cast<uint32>(z.zoneidnumber),
|
||||
.instance = InstanceListRepository::NewEntity(),
|
||||
.zone = z
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return WorldContentService::FindZoneResult{.zone_id = 0};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
#include "../repositories/content_flags_repository.h"
|
||||
#include "../repositories/zone_repository.h"
|
||||
#include "../repositories/instance_list_repository.h"
|
||||
|
||||
class Database;
|
||||
|
||||
@@ -169,7 +170,14 @@ public:
|
||||
void SetContentFlag(const std::string &content_flag_name, bool enabled);
|
||||
|
||||
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:
|
||||
int current_expansion{};
|
||||
std::vector<ContentFlagsRepository::ContentFlags> content_flags;
|
||||
@@ -180,6 +188,9 @@ private:
|
||||
|
||||
// holds a record of the zone table from the database
|
||||
std::vector<ZoneRepository::Zone> m_zones = {};
|
||||
WorldContentService *LoadStaticGlobalZoneInstances();
|
||||
std::vector<InstanceListRepository::InstanceList> m_zone_instances;
|
||||
WorldContentService * LoadZones();
|
||||
};
|
||||
|
||||
extern WorldContentService content_service;
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
#if WINDOWS
|
||||
#ifdef _WINDOWS
|
||||
#define popen _popen
|
||||
#endif
|
||||
|
||||
|
||||
+1011
-1285
File diff suppressed because it is too large
Load Diff
+145
-139
@@ -18,8 +18,8 @@
|
||||
#ifndef EQEMU_DATABASE_H
|
||||
#define EQEMU_DATABASE_H
|
||||
|
||||
#define AUTHENTICATION_TIMEOUT 60
|
||||
#define INVALID_ID 0xFFFFFFFF
|
||||
#define AUTHENTICATION_TIMEOUT 60
|
||||
#define INVALID_ID 0xFFFFFFFF
|
||||
|
||||
#include "global_define.h"
|
||||
#include "eqemu_logsys.h"
|
||||
@@ -38,8 +38,7 @@
|
||||
class MySQLRequestResult;
|
||||
class Client;
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace EQ {
|
||||
class InventoryProfile;
|
||||
}
|
||||
|
||||
@@ -52,10 +51,11 @@ struct npcDecayTimes_Struct {
|
||||
|
||||
struct VarCache_Struct {
|
||||
std::map<std::string, std::string> m_cache;
|
||||
uint32 last_update;
|
||||
uint32 last_update;
|
||||
VarCache_Struct() : last_update(0) { }
|
||||
void Add(const std::string &key, const std::string &value) { m_cache[key] = value; }
|
||||
const std::string *Get(const std::string &key) {
|
||||
void Add(const std::string& key, const std::string& value) { m_cache[key] = value; }
|
||||
const std::string* Get(const std::string& key)
|
||||
{
|
||||
auto it = m_cache.find(key);
|
||||
return (it != m_cache.end() ? &it->second : nullptr);
|
||||
}
|
||||
@@ -76,37 +76,33 @@ class PTimerList;
|
||||
|
||||
#define SQL(...) #__VA_ARGS__
|
||||
|
||||
class LogSettings;
|
||||
class Database : public DBcore {
|
||||
public:
|
||||
Database();
|
||||
Database(const char* host, const char* user, const char* passwd, const char* database,uint32 port);
|
||||
bool Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port, std::string connection_label = "default");
|
||||
Database(
|
||||
const std::string& host,
|
||||
const std::string& user,
|
||||
const std::string& password,
|
||||
const std::string& database,
|
||||
uint32 port
|
||||
);
|
||||
bool Connect(
|
||||
const std::string& host,
|
||||
const std::string& user,
|
||||
const std::string& password,
|
||||
const std::string& database,
|
||||
uint32 port,
|
||||
std::string connection_label = "default"
|
||||
);
|
||||
~Database();
|
||||
|
||||
/* Character Creation */
|
||||
|
||||
bool CreateCharacter(
|
||||
uint32 account_id,
|
||||
char *name,
|
||||
uint16 gender,
|
||||
uint16 race,
|
||||
uint16 class_,
|
||||
uint8 str,
|
||||
uint8 sta,
|
||||
uint8 cha,
|
||||
uint8 dex,
|
||||
uint8 int_,
|
||||
uint8 agi,
|
||||
uint8 wis,
|
||||
uint8 face
|
||||
);
|
||||
bool DeleteCharacter(char *character_name);
|
||||
bool MoveCharacterToZone(const char *charname, uint32 zone_id);
|
||||
bool DeleteCharacter(const std::string& name);
|
||||
bool MoveCharacterToZone(const std::string& name, uint32 zone_id);
|
||||
bool MoveCharacterToZone(uint32 character_id, uint32 zone_id);
|
||||
bool ReserveName(uint32 account_id, char *name);
|
||||
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct *pp);
|
||||
bool UpdateName(const char *oldname, const char *newname);
|
||||
bool ReserveName(uint32 account_id, const std::string& name);
|
||||
bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp);
|
||||
bool UpdateName(const std::string& old_name, const std::string& new_name);
|
||||
bool CopyCharacter(
|
||||
const std::string& source_character_name,
|
||||
const std::string& destination_character_name,
|
||||
@@ -114,161 +110,171 @@ public:
|
||||
);
|
||||
|
||||
/* General Information Queries */
|
||||
bool AddBannedIP(const std::string& banned_ip, const std::string& notes); //Add IP address to the banned_ips table.
|
||||
bool AddToNameFilter(const std::string& name);
|
||||
bool CheckBannedIPs(const std::string& login_ip); //Check incoming connection against banned IP table.
|
||||
bool CheckGMIPs(const std::string& login_ip, uint32 account_id);
|
||||
bool CheckNameFilter(const std::string& name, bool surname = false);
|
||||
bool IsNameUsed(const std::string& name);
|
||||
|
||||
bool AddBannedIP(std::string banned_ip, std::string notes); //Add IP address to the banned_ips table.
|
||||
bool AddToNameFilter(std::string name);
|
||||
bool CheckBannedIPs(std::string login_ip); //Check incoming connection against banned IP table.
|
||||
bool CheckGMIPs(std::string login_ip, uint32 account_id);
|
||||
bool CheckNameFilter(std::string name, bool surname = false);
|
||||
bool CheckUsedName(std::string name);
|
||||
uint32 GetAccountIDByChar(const std::string& name, uint32* character_id = 0);
|
||||
uint32 GetAccountIDByChar(uint32 character_id);
|
||||
uint32 GetAccountIDByName(const std::string& account_name, const std::string& loginserver, int16* status = 0, uint32* lsid = 0);
|
||||
uint32 GetCharacterID(const std::string& name);
|
||||
uint32 GetGuildIDByCharID(uint32 character_id);
|
||||
uint32 GetGroupIDByCharID(uint32 character_id);
|
||||
uint32 GetRaidIDByCharID(uint32 character_id);
|
||||
|
||||
uint32 GetAccountIDByChar(const char* charname, uint32* oCharID = 0);
|
||||
uint32 GetAccountIDByChar(uint32 char_id);
|
||||
uint32 GetAccountIDByName(std::string account_name, std::string loginserver, int16* status = 0, uint32* lsid = 0);
|
||||
uint32 GetCharacterID(const std::string& name);
|
||||
uint32 GetCharacterInfo(std::string character_name, uint32 *account_id, uint32 *zone_id, uint32 *instance_id);
|
||||
uint32 GetGuildIDByCharID(uint32 char_id);
|
||||
uint32 GetGroupIDByCharID(uint32 char_id);
|
||||
uint32 GetRaidIDByCharID(uint32 char_id);
|
||||
|
||||
void GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID = 0);
|
||||
void GetCharName(uint32 char_id, char* name);
|
||||
std::string GetCharNameByID(uint32 char_id);
|
||||
std::string GetNPCNameByID(uint32 npc_id);
|
||||
std::string GetCleanNPCNameByID(uint32 npc_id);
|
||||
void LoginIP(uint32 account_id, std::string login_ip);
|
||||
const std::string GetAccountName(uint32 account_id, uint32* lsaccount_id = 0);
|
||||
const std::string GetCharName(uint32 character_id);
|
||||
const std::string GetCharNameByID(uint32 character_id);
|
||||
const std::string GetNPCNameByID(uint32 npc_id);
|
||||
const std::string GetCleanNPCNameByID(uint32 npc_id);
|
||||
void LoginIP(uint32 account_id, const std::string& login_ip);
|
||||
|
||||
/* Instancing */
|
||||
|
||||
bool AddClientToInstance(uint16 instance_id, uint32 character_id);
|
||||
bool CheckInstanceByCharID(uint16 instance_id, uint32 character_id);
|
||||
bool CheckInstanceExists(uint16 instance_id);
|
||||
bool CheckInstanceExpired(uint16 instance_id);
|
||||
bool CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration);
|
||||
bool GetUnusedInstanceID(uint16 &instance_id);
|
||||
bool GetUnusedInstanceID(uint16& instance_id);
|
||||
bool IsGlobalInstance(uint16 instance_id);
|
||||
bool RemoveClientFromInstance(uint16 instance_id, uint32 char_id);
|
||||
bool RemoveClientsFromInstance(uint16 instance_id);
|
||||
bool VerifyInstanceAlive(uint16 instance_id, uint32 character_id);
|
||||
bool VerifyZoneInstance(uint32 zone_id, uint16 instance_id);
|
||||
|
||||
uint16 GetInstanceID(uint32 zone, uint32 charid, int16 version);
|
||||
uint16 GetInstanceID(uint32 zone, uint32 character_id, int16 version);
|
||||
std::vector<uint16> GetInstanceIDs(uint32 zone_id, uint32 character_id);
|
||||
uint8_t GetInstanceVersion(uint16 instance_id);
|
||||
uint32 GetTimeRemainingInstance(uint16 instance_id, bool &is_perma);
|
||||
uint32 GetTimeRemainingInstance(uint16 instance_id, bool& is_perma);
|
||||
uint32 GetInstanceZoneID(uint16 instance_id);
|
||||
|
||||
void AssignGroupToInstance(uint32 gid, uint32 instance_id);
|
||||
void AssignRaidToInstance(uint32 rid, uint32 instance_id);
|
||||
void AssignGroupToInstance(uint32 group_id, uint32 instance_id);
|
||||
void AssignRaidToInstance(uint32 raid_id, uint32 instance_id);
|
||||
void DeleteInstance(uint16 instance_id);
|
||||
void FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 charid, uint32 group_id);
|
||||
void FlagInstanceByRaidLeader(uint32 zone_id, int16 version, uint32 charid, uint32 raid_id);
|
||||
void GetCharactersInInstance(uint16 instance_id, std::list<uint32> &character_ids);
|
||||
void FlagInstanceByGroupLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 group_id);
|
||||
void FlagInstanceByRaidLeader(uint32 zone_id, int16 version, uint32 character_id, uint32 raid_id);
|
||||
void GetCharactersInInstance(uint16 instance_id, std::list<uint32>& character_ids);
|
||||
void PurgeExpiredInstances();
|
||||
void SetInstanceDuration(uint16 instance_id, uint32 new_duration);
|
||||
void CleanupInstanceCorpses();
|
||||
|
||||
/* Adventure related. */
|
||||
|
||||
void UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win = false, bool remove = false);
|
||||
bool GetAdventureStats(uint32 char_id, AdventureStats_Struct *as);
|
||||
void UpdateAdventureStatsEntry(uint32 character_id, uint8 theme_id, bool is_win = false, bool is_remove = false);
|
||||
bool GetAdventureStats(uint32 character_id, AdventureStats_Struct* as);
|
||||
|
||||
/* Account Related */
|
||||
const std::string GetLiveChar(uint32 account_id);
|
||||
bool SetAccountStatus(const std::string& account_name, int16 status);
|
||||
bool SetLocalPassword(uint32 account_id, const std::string& password);
|
||||
bool UpdateLiveChar(const std::string& name, uint32 account_id);
|
||||
int16 CheckStatus(uint32 account_id);
|
||||
void SetAccountCRCField(uint32 account_id, const std::string& field_name, uint64 checksum);
|
||||
uint32 CheckLogin(const std::string& name, const std::string& password, const std::string& loginserver, int16* status = 0);
|
||||
uint32 CreateAccount(
|
||||
const std::string& name,
|
||||
const std::string& password,
|
||||
int16 status,
|
||||
const std::string& loginserver,
|
||||
uint32 lsaccount_id
|
||||
);
|
||||
uint32 GetAccountIDFromLSID(
|
||||
const std::string& in_loginserver_id,
|
||||
uint32 in_loginserver_account_id,
|
||||
char* in_account_name = 0,
|
||||
int16* in_status = 0
|
||||
);
|
||||
|
||||
bool DeleteAccount(const char *name, const char* loginserver);
|
||||
bool GetLiveChar(uint32 account_id, char* cname);
|
||||
bool SetAccountStatus(const char* name, int16 status);
|
||||
bool SetAccountStatus(const std::string& account_name, int16 status);
|
||||
bool SetLocalPassword(uint32 accid, const char* password);
|
||||
bool UpdateLiveChar(char* charname, uint32 account_id);
|
||||
uint8 GetAgreementFlag(uint32 account_id);
|
||||
void SetAgreementFlag(uint32 account_id);
|
||||
|
||||
int16 CheckStatus(uint32 account_id);
|
||||
int GetIPExemption(const std::string& account_ip);
|
||||
void SetIPExemption(const std::string& account_ip, int exemption_amount);
|
||||
|
||||
void SetAccountCRCField(uint32 account_id, std::string field_name, uint64 checksum);
|
||||
|
||||
uint32 CheckLogin(const char* name, const char* password, const char *loginserver, int16* oStatus = 0);
|
||||
uint32 CreateAccount(const char* name, const char* password, int16 status, const char* loginserver, uint32 lsaccount_id);
|
||||
uint32 GetAccountIDFromLSID(const std::string& in_loginserver_id, uint32 in_loginserver_account_id, char* in_account_name = 0, int16* in_status = 0);
|
||||
uint8 GetAgreementFlag(uint32 account_id);
|
||||
|
||||
void GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus);
|
||||
void SetAgreementFlag(uint32 account_id);
|
||||
|
||||
int GetIPExemption(std::string account_ip);
|
||||
void SetIPExemption(std::string account_ip, int exemption_amount);
|
||||
|
||||
int GetInstanceID(uint32 char_id, uint32 zone_id);
|
||||
int GetInstanceID(uint32 character_id, uint32 zone_id);
|
||||
|
||||
|
||||
/* Groups */
|
||||
|
||||
std::string GetGroupLeaderForLogin(std::string character_name);
|
||||
char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr);
|
||||
|
||||
uint32 GetGroupID(const char* name);
|
||||
|
||||
void ClearGroup(uint32 gid = 0);
|
||||
void ClearGroupLeader(uint32 gid = 0);
|
||||
void SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ismerc = false);
|
||||
void SetGroupLeaderName(uint32 gid, const char* name);
|
||||
std::string GetGroupLeaderForLogin(const std::string& character_name);
|
||||
char* GetGroupLeadershipInfo(
|
||||
uint32 group_id,
|
||||
char* leaderbuf,
|
||||
char* maintank = nullptr,
|
||||
char* assist = nullptr,
|
||||
char* puller = nullptr,
|
||||
char* marknpc = nullptr,
|
||||
char* mentoree = nullptr,
|
||||
int* mentor_percent = nullptr,
|
||||
GroupLeadershipAA_Struct* GLAA = nullptr
|
||||
);
|
||||
std::string GetGroupLeaderName(uint32 group_id);
|
||||
uint32 GetGroupID(const std::string& name);
|
||||
void ClearGroup(uint32 group_id = 0);
|
||||
void ClearGroupLeader(uint32 group_id = 0);
|
||||
void SetGroupLeaderName(uint32 group_id, const std::string& name);
|
||||
|
||||
/* Raids */
|
||||
const std::string GetRaidLeaderName(uint32 raid_id);
|
||||
uint32 GetRaidID(const std::string& name);
|
||||
void ClearRaid(uint32 raid_id = 0);
|
||||
void ClearRaidDetails(uint32 raid_id = 0);
|
||||
void ClearRaidLeader(uint32 group_id = std::numeric_limits<uint32>::max(), uint32 raid_id = 0);
|
||||
void GetGroupLeadershipInfo(
|
||||
uint32 group_id,
|
||||
uint32 raid_id,
|
||||
char* maintank = nullptr,
|
||||
char* assist = nullptr,
|
||||
char* puller = nullptr,
|
||||
char* marknpc = nullptr,
|
||||
char* mentoree = nullptr,
|
||||
int* mentor_percent = nullptr,
|
||||
GroupLeadershipAA_Struct* GLAA = nullptr
|
||||
);
|
||||
void GetRaidLeadershipInfo(
|
||||
uint32 raid_id,
|
||||
char* maintank = nullptr,
|
||||
char* assist = nullptr,
|
||||
char* puller = nullptr,
|
||||
char* marknpc = nullptr,
|
||||
RaidLeadershipAA_Struct* RLAA = nullptr
|
||||
);
|
||||
void SetRaidGroupLeaderInfo(uint32 group_id, uint32 raid_id);
|
||||
|
||||
const char *GetRaidLeaderName(uint32 rid);
|
||||
|
||||
uint32 GetRaidID(const char* name);
|
||||
|
||||
void ClearRaid(uint32 rid = 0);
|
||||
void ClearRaidDetails(uint32 rid = 0);
|
||||
void ClearRaidLeader(uint32 gid = 0xFFFFFFFF, uint32 rid = 0);
|
||||
void GetGroupLeadershipInfo(uint32 gid, uint32 rid, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr);
|
||||
void GetRaidLeadershipInfo(uint32 rid, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, RaidLeadershipAA_Struct* RLAA = nullptr);
|
||||
void SetRaidGroupLeaderInfo(uint32 gid, uint32 rid);
|
||||
|
||||
void PurgeAllDeletedDataBuckets();
|
||||
void PurgeAllDeletedDataBuckets();
|
||||
|
||||
|
||||
/* Database Variables */
|
||||
bool GetVariable(const std::string& name, std::string& value);
|
||||
bool SetVariable(const std::string& name, const std::string& value);
|
||||
bool LoadVariables();
|
||||
|
||||
bool GetVariable(std::string varname, std::string &varvalue);
|
||||
bool SetVariable(const std::string& varname, const std::string &varvalue);
|
||||
bool LoadVariables();
|
||||
uint8 GetPEQZone(uint32 zone_id, uint32 version);
|
||||
uint32 GetServerType();
|
||||
void AddReport(const std::string& who, const std::string& against, const std::string& lines);
|
||||
struct TimeOfDay_Struct LoadTime(time_t& realtime);
|
||||
bool SaveTime(int8 minute, int8 hour, int8 day, int8 month, int16 year);
|
||||
void ClearMerchantTemp();
|
||||
void ClearPTimers(uint32 character_id);
|
||||
void SetFirstLogon(uint32 character_id, uint8 first_logon);
|
||||
void SetLFG(uint32 character_id, bool is_lfg);
|
||||
void SetLFP(uint32 character_id, bool is_lfp);
|
||||
void SetLoginFlags(uint32 character_id, bool is_lfp, bool is_lfg, uint8 first_logon);
|
||||
|
||||
/* General Queries */
|
||||
int64 CountInvSnapshots();
|
||||
void ClearInvSnapshots(bool from_now = false);
|
||||
|
||||
bool GetZoneGraveyard(const uint32 graveyard_id, uint32* graveyard_zoneid = 0, float* graveyard_x = 0, float* graveyard_y = 0, float* graveyard_z = 0, float* graveyard_heading = 0);
|
||||
bool LoadPTimers(uint32 charid, PTimerList &into);
|
||||
|
||||
uint8 GetPEQZone(uint32 zone_id, uint32 version);
|
||||
uint8 GetMinStatus(uint32 zone_id, uint32 instance_version);
|
||||
uint8 GetRaceSkill(uint8 skillid, uint8 in_race);
|
||||
uint8 GetServerType();
|
||||
uint8 GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 in_level);
|
||||
|
||||
void AddReport(std::string who, std::string against, std::string lines);
|
||||
struct TimeOfDay_Struct LoadTime(time_t &realtime);
|
||||
bool SaveTime(int8 minute, int8 hour, int8 day, int8 month, int16 year);
|
||||
void ClearMerchantTemp();
|
||||
void ClearPTimers(uint32 charid);
|
||||
void SetFirstLogon(uint32 CharID, uint8 firstlogon);
|
||||
void SetLFG(uint32 CharID, bool LFG);
|
||||
void SetLFP(uint32 CharID, bool LFP);
|
||||
void SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon);
|
||||
|
||||
int CountInvSnapshots();
|
||||
void ClearInvSnapshots(bool from_now = false);
|
||||
|
||||
void SourceDatabaseTableFromUrl(std::string table_name, std::string url);
|
||||
void SourceSqlFromUrl(std::string url);
|
||||
void SourceDatabaseTableFromUrl(const std::string& table_name, const std::string& url);
|
||||
void SourceSqlFromUrl(const std::string& url);
|
||||
|
||||
private:
|
||||
|
||||
Mutex Mvarcache;
|
||||
Mutex Mvarcache;
|
||||
VarCache_Struct varcache;
|
||||
|
||||
/* Groups, utility methods. */
|
||||
void ClearAllGroupLeaders();
|
||||
void ClearAllGroups();
|
||||
void ClearAllGroupLeaders();
|
||||
void ClearAllGroups();
|
||||
|
||||
/* Raid, utility methods. */
|
||||
void ClearAllRaids();
|
||||
|
||||
@@ -575,7 +575,12 @@ void DatabaseDumpService::RemoveSqlBackup()
|
||||
{
|
||||
std::string file = fmt::format("{}.sql", GetDumpFileNameWithPath());
|
||||
if (File::Exists(file)) {
|
||||
std::filesystem::remove(file);
|
||||
try {
|
||||
std::filesystem::remove(file);
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
LogError("std::filesystem::remove err [{}]", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
RemoveCredentialsFile();
|
||||
|
||||
@@ -5410,6 +5410,65 @@ 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`;
|
||||
)",
|
||||
.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 NULL DEFAULT NULL AFTER `rule_name`;
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9267,
|
||||
.description = "2024_02_18_group_id_bot_id.sql",
|
||||
.check = "SHOW COLUMNS FROM `group_id` LIKE 'bot_id'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `group_id`
|
||||
CHANGE COLUMN `groupid` `group_id` int(11) UNSIGNED NOT NULL DEFAULT 0 FIRST,
|
||||
CHANGE COLUMN `charid` `character_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `group_id`,
|
||||
CHANGE COLUMN `ismerc` `merc_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `name`,
|
||||
ADD COLUMN `bot_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `character_id`,
|
||||
MODIFY COLUMN `name` varchar(64) NOT NULL DEFAULT '' AFTER `character_id`,
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`group_id`, `character_id`, `bot_id`, `merc_id`) USING BTREE;
|
||||
ALTER TABLE `group_id`
|
||||
MODIFY COLUMN `character_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `name`;
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9268,
|
||||
.description = "2024_03_23_skill_caps.sql",
|
||||
.check = "SHOW COLUMNS FROM `skill_caps` LIKE 'skill_id'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `skill_caps`
|
||||
CHANGE COLUMN `skillID` `skill_id` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 FIRST,
|
||||
CHANGE COLUMN `class` `class_id` tinyint(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `skill_id`,
|
||||
ADD COLUMN `id` int(3) UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`) USING BTREE,
|
||||
ADD INDEX `level_skill_cap`(`skill_id`, `class_id`, `level`, `cap`);
|
||||
)",
|
||||
.content_schema_update = true,
|
||||
}
|
||||
// -- template; copy/paste this when you need to create a new entry
|
||||
// ManifestEntry{
|
||||
|
||||
@@ -421,20 +421,25 @@ void Database::AssignGroupToInstance(uint32 group_id, uint32 instance_id)
|
||||
auto zone_id = GetInstanceZoneID(instance_id);
|
||||
auto version = GetInstanceVersion(instance_id);
|
||||
|
||||
auto l = GroupIdRepository::GetWhere(
|
||||
const auto& l = GroupIdRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"groupid = {}",
|
||||
"`group_id` = {}",
|
||||
group_id
|
||||
)
|
||||
);
|
||||
|
||||
if (l.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& e : l) {
|
||||
if (!GetInstanceID(zone_id, e.charid, version)) {
|
||||
AddClientToInstance(instance_id, e.charid);
|
||||
if (!e.character_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!GetInstanceID(zone_id, e.character_id, version)) {
|
||||
AddClientToInstance(instance_id, e.character_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -504,7 +509,7 @@ void Database::FlagInstanceByRaidLeader(uint32 zone_id, int16 version, uint32 ch
|
||||
return;
|
||||
}
|
||||
|
||||
auto raid_leader_id = GetCharacterID(GetRaidLeaderName(raid_id));
|
||||
auto raid_leader_id = GetCharacterID(GetRaidLeaderName(raid_id).c_str());
|
||||
auto raid_leader_instance_id = GetInstanceID(zone_id, raid_leader_id, version);
|
||||
|
||||
if (!raid_leader_instance_id) {
|
||||
|
||||
@@ -698,6 +698,8 @@ const std::map<uint32, std::string>& EQ::constants::GetSpecialAbilityMap()
|
||||
{ IMMUNE_OPEN, "Immune to Open" },
|
||||
{ IMMUNE_ASSASSINATE, "Immune to Assassinate" },
|
||||
{ IMMUNE_HEADSHOT, "Immune to Headshot" },
|
||||
{ IMMUNE_AGGRO_BOT, "Immune to Bot Aggro" },
|
||||
{ IMMUNE_DAMAGE_BOT, "Immune to Bot Damage" },
|
||||
};
|
||||
|
||||
return special_ability_map;
|
||||
|
||||
@@ -89,6 +89,8 @@ namespace EQ
|
||||
using RoF2::invslot::SLOT_INVALID;
|
||||
using RoF2::invslot::SLOT_BEGIN;
|
||||
|
||||
using RoF2::invslot::SLOT_QUEST;
|
||||
|
||||
using Titanium::invslot::SLOT_TRADESKILL_EXPERIMENT_COMBINE;
|
||||
|
||||
const int16 SLOT_AUGMENT_GENERIC_RETURN = 1001; // clients don't appear to use this method... (internal inventory return value)
|
||||
@@ -656,7 +658,9 @@ enum {
|
||||
IMMUNE_OPEN = 53,
|
||||
IMMUNE_ASSASSINATE = 54,
|
||||
IMMUNE_HEADSHOT = 55,
|
||||
MAX_SPECIAL_ATTACK = 56
|
||||
IMMUNE_AGGRO_BOT = 56,
|
||||
IMMUNE_DAMAGE_BOT = 57,
|
||||
MAX_SPECIAL_ATTACK = 58
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -130,6 +130,11 @@ enum CrystalReclaimTypes
|
||||
Radiant = 4,
|
||||
};
|
||||
|
||||
namespace ItemStackSizeConstraint {
|
||||
constexpr int16 Minimum = 1;
|
||||
constexpr int16 Maximum = 1000;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@ namespace Logs {
|
||||
Zoning,
|
||||
EqTime,
|
||||
Corpses,
|
||||
XTargets,
|
||||
MaxCategoryID /* Don't Remove this */
|
||||
};
|
||||
|
||||
@@ -241,6 +242,7 @@ namespace Logs {
|
||||
"Zoning",
|
||||
"EqTime",
|
||||
"Corpses",
|
||||
"XTargets"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -834,6 +834,16 @@
|
||||
OutF(LogSys, Logs::Detail, Logs::Corpses, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
} 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 {\
|
||||
if (LogSys.IsLogEnabled(debug_level, log_category))\
|
||||
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
|
||||
|
||||
@@ -82,6 +82,10 @@ Zone extensions and features
|
||||
#define QUEST_GLOBAL_DIRECTORY "global"
|
||||
#endif
|
||||
|
||||
// Number of quest items a Quest NPC can hold
|
||||
#define MAX_NPC_QUEST_INVENTORY 24
|
||||
|
||||
|
||||
//the min ratio at which a mob's speed is reduced
|
||||
#define FLEE_HP_MINSPEED 22
|
||||
//number of tics to try to run straight away before looking again
|
||||
@@ -111,6 +115,7 @@ Zone extensions and features
|
||||
|
||||
#define SKILL_MAX_LEVEL 75
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Zone Numerical configuration
|
||||
|
||||
+7
-34
@@ -342,41 +342,14 @@ bool BaseGuildManager::_StoreGuildDB(uint32 guild_id)
|
||||
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 guild_id = UpdateDbCreateGuild(name, leader_char_id);
|
||||
if (guild_id == GUILD_NONE) {
|
||||
return (GUILD_NONE);
|
||||
}
|
||||
//RefreshGuild(guild_id);
|
||||
//SendGuildRefresh(guild_id, true, false, false, false);
|
||||
//SendCharRefresh(GUILD_NONE, guild_id, leader_char_id);
|
||||
uint32 guild_id = UpdateDbCreateGuild(name, leader_char_id);
|
||||
if (guild_id == GUILD_NONE) {
|
||||
return (GUILD_NONE);
|
||||
}
|
||||
|
||||
return guild_id;
|
||||
return guild_id;
|
||||
}
|
||||
|
||||
bool BaseGuildManager::DeleteGuild(uint32 guild_id)
|
||||
@@ -539,8 +512,8 @@ bool BaseGuildManager::SetPublicNote(uint32 charid, std::string public_note)
|
||||
|
||||
uint32 BaseGuildManager::UpdateDbCreateGuild(std::string name, uint32 leader)
|
||||
{
|
||||
auto new_id = _GetFreeGuildID();
|
||||
if (new_id == GUILD_NONE) {
|
||||
auto new_id = GuildsRepository::GetMaxId(*m_db) + 1;
|
||||
if (!new_id) {
|
||||
return GUILD_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -208,7 +208,6 @@ class BaseGuildManager
|
||||
|
||||
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);
|
||||
uint32 _GetFreeGuildID();
|
||||
GuildsRepository::Guilds CreateGuildRepoFromGuildInfo(uint32 guild_id, BaseGuildManager::GuildInfo& in);
|
||||
};
|
||||
#endif /*GUILD_BASE_H_*/
|
||||
|
||||
@@ -25,6 +25,8 @@ struct LootItem {
|
||||
uint16 trivial_max_level;
|
||||
uint16 npc_min_level;
|
||||
uint16 npc_max_level;
|
||||
uint8 quest;
|
||||
uint8 pet;
|
||||
};
|
||||
|
||||
typedef std::list<LootItem*> LootItems;
|
||||
|
||||
@@ -165,6 +165,8 @@ namespace RoF2
|
||||
const int16 SLOT_INVALID = IINVALID;
|
||||
const int16 SLOT_BEGIN = INULL;
|
||||
|
||||
const int16 SLOT_QUEST = 9999;
|
||||
|
||||
const int16 POSSESSIONS_BEGIN = slotCharm;
|
||||
const int16 POSSESSIONS_END = slotCursor;
|
||||
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
|
||||
|
||||
@@ -162,6 +162,8 @@ namespace RoF
|
||||
const int16 SLOT_INVALID = IINVALID;
|
||||
const int16 SLOT_BEGIN = INULL;
|
||||
|
||||
const int16 SLOT_QUEST = 9999;
|
||||
|
||||
const int16 POSSESSIONS_BEGIN = slotCharm;
|
||||
const int16 POSSESSIONS_END = slotCursor;
|
||||
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
|
||||
|
||||
@@ -152,6 +152,8 @@ namespace SoD
|
||||
|
||||
const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000;
|
||||
|
||||
const int16 SLOT_QUEST = 9999;
|
||||
|
||||
const int16 POSSESSIONS_BEGIN = slotCharm;
|
||||
const int16 POSSESSIONS_END = slotCursor;
|
||||
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
|
||||
|
||||
@@ -152,6 +152,8 @@ namespace SoF
|
||||
|
||||
const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000;
|
||||
|
||||
const int16 SLOT_QUEST = 9999;
|
||||
|
||||
const int16 POSSESSIONS_BEGIN = slotCharm;
|
||||
const int16 POSSESSIONS_END = slotCursor;
|
||||
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
|
||||
|
||||
@@ -151,6 +151,8 @@ namespace Titanium
|
||||
|
||||
const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000;
|
||||
|
||||
const int16 SLOT_QUEST = 9999;
|
||||
|
||||
const int16 POSSESSIONS_BEGIN = slotCharm;
|
||||
const int16 POSSESSIONS_END = slotCursor;
|
||||
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
|
||||
|
||||
@@ -152,6 +152,8 @@ namespace UF
|
||||
|
||||
const int16 SLOT_TRADESKILL_EXPERIMENT_COMBINE = 1000;
|
||||
|
||||
const int16 SLOT_QUEST = 9999;
|
||||
|
||||
const int16 POSSESSIONS_BEGIN = slotCharm;
|
||||
const int16 POSSESSIONS_END = slotCursor;
|
||||
const int16 POSSESSIONS_COUNT = (POSSESSIONS_END - POSSESSIONS_BEGIN) + 1;
|
||||
|
||||
+1
-1
@@ -1379,7 +1379,7 @@ uint32 GetPlayerRaceValue(uint16 race_id) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32 GetPlayerRaceBit(uint16 race_id) {
|
||||
uint16 GetPlayerRaceBit(uint16 race_id) {
|
||||
switch (race_id) {
|
||||
case HUMAN:
|
||||
return PLAYER_RACE_HUMAN_BIT;
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ bool IsPlayerRace(uint16 race_id);
|
||||
const std::string GetPlayerRaceAbbreviation(uint16 race_id);
|
||||
|
||||
uint32 GetPlayerRaceValue(uint16 race_id);
|
||||
uint32 GetPlayerRaceBit(uint16 race_id);
|
||||
uint16 GetPlayerRaceBit(uint16 race_id);
|
||||
|
||||
uint16 GetRaceIDFromPlayerRaceValue(uint32 player_race_value);
|
||||
uint16 GetRaceIDFromPlayerRaceBit(uint32 player_race_bit);
|
||||
|
||||
@@ -44,7 +44,51 @@ public:
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
static int16 GetAccountStatus(Database& db, const uint32 account_id)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"SELECT `status`, TIMESTAMPDIFF(SECOND, NOW(), `suspendeduntil`) FROM `{}` WHERE `{}` = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
account_id
|
||||
)
|
||||
);
|
||||
|
||||
if (!results.Success() || !results.RowCount()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto row = results.begin();
|
||||
|
||||
int16 status = static_cast<int16>(Strings::ToInt(row[0]));
|
||||
int date_diff = 0;
|
||||
|
||||
if (row[1]) {
|
||||
date_diff = Strings::ToInt(row[1]);
|
||||
}
|
||||
|
||||
if (date_diff > 0) {
|
||||
status = -1;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool UpdatePassword(Database& db, const uint32 account_id, const std::string& password)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE `{}` SET `password` = MD5('{}') WHERE `{}` = {}",
|
||||
TableName(),
|
||||
password,
|
||||
PrimaryKey(),
|
||||
account_id
|
||||
)
|
||||
);
|
||||
|
||||
return results.Success();
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_ACCOUNT_REPOSITORY_H
|
||||
|
||||
@@ -44,7 +44,65 @@ public:
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
static void UpdateAdventureStatsEntry(Database& db, uint32 character_id, uint8 theme_id, bool is_win, bool is_remove)
|
||||
{
|
||||
std::string field;
|
||||
|
||||
switch (theme_id) {
|
||||
case LDoNThemes::GUK: {
|
||||
field = "guk_";
|
||||
break;
|
||||
}
|
||||
case LDoNThemes::MIR: {
|
||||
field = "mir_";
|
||||
break;
|
||||
}
|
||||
case LDoNThemes::MMC: {
|
||||
field = "mmc_";
|
||||
break;
|
||||
}
|
||||
case LDoNThemes::RUJ: {
|
||||
field = "ruj_";
|
||||
break;
|
||||
}
|
||||
case LDoNThemes::TAK: {
|
||||
field = "tak_";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
field += is_win ? "wins" : "losses";
|
||||
|
||||
auto e = FindOne(db, character_id);
|
||||
|
||||
if (!e.player_id && !is_remove) {
|
||||
const std::string& query = fmt::format(
|
||||
"INSERT INTO `{}` SET `{}` = 1, `{}` = {}",
|
||||
TableName(),
|
||||
field,
|
||||
PrimaryKey(),
|
||||
character_id
|
||||
);
|
||||
|
||||
db.QueryDatabase(query);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string& field_operation = is_remove ? "-" : "+";
|
||||
|
||||
const std::string& query = fmt::format(
|
||||
"UPDATE `{}` SET `{}` = {} {} 1 WHERE `{}` = {}",
|
||||
TableName(),
|
||||
field,
|
||||
field,
|
||||
field_operation,
|
||||
PrimaryKey(),
|
||||
character_id
|
||||
);
|
||||
|
||||
db.QueryDatabase(query);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_ADVENTURE_STATS_REPOSITORY_H
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
int32_t accid;
|
||||
std::string ip;
|
||||
int32_t count;
|
||||
std::string lastused;
|
||||
time_t lastused;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
"accid",
|
||||
"ip",
|
||||
"count",
|
||||
"lastused",
|
||||
"UNIX_TIMESTAMP(lastused)",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
e.accid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.ip = row[1] ? row[1] : "";
|
||||
e.count = row[2] ? static_cast<int32_t>(atoi(row[2])) : 1;
|
||||
e.lastused = row[3] ? row[3] : std::time(nullptr);
|
||||
e.lastused = strtoll(row[3] ? row[3] : "-1", nullptr, 10);
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -167,7 +167,7 @@ public:
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.accid));
|
||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.ip) + "'");
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.count));
|
||||
v.push_back(columns[3] + " = '" + Strings::Escape(e.lastused) + "'");
|
||||
v.push_back(columns[3] + " = FROM_UNIXTIME(" + (e.lastused > 0 ? std::to_string(e.lastused) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -192,7 +192,7 @@ public:
|
||||
v.push_back(std::to_string(e.accid));
|
||||
v.push_back("'" + Strings::Escape(e.ip) + "'");
|
||||
v.push_back(std::to_string(e.count));
|
||||
v.push_back("'" + Strings::Escape(e.lastused) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.lastused > 0 ? std::to_string(e.lastused) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -225,7 +225,7 @@ public:
|
||||
v.push_back(std::to_string(e.accid));
|
||||
v.push_back("'" + Strings::Escape(e.ip) + "'");
|
||||
v.push_back(std::to_string(e.count));
|
||||
v.push_back("'" + Strings::Escape(e.lastused) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.lastused > 0 ? std::to_string(e.lastused) : "null") + ")");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -262,7 +262,7 @@ public:
|
||||
e.accid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.ip = row[1] ? row[1] : "";
|
||||
e.count = row[2] ? static_cast<int32_t>(atoi(row[2])) : 1;
|
||||
e.lastused = row[3] ? row[3] : std::time(nullptr);
|
||||
e.lastused = strtoll(row[3] ? row[3] : "-1", nullptr, 10);
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -290,7 +290,7 @@ public:
|
||||
e.accid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.ip = row[1] ? row[1] : "";
|
||||
e.count = row[2] ? static_cast<int32_t>(atoi(row[2])) : 1;
|
||||
e.lastused = row[3] ? row[3] : std::time(nullptr);
|
||||
e.lastused = strtoll(row[3] ? row[3] : "-1", nullptr, 10);
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -368,7 +368,7 @@ public:
|
||||
v.push_back(std::to_string(e.accid));
|
||||
v.push_back("'" + Strings::Escape(e.ip) + "'");
|
||||
v.push_back(std::to_string(e.count));
|
||||
v.push_back("'" + Strings::Escape(e.lastused) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.lastused > 0 ? std::to_string(e.lastused) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -394,7 +394,7 @@ public:
|
||||
v.push_back(std::to_string(e.accid));
|
||||
v.push_back("'" + Strings::Escape(e.ip) + "'");
|
||||
v.push_back(std::to_string(e.count));
|
||||
v.push_back("'" + Strings::Escape(e.lastused) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.lastused > 0 ? std::to_string(e.lastused) : "null") + ")");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -19,34 +19,37 @@
|
||||
class BaseGroupIdRepository {
|
||||
public:
|
||||
struct GroupId {
|
||||
int32_t groupid;
|
||||
int32_t charid;
|
||||
uint32_t group_id;
|
||||
std::string name;
|
||||
int8_t ismerc;
|
||||
uint32_t character_id;
|
||||
uint32_t bot_id;
|
||||
uint32_t merc_id;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("groupid");
|
||||
return std::string("group_id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"groupid",
|
||||
"charid",
|
||||
"group_id",
|
||||
"name",
|
||||
"ismerc",
|
||||
"character_id",
|
||||
"bot_id",
|
||||
"merc_id",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"groupid",
|
||||
"charid",
|
||||
"group_id",
|
||||
"name",
|
||||
"ismerc",
|
||||
"character_id",
|
||||
"bot_id",
|
||||
"merc_id",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -87,10 +90,11 @@ public:
|
||||
{
|
||||
GroupId e{};
|
||||
|
||||
e.groupid = 0;
|
||||
e.charid = 0;
|
||||
e.name = "";
|
||||
e.ismerc = 0;
|
||||
e.group_id = 0;
|
||||
e.name = "";
|
||||
e.character_id = 0;
|
||||
e.bot_id = 0;
|
||||
e.merc_id = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -101,7 +105,7 @@ public:
|
||||
)
|
||||
{
|
||||
for (auto &group_id : group_ids) {
|
||||
if (group_id.groupid == group_id_id) {
|
||||
if (group_id.group_id == group_id_id) {
|
||||
return group_id;
|
||||
}
|
||||
}
|
||||
@@ -127,10 +131,11 @@ public:
|
||||
if (results.RowCount() == 1) {
|
||||
GroupId e{};
|
||||
|
||||
e.groupid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.charid = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.name = row[2] ? row[2] : "";
|
||||
e.ismerc = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||
e.group_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.name = row[1] ? row[1] : "";
|
||||
e.character_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.bot_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.merc_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -164,10 +169,11 @@ public:
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.groupid));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.charid));
|
||||
v.push_back(columns[2] + " = '" + Strings::Escape(e.name) + "'");
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.ismerc));
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.group_id));
|
||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.name) + "'");
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.character_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.bot_id));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.merc_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -175,7 +181,7 @@ public:
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.groupid
|
||||
e.group_id
|
||||
)
|
||||
);
|
||||
|
||||
@@ -189,10 +195,11 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.groupid));
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.group_id));
|
||||
v.push_back("'" + Strings::Escape(e.name) + "'");
|
||||
v.push_back(std::to_string(e.ismerc));
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.merc_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -203,7 +210,7 @@ public:
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.groupid = results.LastInsertedID();
|
||||
e.group_id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -222,10 +229,11 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.groupid));
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.group_id));
|
||||
v.push_back("'" + Strings::Escape(e.name) + "'");
|
||||
v.push_back(std::to_string(e.ismerc));
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.merc_id));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -259,10 +267,11 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
GroupId e{};
|
||||
|
||||
e.groupid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.charid = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.name = row[2] ? row[2] : "";
|
||||
e.ismerc = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||
e.group_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.name = row[1] ? row[1] : "";
|
||||
e.character_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.bot_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.merc_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -287,10 +296,11 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
GroupId e{};
|
||||
|
||||
e.groupid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.charid = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.name = row[2] ? row[2] : "";
|
||||
e.ismerc = row[3] ? static_cast<int8_t>(atoi(row[3])) : 0;
|
||||
e.group_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.name = row[1] ? row[1] : "";
|
||||
e.character_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.bot_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.merc_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -365,10 +375,11 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.groupid));
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.group_id));
|
||||
v.push_back("'" + Strings::Escape(e.name) + "'");
|
||||
v.push_back(std::to_string(e.ismerc));
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.merc_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -391,10 +402,11 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.groupid));
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.group_id));
|
||||
v.push_back("'" + Strings::Escape(e.name) + "'");
|
||||
v.push_back(std::to_string(e.ismerc));
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.merc_id));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ public:
|
||||
e.gid = 0;
|
||||
e.leadername = "";
|
||||
e.marknpc = "";
|
||||
e.leadershipaa = 0;
|
||||
e.leadershipaa = "";
|
||||
e.maintank = "";
|
||||
e.assist = "";
|
||||
e.puller = "";
|
||||
@@ -150,7 +150,7 @@ public:
|
||||
e.gid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.leadername = row[1] ? row[1] : "";
|
||||
e.marknpc = row[2] ? row[2] : "";
|
||||
e.leadershipaa = row[3] ? row[3] : 0;
|
||||
e.leadershipaa = row[3] ? row[3] : "";
|
||||
e.maintank = row[4] ? row[4] : "";
|
||||
e.assist = row[5] ? row[5] : "";
|
||||
e.puller = row[6] ? row[6] : "";
|
||||
@@ -302,7 +302,7 @@ public:
|
||||
e.gid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.leadername = row[1] ? row[1] : "";
|
||||
e.marknpc = row[2] ? row[2] : "";
|
||||
e.leadershipaa = row[3] ? row[3] : 0;
|
||||
e.leadershipaa = row[3] ? row[3] : "";
|
||||
e.maintank = row[4] ? row[4] : "";
|
||||
e.assist = row[5] ? row[5] : "";
|
||||
e.puller = row[6] ? row[6] : "";
|
||||
@@ -335,7 +335,7 @@ public:
|
||||
e.gid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.leadername = row[1] ? row[1] : "";
|
||||
e.marknpc = row[2] ? row[2] : "";
|
||||
e.leadershipaa = row[3] ? row[3] : 0;
|
||||
e.leadershipaa = row[3] ? row[3] : "";
|
||||
e.maintank = row[4] ? row[4] : "";
|
||||
e.assist = row[5] ? row[5] : "";
|
||||
e.puller = row[6] ? row[6] : "";
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
class BaseSkillCapsRepository {
|
||||
public:
|
||||
struct SkillCaps {
|
||||
uint8_t skillID;
|
||||
uint8_t class_;
|
||||
uint32_t id;
|
||||
uint8_t skill_id;
|
||||
uint8_t class_id;
|
||||
uint8_t level;
|
||||
uint32_t cap;
|
||||
uint8_t class_;
|
||||
@@ -28,14 +29,15 @@ public:
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("skillID");
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"skillID",
|
||||
"`class`",
|
||||
"id",
|
||||
"skill_id",
|
||||
"class_id",
|
||||
"level",
|
||||
"cap",
|
||||
"class_",
|
||||
@@ -45,8 +47,9 @@ public:
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"skillID",
|
||||
"`class`",
|
||||
"id",
|
||||
"skill_id",
|
||||
"class_id",
|
||||
"level",
|
||||
"cap",
|
||||
"class_",
|
||||
@@ -90,11 +93,12 @@ public:
|
||||
{
|
||||
SkillCaps e{};
|
||||
|
||||
e.skillID = 0;
|
||||
e.class_ = 0;
|
||||
e.level = 0;
|
||||
e.cap = 0;
|
||||
e.class_ = 0;
|
||||
e.id = 0;
|
||||
e.skill_id = 0;
|
||||
e.class_id = 0;
|
||||
e.level = 0;
|
||||
e.cap = 0;
|
||||
e.class_ = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -105,7 +109,7 @@ public:
|
||||
)
|
||||
{
|
||||
for (auto &skill_caps : skill_capss) {
|
||||
if (skill_caps.skillID == skill_caps_id) {
|
||||
if (skill_caps.id == skill_caps_id) {
|
||||
return skill_caps;
|
||||
}
|
||||
}
|
||||
@@ -131,11 +135,12 @@ public:
|
||||
if (results.RowCount() == 1) {
|
||||
SkillCaps e{};
|
||||
|
||||
e.skillID = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.class_ = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.level = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.cap = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.class_ = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.skill_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.class_id = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.level = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.cap = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.class_ = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -169,11 +174,11 @@ public:
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.skillID));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.class_));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.level));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.cap));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.class_));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.skill_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.class_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.level));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.cap));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.class_));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -181,7 +186,7 @@ public:
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.skillID
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
@@ -195,8 +200,9 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.skillID));
|
||||
v.push_back(std::to_string(e.class_));
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.skill_id));
|
||||
v.push_back(std::to_string(e.class_id));
|
||||
v.push_back(std::to_string(e.level));
|
||||
v.push_back(std::to_string(e.cap));
|
||||
v.push_back(std::to_string(e.class_));
|
||||
@@ -210,7 +216,7 @@ public:
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.skillID = results.LastInsertedID();
|
||||
e.id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -229,8 +235,9 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.skillID));
|
||||
v.push_back(std::to_string(e.class_));
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.skill_id));
|
||||
v.push_back(std::to_string(e.class_id));
|
||||
v.push_back(std::to_string(e.level));
|
||||
v.push_back(std::to_string(e.cap));
|
||||
v.push_back(std::to_string(e.class_));
|
||||
@@ -267,11 +274,12 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
SkillCaps e{};
|
||||
|
||||
e.skillID = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.class_ = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.level = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.cap = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.class_ = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.skill_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.class_id = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.level = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.cap = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.class_ = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -296,11 +304,12 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
SkillCaps e{};
|
||||
|
||||
e.skillID = row[0] ? static_cast<uint8_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.class_ = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.level = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.cap = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.class_ = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.skill_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.class_id = row[2] ? static_cast<uint8_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.level = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.cap = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.class_ = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -375,8 +384,9 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.skillID));
|
||||
v.push_back(std::to_string(e.class_));
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.skill_id));
|
||||
v.push_back(std::to_string(e.class_id));
|
||||
v.push_back(std::to_string(e.level));
|
||||
v.push_back(std::to_string(e.cap));
|
||||
v.push_back(std::to_string(e.class_));
|
||||
@@ -402,8 +412,9 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.skillID));
|
||||
v.push_back(std::to_string(e.class_));
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.skill_id));
|
||||
v.push_back(std::to_string(e.class_id));
|
||||
v.push_back(std::to_string(e.level));
|
||||
v.push_back(std::to_string(e.cap));
|
||||
v.push_back(std::to_string(e.class_));
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
std::string varname;
|
||||
std::string value;
|
||||
std::string information;
|
||||
std::string ts;
|
||||
time_t ts;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
"varname",
|
||||
"value",
|
||||
"information",
|
||||
"ts",
|
||||
"UNIX_TIMESTAMP(ts)",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ public:
|
||||
e.varname = row[1] ? row[1] : "";
|
||||
e.value = row[2] ? row[2] : "";
|
||||
e.information = row[3] ? row[3] : "";
|
||||
e.ts = row[4] ? row[4] : std::time(nullptr);
|
||||
e.ts = strtoll(row[4] ? row[4] : "-1", nullptr, 10);
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -172,7 +172,7 @@ public:
|
||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.varname) + "'");
|
||||
v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'");
|
||||
v.push_back(columns[3] + " = '" + Strings::Escape(e.information) + "'");
|
||||
v.push_back(columns[4] + " = '" + Strings::Escape(e.ts) + "'");
|
||||
v.push_back(columns[4] + " = FROM_UNIXTIME(" + (e.ts > 0 ? std::to_string(e.ts) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -198,7 +198,7 @@ public:
|
||||
v.push_back("'" + Strings::Escape(e.varname) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.information) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.ts) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.ts > 0 ? std::to_string(e.ts) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -232,7 +232,7 @@ public:
|
||||
v.push_back("'" + Strings::Escape(e.varname) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.information) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.ts) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.ts > 0 ? std::to_string(e.ts) : "null") + ")");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -270,7 +270,7 @@ public:
|
||||
e.varname = row[1] ? row[1] : "";
|
||||
e.value = row[2] ? row[2] : "";
|
||||
e.information = row[3] ? row[3] : "";
|
||||
e.ts = row[4] ? row[4] : std::time(nullptr);
|
||||
e.ts = strtoll(row[4] ? row[4] : "-1", nullptr, 10);
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -299,7 +299,7 @@ public:
|
||||
e.varname = row[1] ? row[1] : "";
|
||||
e.value = row[2] ? row[2] : "";
|
||||
e.information = row[3] ? row[3] : "";
|
||||
e.ts = row[4] ? row[4] : std::time(nullptr);
|
||||
e.ts = strtoll(row[4] ? row[4] : "-1", nullptr, 10);
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -378,7 +378,7 @@ public:
|
||||
v.push_back("'" + Strings::Escape(e.varname) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.information) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.ts) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.ts > 0 ? std::to_string(e.ts) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -405,7 +405,7 @@ public:
|
||||
v.push_back("'" + Strings::Escape(e.varname) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.information) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.ts) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.ts > 0 ? std::to_string(e.ts) : "null") + ")");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -59,6 +59,24 @@ public:
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
// insert with ON DUPLICATE KEY UPDATE to leave rows that exist unchanged
|
||||
static int InsertUpdateMany(Database& db, const std::vector<CharRecipeList>& entries)
|
||||
{
|
||||
std::vector<std::string> values;
|
||||
values.reserve(entries.size());
|
||||
|
||||
for (const auto& e: entries)
|
||||
{
|
||||
values.emplace_back(fmt::format("({},{},{})", e.char_id, e.recipe_id, e.madecount));
|
||||
}
|
||||
|
||||
auto results = db.QueryDatabase(fmt::format(
|
||||
"INSERT INTO {0} (char_id, recipe_id, madecount) VALUES {1} ON DUPLICATE KEY UPDATE {2}={2}",
|
||||
TableName(), fmt::join(values, ","), PrimaryKey()));
|
||||
|
||||
return results.Success() ? results.RowsAffected() : 0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_CHAR_RECIPE_LIST_REPOSITORY_H
|
||||
|
||||
@@ -213,23 +213,23 @@ public:
|
||||
const glm::vec4& position
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
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
|
||||
)
|
||||
);
|
||||
auto corpse = FindOne(db, 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{
|
||||
.aa_modifier = l[0].aa_modifier,
|
||||
.exp_modifier = l[0].exp_modifier
|
||||
.aa_modifier = m.aa_modifier,
|
||||
.exp_modifier = m.exp_modifier
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,15 @@ public:
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
|
||||
static void ClearAllGroups(Database& db)
|
||||
{
|
||||
db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM `{}`",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_GROUP_ID_REPOSITORY_H
|
||||
|
||||
@@ -44,7 +44,15 @@ public:
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
|
||||
static void ClearAllGroupLeaders(Database& db)
|
||||
{
|
||||
db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM `{}`",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_GROUP_LEADERS_REPOSITORY_H
|
||||
|
||||
@@ -191,4 +191,5 @@ public:
|
||||
return UpdateOne(db, m);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_GUILD_MEMBERS_REPOSITORY_H
|
||||
|
||||
@@ -44,7 +44,30 @@ public:
|
||||
*/
|
||||
|
||||
// Custom extended repository methods here
|
||||
static int64 CountInventorySnapshots(Database& db)
|
||||
{
|
||||
const std::string& query = "SELECT COUNT(*) FROM (SELECT * FROM `inventory_snapshots` a GROUP BY `charid`, `time_index`) b";
|
||||
|
||||
auto results = db.QueryDatabase(query);
|
||||
|
||||
if (!results.Success() || !results.RowCount()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto row = results.begin();
|
||||
|
||||
const int64 count = Strings::ToBigInt(row[0]);
|
||||
|
||||
if (count > std::numeric_limits<int>::max()) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (count < 0) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_INVENTORY_SNAPSHOTS_REPOSITORY_H
|
||||
|
||||
@@ -66,6 +66,16 @@ public:
|
||||
|
||||
return results.Success() ? results.RowsAffected() : 0;
|
||||
}
|
||||
|
||||
static void ClearAllRaidDetails(Database& db)
|
||||
{
|
||||
db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM `{}`",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_RAID_DETAILS_REPOSITORY_H
|
||||
|
||||
@@ -1,297 +0,0 @@
|
||||
#ifndef EQEMU_RAID_LEADERS_REPOSITORY_H
|
||||
#define EQEMU_RAID_LEADERS_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
|
||||
class RaidLeadersRepository {
|
||||
public:
|
||||
struct RaidLeaders {
|
||||
int gid;
|
||||
int rid;
|
||||
std::string marknpc;
|
||||
std::string maintank;
|
||||
std::string assist;
|
||||
std::string puller;
|
||||
std::string leadershipaa;
|
||||
std::string mentoree;
|
||||
int mentor_percent;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"gid",
|
||||
"rid",
|
||||
"marknpc",
|
||||
"maintank",
|
||||
"assist",
|
||||
"puller",
|
||||
"leadershipaa",
|
||||
"mentoree",
|
||||
"mentor_percent",
|
||||
};
|
||||
}
|
||||
|
||||
static std::string ColumnsRaw()
|
||||
{
|
||||
return std::string(Strings::Implode(", ", Columns()));
|
||||
}
|
||||
|
||||
static std::string InsertColumnsRaw()
|
||||
{
|
||||
std::vector<std::string> insert_columns;
|
||||
|
||||
for (auto &column : Columns()) {
|
||||
if (column == PrimaryKey()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
insert_columns.push_back(column);
|
||||
}
|
||||
|
||||
return std::string(Strings::Implode(", ", insert_columns));
|
||||
}
|
||||
|
||||
static std::string TableName()
|
||||
{
|
||||
return std::string("raid_leaders");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
ColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
InsertColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static RaidLeaders NewEntity()
|
||||
{
|
||||
RaidLeaders entry{};
|
||||
|
||||
entry.gid = 0;
|
||||
entry.rid = 0;
|
||||
entry.marknpc = "";
|
||||
entry.maintank = "";
|
||||
entry.assist = "";
|
||||
entry.puller = "";
|
||||
entry.leadershipaa = 0;
|
||||
entry.mentoree = "";
|
||||
entry.mentor_percent = 0;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static RaidLeaders GetRaidLeadersEntry(
|
||||
const std::vector<RaidLeaders> &raid_leaderss,
|
||||
int raid_leaders_id
|
||||
)
|
||||
{
|
||||
for (auto &raid_leaders : raid_leaderss) {
|
||||
if (raid_leaders. == raid_leaders_id) {
|
||||
return raid_leaders;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static RaidLeaders FindOne(
|
||||
int raid_leaders_id
|
||||
)
|
||||
{
|
||||
auto results = database.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
raid_leaders_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
RaidLeaders entry{};
|
||||
|
||||
entry.gid = atoi(row[0]);
|
||||
entry.rid = atoi(row[1]);
|
||||
entry.marknpc = row[2];
|
||||
entry.maintank = row[3];
|
||||
entry.assist = row[4];
|
||||
entry.puller = row[5];
|
||||
entry.leadershipaa = row[6];
|
||||
entry.mentoree = row[7];
|
||||
entry.mentor_percent = atoi(row[8]);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
int raid_leaders_id
|
||||
)
|
||||
{
|
||||
auto results = database.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
raid_leaders_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
RaidLeaders raid_leaders_entry
|
||||
)
|
||||
{
|
||||
std::vector<std::string> update_values;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
update_values.push_back(columns[0] + " = " + std::to_string(raid_leaders_entry.gid));
|
||||
update_values.push_back(columns[1] + " = " + std::to_string(raid_leaders_entry.rid));
|
||||
update_values.push_back(columns[2] + " = '" + Strings::Escape(raid_leaders_entry.marknpc) + "'");
|
||||
update_values.push_back(columns[3] + " = '" + Strings::Escape(raid_leaders_entry.maintank) + "'");
|
||||
update_values.push_back(columns[4] + " = '" + Strings::Escape(raid_leaders_entry.assist) + "'");
|
||||
update_values.push_back(columns[5] + " = '" + Strings::Escape(raid_leaders_entry.puller) + "'");
|
||||
update_values.push_back(columns[6] + " = '" + Strings::Escape(raid_leaders_entry.leadershipaa) + "'");
|
||||
update_values.push_back(columns[7] + " = '" + Strings::Escape(raid_leaders_entry.mentoree) + "'");
|
||||
update_values.push_back(columns[8] + " = " + std::to_string(raid_leaders_entry.mentor_percent));
|
||||
|
||||
auto results = database.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", update_values),
|
||||
PrimaryKey(),
|
||||
raid_leaders_entry.
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static RaidLeaders InsertOne(
|
||||
RaidLeaders raid_leaders_entry
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_values;
|
||||
|
||||
insert_values.push_back(std::to_string(raid_leaders_entry.gid));
|
||||
insert_values.push_back(std::to_string(raid_leaders_entry.rid));
|
||||
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.marknpc) + "'");
|
||||
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.maintank) + "'");
|
||||
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.assist) + "'");
|
||||
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.puller) + "'");
|
||||
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.leadershipaa) + "'");
|
||||
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.mentoree) + "'");
|
||||
insert_values.push_back(std::to_string(raid_leaders_entry.mentor_percent));
|
||||
|
||||
auto results = database.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", insert_values)
|
||||
)
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
raid_leaders_entry.id = results.LastInsertedID();
|
||||
return raid_leaders_entry;
|
||||
}
|
||||
|
||||
raid_leaders_entry = InstanceListRepository::NewEntity();
|
||||
|
||||
return raid_leaders_entry;
|
||||
}
|
||||
|
||||
static int InsertMany(
|
||||
std::vector<RaidLeaders> raid_leaders_entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &raid_leaders_entry: raid_leaders_entries) {
|
||||
std::vector<std::string> insert_values;
|
||||
|
||||
insert_values.push_back(std::to_string(raid_leaders_entry.gid));
|
||||
insert_values.push_back(std::to_string(raid_leaders_entry.rid));
|
||||
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.marknpc) + "'");
|
||||
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.maintank) + "'");
|
||||
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.assist) + "'");
|
||||
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.puller) + "'");
|
||||
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.leadershipaa) + "'");
|
||||
insert_values.push_back("'" + Strings::Escape(raid_leaders_entry.mentoree) + "'");
|
||||
insert_values.push_back(std::to_string(raid_leaders_entry.mentor_percent));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", insert_values) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> insert_values;
|
||||
|
||||
auto results = database.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static std::vector<RaidLeaders> All()
|
||||
{
|
||||
std::vector<RaidLeaders> all_entries;
|
||||
|
||||
auto results = database.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
RaidLeaders entry{};
|
||||
|
||||
entry.gid = atoi(row[0]);
|
||||
entry.rid = atoi(row[1]);
|
||||
entry.marknpc = row[2];
|
||||
entry.maintank = row[3];
|
||||
entry.assist = row[4];
|
||||
entry.puller = row[5];
|
||||
entry.leadershipaa = row[6];
|
||||
entry.mentoree = row[7];
|
||||
entry.mentor_percent = atoi(row[8]);
|
||||
|
||||
all_entries.push_back(entry);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_RAID_LEADERS_REPOSITORY_H
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
);
|
||||
return results.Success() ? results.RowsAffected() : 0;
|
||||
}
|
||||
|
||||
|
||||
static int UpdateRaidAssister(
|
||||
Database& db,
|
||||
int32_t raid_id,
|
||||
@@ -98,5 +98,15 @@ public:
|
||||
|
||||
return results.Success() ? results.RowsAffected() : 0;
|
||||
}
|
||||
|
||||
static void ClearAllRaids(Database& db)
|
||||
{
|
||||
db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM `{}`",
|
||||
TableName()
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
#endif //EQEMU_RAID_MEMBERS_REPOSITORY_H
|
||||
|
||||
+35
-5
@@ -34,8 +34,8 @@ const char *RuleManager::s_categoryNames[_CatCount + 1] = {
|
||||
"InvalidCategory"
|
||||
};
|
||||
|
||||
const RuleManager::RuleInfo RuleManager::s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount + 1] = {
|
||||
/* this is done in three steps so we can reliably get to them by index*/
|
||||
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*/
|
||||
#define RULE_INT(category_name, rule_name, default_value, notes) \
|
||||
{ #category_name ":" #rule_name, Category__##category_name, IntRule, Int__##rule_name, notes },
|
||||
#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) \
|
||||
{ #category_name ":" #rule_name, Category__##category_name, BoolRule, Bool__##rule_name, notes },
|
||||
#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 }
|
||||
};
|
||||
|
||||
@@ -114,6 +117,9 @@ bool RuleManager::GetRule(const std::string &rule_name, std::string &rule_value)
|
||||
case BoolRule:
|
||||
rule_value = m_RuleBoolValues[index] ? "true" : "false";
|
||||
break;
|
||||
case StringRule:
|
||||
rule_value = m_RuleStringValues[index];
|
||||
break;
|
||||
}
|
||||
|
||||
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));
|
||||
LogRules("Set rule [{}] to value [{}]", rule_name, m_RuleBoolValues[index] == 1 ? "true" : "false");
|
||||
break;
|
||||
case StringRule:
|
||||
m_RuleStringValues[index] = rule_value;
|
||||
LogRules("Set rule [{}] to value [{}]", rule_name, rule_value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (db_save) {
|
||||
@@ -215,11 +225,13 @@ std::string RuleManager::_GetRuleName(RuleType type, uint16 index) {
|
||||
return s_RuleInfo[index + IntRuleCount].name;
|
||||
case BoolRule:
|
||||
return s_RuleInfo[index + IntRuleCount + RealRuleCount].name;
|
||||
case StringRule:
|
||||
return s_RuleInfo[index + IntRuleCount + RealRuleCount + StringRuleCount].name;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount].name;
|
||||
return s_RuleInfo[IntRuleCount + RealRuleCount + BoolRuleCount + StringRuleCount].name;
|
||||
}
|
||||
|
||||
//assumes index is valid!
|
||||
@@ -231,11 +243,13 @@ const std::string &RuleManager::_GetRuleNotes(RuleType type, uint16 index) {
|
||||
return s_RuleInfo[index + IntRuleCount].notes;
|
||||
case BoolRule:
|
||||
return s_RuleInfo[index + IntRuleCount + RealRuleCount].notes;
|
||||
case StringRule:
|
||||
return s_RuleInfo[index + IntRuleCount + RealRuleCount + StringRuleCount].notes;
|
||||
default:
|
||||
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) {
|
||||
@@ -343,6 +357,10 @@ void RuleManager::SaveRules(Database *db, const std::string &rule_set_name) {
|
||||
for (i = 0; i < BoolRuleCount; i++) {
|
||||
_SaveRule(db, BoolRule, i);
|
||||
}
|
||||
|
||||
for (i = 0; i < StringRuleCount; i++) {
|
||||
_SaveRule(db, StringRule, i);
|
||||
}
|
||||
}
|
||||
|
||||
void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) {
|
||||
@@ -367,6 +385,9 @@ void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) {
|
||||
case BoolRule:
|
||||
rule_value = m_RuleBoolValues[index] ? "true" : "false";
|
||||
break;
|
||||
case StringRule:
|
||||
rule_value = m_RuleStringValues[index];
|
||||
break;
|
||||
}
|
||||
|
||||
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].second = &r.notes;
|
||||
break;
|
||||
case StringRule:
|
||||
rule_data[r.name].first = m_RuleStringValues[r.rule_index];
|
||||
rule_data[r.name].second = &r.notes;
|
||||
break;
|
||||
default:
|
||||
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);
|
||||
|
||||
if (Strings::Contains(rule.name, e.rule_name)) {
|
||||
@@ -617,3 +642,8 @@ bool RuleManager::GetBoolRule(RuleManager::BoolType t) const
|
||||
{
|
||||
return m_RuleBoolValues[t] == 1;
|
||||
}
|
||||
|
||||
std::string RuleManager::GetStringRule(RuleManager::StringType t) const
|
||||
{
|
||||
return m_RuleStringValues[t];
|
||||
}
|
||||
|
||||
+45
-25
@@ -23,6 +23,7 @@
|
||||
* - RuleI(category, rule) -> fetch an integer rule's value
|
||||
* - RuleR(category, rule) -> fetch a real (float) 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 )
|
||||
#define RuleB(category_name, rule_name) \
|
||||
RuleManager::Instance()->GetBoolRule( RuleManager::Bool__##rule_name )
|
||||
#define RuleS(category_name, rule_name) \
|
||||
RuleManager::Instance()->GetStringRule( RuleManager::String__##rule_name )
|
||||
|
||||
|
||||
#include <vector>
|
||||
@@ -81,6 +84,17 @@ public:
|
||||
|
||||
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 {
|
||||
#define RULE_CATEGORY(category_name) \
|
||||
Category__##category_name,
|
||||
@@ -99,45 +113,49 @@ public:
|
||||
static const IntType InvalidInt = _IntRuleCount;
|
||||
static const RealType InvalidReal = _RealRuleCount;
|
||||
static const BoolType InvalidBool = _BoolRuleCount;
|
||||
static const StringType InvalidString = _StringRuleCount;
|
||||
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
|
||||
int GetIntRule(IntType t) const;
|
||||
float GetRealRule(RealType t) const;
|
||||
bool GetBoolRule(BoolType t) const;
|
||||
std::string GetStringRule(StringType t) const;
|
||||
|
||||
//management routines
|
||||
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(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 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 std::string GetRuleName(StringType t) { return s_RuleInfo[static_cast<int>(t) + IntRuleCount + RealRuleCount + StringRuleCount].name; }
|
||||
static const std::string& GetRuleNotes(IntType t) { return s_RuleInfo[static_cast<int>(t)].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 CategoryType FindCategory(const std::string &category_name);
|
||||
bool ListRules(const std::string &category_name, std::vector <std::string> &l);
|
||||
bool ListCategories(std::vector <std::string> &l);
|
||||
bool GetRule(const std::string &rule_name, std::string &rule_value);
|
||||
static CategoryType FindCategory(const std::string& category_name);
|
||||
bool ListRules(const std::string& category_name, std::vector<std::string>& l);
|
||||
bool ListCategories(std::vector<std::string>& l);
|
||||
bool GetRule(const std::string& rule_name, std::string& rule_value);
|
||||
bool SetRule(
|
||||
const std::string &rule_name,
|
||||
const std::string &rule_value,
|
||||
Database *db = nullptr,
|
||||
const std::string& rule_name,
|
||||
const std::string& rule_value,
|
||||
Database* db = nullptr,
|
||||
bool db_save = false,
|
||||
bool reload = false
|
||||
);
|
||||
|
||||
int GetActiveRulesetID() const { return m_activeRuleset; }
|
||||
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);
|
||||
bool LoadRules(Database *db, const std::string &rule_set_name, bool reload = false);
|
||||
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 UpdateOrphanedRules(Database *db, bool quiet_update = false);
|
||||
bool RestoreRuleNotes(Database *db);
|
||||
bool LoadRules(Database* db, const std::string& rule_set_name, bool reload = false);
|
||||
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 UpdateOrphanedRules(Database* db, bool quiet_update = false);
|
||||
bool RestoreRuleNotes(Database* db);
|
||||
|
||||
private:
|
||||
RuleManager();
|
||||
@@ -147,21 +165,23 @@ private:
|
||||
int m_activeRuleset;
|
||||
std::string m_activeName;
|
||||
|
||||
int m_RuleIntValues[IntRuleCount];
|
||||
float m_RuleRealValues[RealRuleCount];
|
||||
uint32 m_RuleBoolValues[BoolRuleCount];
|
||||
int m_RuleIntValues[IntRuleCount];
|
||||
float m_RuleRealValues[RealRuleCount];
|
||||
uint32 m_RuleBoolValues[BoolRuleCount];
|
||||
std::string m_RuleStringValues[StringRuleCount];
|
||||
|
||||
typedef enum {
|
||||
IntRule,
|
||||
RealRule,
|
||||
BoolRule
|
||||
BoolRule,
|
||||
StringRule
|
||||
} 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 const std::string &_GetRuleNotes(RuleType type, uint16 index);
|
||||
static int _FindOrCreateRuleset(Database *db, const std::string &rule_set_name);
|
||||
void _SaveRule(Database *db, RuleType type, uint16 index);
|
||||
static const std::string& _GetRuleNotes(RuleType type, uint16 index);
|
||||
static int _FindOrCreateRuleset(Database* db, const std::string& rule_set_name);
|
||||
void _SaveRule(Database* db, RuleType type, uint16 index);
|
||||
|
||||
static const char* s_categoryNames[];
|
||||
typedef struct {
|
||||
|
||||
+17
-4
@@ -28,6 +28,9 @@
|
||||
#ifndef RULE_BOOL
|
||||
#define RULE_BOOL(cat, rule, default_value, notes)
|
||||
#endif
|
||||
#ifndef RULE_STRING
|
||||
#define RULE_STRING(cat, rule, default_value, notes)
|
||||
#endif
|
||||
#ifndef RULE_CATEGORY_END
|
||||
#define RULE_CATEGORY_END()
|
||||
#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, 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, 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, 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, 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_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_INT(Character, MaxDraggedCorpses, 2, "Maximum number of corpses you can drag at once")
|
||||
@@ -324,6 +327,9 @@ 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_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_STRING(World, IPExemptionZones, "", "Comma-delimited list of zones to exclude from IP-limit checks. Empty string to disable.")
|
||||
RULE_STRING(World, MOTD, "", "Server MOTD sent on login, change from empty to have this be used instead of variables table 'motd' value")
|
||||
RULE_STRING(World, Rules, "", "Server Rules, change from empty to have this be used instead of variables table 'rules' value, lines are pipe (|) separated, example: A|B|C")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Zone)
|
||||
@@ -349,6 +355,7 @@ RULE_INT(Zone, GlobalLootMultiplier, 1, "Sets Global Loot drop multiplier for da
|
||||
RULE_BOOL(Zone, KillProcessOnDynamicShutdown, true, "When process has booted a zone and has hit its zone shut down timer, it will hard kill the process to free memory back to the OS")
|
||||
RULE_INT(Zone, SpawnEventMin, 3, "When strict is set in spawn_events, specifies the max EQ minutes into the trigger hour a spawn_event will fire. Going below 3 may cause the spawn_event to not fire.")
|
||||
RULE_INT(Zone, ForageChance, 25, "Chance of foraging from zone table vs global table")
|
||||
RULE_INT(Zone, FishingChance, 399, "Chance of fishing from zone table vs global table")
|
||||
RULE_BOOL(Zone, AllowCrossZoneSpellsOnBots, false, "Set to true to allow cross zone spells (cast/remove) to affect bots")
|
||||
RULE_BOOL(Zone, AllowCrossZoneSpellsOnMercs, false, "Set to true to allow cross zone spells (cast/remove) to affect mercenaries")
|
||||
RULE_BOOL(Zone, AllowCrossZoneSpellsOnPets, false, "Set to true to allow cross zone spells (cast/remove) to affect pets")
|
||||
@@ -444,7 +451,8 @@ RULE_BOOL(Spells, Jun182014HundredHandsRevamp, false, "This should be true for i
|
||||
RULE_BOOL(Spells, SwarmPetTargetLock, false, "Use old method of swarm pets target locking till target dies then despawning")
|
||||
RULE_BOOL(Spells, NPC_UseFocusFromSpells, true, "Allow NPC to use most spell derived focus effects")
|
||||
RULE_BOOL(Spells, NPC_UseFocusFromItems, false, "Allow NPC to use most item derived focus effects")
|
||||
RULE_BOOL(Spells, UseAdditiveFocusFromWornSlot, false, "Allows an additive focus effect to be calculated from worn slot")
|
||||
RULE_BOOL(Spells, UseAdditiveFocusFromWornSlot, false, "Allows an additive focus effect to be calculated from worn slot. Does not apply limits checks. Can only have one additive focus rule be true.")
|
||||
RULE_BOOL(Spells, UseAdditiveFocusFromWornSlotWithLimits, false, "Allows an additive focus effect to be calculated from worn slot. Applies normal limit checks. Can only have one additive focus rule be true.")
|
||||
RULE_BOOL(Spells, AlwaysSendTargetsBuffs, false, "Ignore Leadership Alternate Abilities level if true")
|
||||
RULE_BOOL(Spells, FlatItemExtraSpellAmt, false, "Allow SpellDmg stat to affect all spells, regardless of cast time/cooldown/etc")
|
||||
RULE_BOOL(Spells, IgnoreSpellDmgLvlRestriction, false, "Ignore the 5 level spread on applying SpellDmg")
|
||||
@@ -495,6 +503,7 @@ RULE_BOOL(Spells, EvacClearCharmPet, false, "Enable to have evac in zone clear c
|
||||
RULE_BOOL(Spells, ManaTapsRequireNPCMana, false, "Enabling will require target to have mana to tap. Default off as many npc's are caster class with 0 mana and need fixed.")
|
||||
RULE_INT(Spells, HarmTouchCritRatio, 200, "Harmtouch crit bonus, on top of BaseCritRatio")
|
||||
RULE_BOOL(Spells, UseClassicSpellFocus, false, "Enabling will tell the server to handle random focus damage as classic spell imports lack the limit values.")
|
||||
RULE_BOOL(Spells, ManaTapsOnAnyClass, false, "Enabling this will allow you to cast mana taps on any class, this will bypass ManaTapsRequireNPCMana rule.")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Combat)
|
||||
@@ -521,6 +530,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_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_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_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")
|
||||
@@ -569,10 +579,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_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_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, 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, 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, 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")
|
||||
@@ -950,4 +962,5 @@ RULE_CATEGORY_END()
|
||||
#undef RULE_INT
|
||||
#undef RULE_REAL
|
||||
#undef RULE_BOOL
|
||||
#undef RULE_STRING
|
||||
#undef RULE_CATEGORY_END
|
||||
|
||||
+4
-3
@@ -348,7 +348,7 @@ std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message)
|
||||
if (ch != startpos)
|
||||
{
|
||||
std::string str(startpos, ch - startpos);
|
||||
new_message += EQ::SayLinkEngine::GenerateQuestSaylink(str, false, str);
|
||||
new_message += Saylink::Create(str);
|
||||
}
|
||||
in_bracket_state = false;
|
||||
}
|
||||
@@ -417,11 +417,12 @@ SaylinkRepository::Saylink EQ::SayLinkEngine::GetOrSaveSaylink(std::string sayli
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string Saylink::Create(const std::string &saylink_text, bool silent, const std::string &link_name)
|
||||
std::string Saylink::Create(const std::string& saylink_text, bool silent, const std::string& link_name)
|
||||
{
|
||||
return EQ::SayLinkEngine::GenerateQuestSaylink(saylink_text, silent, (link_name.empty() ? saylink_text : link_name));
|
||||
}
|
||||
|
||||
std::string Saylink::Silent(const std::string &saylink_text, const std::string &link_name) {
|
||||
std::string Saylink::Silent(const std::string& saylink_text, const std::string& link_name)
|
||||
{
|
||||
return EQ::SayLinkEngine::GenerateQuestSaylink(saylink_text, true, (link_name.empty() ? saylink_text : link_name));
|
||||
}
|
||||
|
||||
+1
-1
@@ -130,7 +130,7 @@ namespace EQ
|
||||
|
||||
class Saylink {
|
||||
public:
|
||||
static std::string Create(const std::string &saylink_text, bool silent, const std::string &link_name = "");
|
||||
static std::string Create(const std::string &saylink_text, bool silent = false, const std::string &link_name = "");
|
||||
static std::string Silent(const std::string &saylink_text, const std::string &link_name = "");
|
||||
};
|
||||
|
||||
|
||||
@@ -273,6 +273,7 @@
|
||||
#define ServerOP_ReloadFactions 0x4126
|
||||
#define ServerOP_ReloadLoot 0x4127
|
||||
#define ServerOP_ReloadBaseData 0x4128
|
||||
#define ServerOP_ReloadSkillCaps 0x4129
|
||||
|
||||
#define ServerOP_CZDialogueWindow 0x4500
|
||||
#define ServerOP_CZLDoNUpdate 0x4501
|
||||
|
||||
@@ -60,7 +60,7 @@ SharedTaskRequest SharedTask::GetRequestCharacters(Database &db, uint32_t reques
|
||||
request.group_type = SharedTaskRequestGroupType::Group;
|
||||
auto characters = CharacterDataRepository::GetWhere(
|
||||
db, fmt::format(
|
||||
"id IN (select charid from group_id where groupid = (select groupid from group_id where charid = {}))",
|
||||
"id IN (select character_id from group_id where group_id = (select group_id from group_id where character_id = {}))",
|
||||
requested_character_id
|
||||
)
|
||||
);
|
||||
|
||||
+4
-134
@@ -45,6 +45,7 @@
|
||||
#include "repositories/loottable_repository.h"
|
||||
#include "repositories/character_item_recast_repository.h"
|
||||
#include "repositories/character_corpses_repository.h"
|
||||
#include "repositories/skill_caps_repository.h"
|
||||
|
||||
namespace ItemField
|
||||
{
|
||||
@@ -793,8 +794,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
|
||||
}
|
||||
|
||||
if (cv_conflict) {
|
||||
char char_name[64] = "";
|
||||
GetCharName(char_id, char_name);
|
||||
const std::string& char_name = GetCharName(char_id);
|
||||
LogError("ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])",
|
||||
char_name,
|
||||
char_id,
|
||||
@@ -1626,136 +1626,6 @@ bool SharedDatabase::GetCommandSubSettings(std::vector<CommandSubsettingsReposit
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SharedDatabase::LoadSkillCaps(const std::string &prefix) {
|
||||
skill_caps_mmf.reset(nullptr);
|
||||
|
||||
try {
|
||||
const auto Config = EQEmuConfig::get();
|
||||
EQ::IPCMutex mutex("skill_caps");
|
||||
mutex.Lock();
|
||||
std::string file_name = fmt::format("{}/{}{}", path.GetSharedMemoryPath(), prefix, std::string("skill_caps"));
|
||||
LogInfo("Loading [{}]", file_name);
|
||||
skill_caps_mmf = std::make_unique<EQ::MemoryMappedFile>(file_name);
|
||||
|
||||
LogInfo("Loaded skill caps via shared memory");
|
||||
|
||||
mutex.Unlock();
|
||||
} catch(std::exception &ex) {
|
||||
LogError("Error loading skill caps: {}", ex.what());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SharedDatabase::LoadSkillCaps(void *data) {
|
||||
const uint32 class_count = Class::PLAYER_CLASS_COUNT;
|
||||
const uint32 skill_count = EQ::skills::HIGHEST_SKILL + 1;
|
||||
const uint32 level_count = HARD_LEVEL_CAP + 1;
|
||||
uint16 *skill_caps_table = static_cast<uint16*>(data);
|
||||
|
||||
const std::string query = "SELECT skillID, class, level, cap FROM skill_caps ORDER BY skillID, class, level";
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogError("Error loading skill caps from database: {}", results.ErrorMessage().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
for(auto& row = results.begin(); row != results.end(); ++row) {
|
||||
const uint8 skillID = Strings::ToUnsignedInt(row[0]);
|
||||
const uint8 class_ = Strings::ToUnsignedInt(row[1]) - 1;
|
||||
const uint8 level = Strings::ToUnsignedInt(row[2]);
|
||||
const uint16 cap = Strings::ToUnsignedInt(row[3]);
|
||||
|
||||
if(skillID >= skill_count || class_ >= class_count || level >= level_count)
|
||||
continue;
|
||||
|
||||
const uint32 index = (((class_ * skill_count) + skillID) * level_count) + level;
|
||||
skill_caps_table[index] = cap;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 SharedDatabase::GetSkillCap(uint8 Class_, EQ::skills::SkillType Skill, uint8 Level) const
|
||||
{
|
||||
if(!skill_caps_mmf) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(Class_ == 0)
|
||||
return 0;
|
||||
|
||||
int SkillMaxLevel = RuleI(Character, SkillCapMaxLevel);
|
||||
if(SkillMaxLevel < 1) {
|
||||
SkillMaxLevel = RuleI(Character, MaxLevel);
|
||||
}
|
||||
|
||||
const uint32 class_count = Class::PLAYER_CLASS_COUNT;
|
||||
const uint32 skill_count = EQ::skills::HIGHEST_SKILL + 1;
|
||||
const uint32 level_count = HARD_LEVEL_CAP + 1;
|
||||
if(Class_ > class_count || static_cast<uint32>(Skill) > skill_count || Level > level_count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(Level > static_cast<uint8>(SkillMaxLevel)){
|
||||
Level = static_cast<uint8>(SkillMaxLevel);
|
||||
}
|
||||
|
||||
const uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count) + Level;
|
||||
const uint16 *skill_caps_table = static_cast<uint16*>(skill_caps_mmf->Get());
|
||||
return skill_caps_table[index];
|
||||
}
|
||||
|
||||
uint8 SharedDatabase::GetTrainLevel(uint8 Class_, EQ::skills::SkillType Skill, uint8 Level) const
|
||||
{
|
||||
if(!skill_caps_mmf) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(Class_ == 0)
|
||||
return 0;
|
||||
|
||||
int SkillMaxLevel = RuleI(Character, SkillCapMaxLevel);
|
||||
if (SkillMaxLevel < 1) {
|
||||
SkillMaxLevel = RuleI(Character, MaxLevel);
|
||||
}
|
||||
|
||||
const uint32 class_count = Class::PLAYER_CLASS_COUNT;
|
||||
const uint32 skill_count = EQ::skills::HIGHEST_SKILL + 1;
|
||||
const uint32 level_count = HARD_LEVEL_CAP + 1;
|
||||
if(Class_ > class_count || static_cast<uint32>(Skill) > skill_count || Level > level_count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8 ret = 0;
|
||||
if(Level > static_cast<uint8>(SkillMaxLevel)) {
|
||||
const uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count);
|
||||
const uint16 *skill_caps_table = static_cast<uint16*>(skill_caps_mmf->Get());
|
||||
for(uint8 x = 0; x < Level; x++){
|
||||
if(skill_caps_table[index + x]){
|
||||
ret = x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count);
|
||||
const uint16 *skill_caps_table = static_cast<uint16*>(skill_caps_mmf->Get());
|
||||
for(int x = 0; x < SkillMaxLevel; x++){
|
||||
if(skill_caps_table[index + x]){
|
||||
ret = x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ret > GetSkillCap(Class_, Skill, Level))
|
||||
ret = static_cast<uint8>(GetSkillCap(Class_, Skill, Level));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SharedDatabase::LoadDamageShieldTypes(SPDat_Spell_Struct* sp, int32 iMaxSpellID) {
|
||||
const std::string query = StringFormat("SELECT `spellid`, `type` FROM `damageshieldtypes` WHERE `spellid` > 0 "
|
||||
"AND `spellid` <= %i", iMaxSpellID);
|
||||
@@ -1984,9 +1854,9 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
|
||||
sp[tempid].min_range = Strings::ToFloat(row[231]);
|
||||
sp[tempid].no_remove = Strings::ToBool(row[232]);
|
||||
sp[tempid].damage_shield_type = 0;
|
||||
}
|
||||
}
|
||||
|
||||
LoadDamageShieldTypes(sp, max_spells);
|
||||
LoadDamageShieldTypes(sp, max_spells);
|
||||
}
|
||||
|
||||
void SharedDatabase::LoadCharacterInspectMessage(uint32 character_id, InspectMessage_Struct* message) {
|
||||
|
||||
@@ -160,14 +160,6 @@ public:
|
||||
uint32 GetSharedItemsCount() { return m_shared_items_count; }
|
||||
uint32 GetItemsCount();
|
||||
|
||||
/**
|
||||
* skills
|
||||
*/
|
||||
void LoadSkillCaps(void *data);
|
||||
bool LoadSkillCaps(const std::string &prefix);
|
||||
uint16 GetSkillCap(uint8 Class_, EQ::skills::SkillType Skill, uint8 Level) const;
|
||||
uint8 GetTrainLevel(uint8 Class_, EQ::skills::SkillType Skill, uint8 Level) const;
|
||||
|
||||
/**
|
||||
* spells
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#include "skill_caps.h"
|
||||
|
||||
SkillCaps *SkillCaps::SetContentDatabase(Database *db)
|
||||
{
|
||||
m_content_database = db;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
SkillCapsRepository::SkillCaps SkillCaps::GetSkillCap(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level)
|
||||
{
|
||||
if (!IsPlayerClass(class_id)) {
|
||||
return SkillCapsRepository::NewEntity();
|
||||
}
|
||||
|
||||
for (const auto &e: m_skill_caps) {
|
||||
if (
|
||||
e.class_id == class_id &&
|
||||
e.level == level &&
|
||||
static_cast<EQ::skills::SkillType>(e.skill_id) == skill_id
|
||||
) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
return SkillCapsRepository::NewEntity();
|
||||
}
|
||||
|
||||
uint8 SkillCaps::GetTrainLevel(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level)
|
||||
{
|
||||
if (
|
||||
!IsPlayerClass(class_id) ||
|
||||
class_id > Class::PLAYER_CLASS_COUNT ||
|
||||
static_cast<uint32>(skill_id) > (EQ::skills::HIGHEST_SKILL + 1)
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint8 skill_cap_max_level = (
|
||||
RuleI(Character, SkillCapMaxLevel) > 0 ?
|
||||
RuleI(Character, SkillCapMaxLevel) :
|
||||
RuleI(Character, MaxLevel)
|
||||
);
|
||||
|
||||
const uint8 max_level = level > skill_cap_max_level ? level : skill_cap_max_level;
|
||||
|
||||
for (const auto &e: m_skill_caps) {
|
||||
for (uint8 current_level = 1; current_level <= max_level; current_level++) {
|
||||
if (
|
||||
e.class_id == class_id &&
|
||||
static_cast<EQ::skills::SkillType>(e.skill_id) == skill_id &&
|
||||
e.level == current_level
|
||||
) {
|
||||
return current_level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SkillCaps::LoadSkillCaps()
|
||||
{
|
||||
const auto &l = SkillCapsRepository::All(*m_content_database);
|
||||
|
||||
m_skill_caps.reserve(l.size());
|
||||
|
||||
for (const auto &e: l) {
|
||||
if (
|
||||
e.level < 1 ||
|
||||
!IsPlayerClass(e.class_id) ||
|
||||
static_cast<EQ::skills::SkillType>(e.skill_id) >= EQ::skills::SkillCount
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_skill_caps.emplace_back(e);
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"Loaded [{}] Skill Cap Entr{}",
|
||||
l.size(),
|
||||
l.size() != 1 ? "ies" : "y"
|
||||
);
|
||||
}
|
||||
|
||||
void SkillCaps::ReloadSkillCaps()
|
||||
{
|
||||
ClearSkillCaps();
|
||||
LoadSkillCaps();
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef CODE_SKILL_CAPS_H
|
||||
#define CODE_SKILL_CAPS_H
|
||||
|
||||
#include "repositories/skill_caps_repository.h"
|
||||
#include "types.h"
|
||||
#include "classes.h"
|
||||
#include "skills.h"
|
||||
|
||||
class SkillCaps {
|
||||
public:
|
||||
inline void ClearSkillCaps() { m_skill_caps.clear(); }
|
||||
SkillCapsRepository::SkillCaps GetSkillCap(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level);
|
||||
uint8 GetTrainLevel(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level);
|
||||
void LoadSkillCaps();
|
||||
void ReloadSkillCaps();
|
||||
|
||||
SkillCaps *SetContentDatabase(Database *db);
|
||||
private:
|
||||
Database *m_content_database{};
|
||||
std::vector<SkillCapsRepository::SkillCaps> m_skill_caps = {};
|
||||
};
|
||||
|
||||
extern SkillCaps skill_caps;
|
||||
|
||||
|
||||
#endif //CODE_SKILL_CAPS_H
|
||||
+1
-1
@@ -18,10 +18,10 @@
|
||||
*/
|
||||
|
||||
#include "skills.h"
|
||||
#include "classes.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
bool EQ::skills::IsTradeskill(SkillType skill)
|
||||
{
|
||||
switch (skill) {
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace EQ
|
||||
{
|
||||
namespace skills {
|
||||
|
||||
+1
-1
@@ -1257,7 +1257,7 @@ typedef enum {
|
||||
#define SE_Ff_Override_NotFocusable 460 // implemented, @Fc, Allow spell to be focused event if flagged with 'not_focusable' in spell table, base: 1
|
||||
#define SE_ImprovedDamage2 461 // implemented, @Fc, On Caster, spell damage mod pct, base: min pct, limit: max pct
|
||||
#define SE_FcDamageAmt2 462 // implemented, @Fc, On Caster, spell damage mod flat amt, base: amt
|
||||
//#define SE_Shield_Target 463 //
|
||||
#define SE_Shield_Target 463 // implemented, Base1 % damage shielded on target
|
||||
#define SE_PC_Pet_Rampage 464 // implemented - Base1 % chance to do rampage for base2 % of damage each melee round
|
||||
#define SE_PC_Pet_AE_Rampage 465 // implemented - Base1 % chance to do AE rampage for base2 % of damage each melee round
|
||||
#define SE_PC_Pet_Flurry_Chance 466 // implemented - Base1 % chance to do flurry from double attack hit.
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@
|
||||
|
||||
// Build variables
|
||||
// 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.48.0-dev" // always append -dev to the current version for custom-builds
|
||||
#define LOGIN_VERSION "0.8.0"
|
||||
#define COMPILE_DATE __DATE__
|
||||
#define COMPILE_TIME __TIME__
|
||||
@@ -42,7 +42,7 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9264
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9268
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9043
|
||||
|
||||
#endif
|
||||
|
||||
+225
-891
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eqemu-server",
|
||||
"version": "22.45.0",
|
||||
"version": "22.48.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EQEmu/Server.git"
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Description
|
||||
|
||||
Please include a summary of the changes and the related issue (Why is this change necessary). Please also include relevant motivation and context. List any dependencies that are required for this change.
|
||||
|
||||
Fixes # (issue)
|
||||
|
||||
## Type of change
|
||||
|
||||
Please delete options that are not relevant.
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] This change requires a documentation update
|
||||
|
||||
# Testing
|
||||
|
||||
Attach images and describe testing done to validate functionality.
|
||||
|
||||
Clients tested:
|
||||
|
||||
# Checklist
|
||||
|
||||
- [ ] I have tested my changes
|
||||
- [ ] I have performed a self-review of my code. Ensuring variables, functions and methods are named in a human-readable way, comments are added only where naming of variables, functions and methods can't give enough context.
|
||||
- [ ] I have made corresponding changes to the documentation (if applicable, if not delete this line)
|
||||
- [ ] I own the changes of my code and take responsibility for the potential issues that occur
|
||||
- [ ] If my changes make database schema changes, I have tested the changes on a local database (attach image). Updated version.h CURRENT_BINARY_DATABASE_VERSION to the new version. (Delete this if not applicable)
|
||||
@@ -4,13 +4,11 @@ SET(shared_memory_sources
|
||||
items.cpp
|
||||
main.cpp
|
||||
spells.cpp
|
||||
skill_caps.cpp
|
||||
)
|
||||
|
||||
SET(shared_memory_headers
|
||||
items.h
|
||||
spells.h
|
||||
skill_caps.h
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE(shared_memory ${shared_memory_sources} ${shared_memory_headers})
|
||||
|
||||
+1
-17
@@ -28,7 +28,6 @@
|
||||
#include "../common/eqemu_exception.h"
|
||||
#include "../common/strings.h"
|
||||
#include "items.h"
|
||||
#include "skill_caps.h"
|
||||
#include "spells.h"
|
||||
#include "../common/content/world_content_service.h"
|
||||
#include "../common/zone_store.h"
|
||||
@@ -183,7 +182,6 @@ int main(int argc, char **argv)
|
||||
bool load_all = true;
|
||||
bool load_items = false;
|
||||
bool load_loot = false;
|
||||
bool load_skill_caps = false;
|
||||
bool load_spells = false;
|
||||
|
||||
if (argc > 1) {
|
||||
@@ -197,11 +195,7 @@ int main(int argc, char **argv)
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if (strcasecmp("skill_caps", argv[i]) == 0) {
|
||||
load_skill_caps = true;
|
||||
load_all = false;
|
||||
}
|
||||
else if (strcasecmp("spells", argv[i]) == 0) {
|
||||
if (strcasecmp("spells", argv[i]) == 0) {
|
||||
load_spells = true;
|
||||
load_all = false;
|
||||
}
|
||||
@@ -236,16 +230,6 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (load_all || load_skill_caps) {
|
||||
LogInfo("Loading skill caps");
|
||||
try {
|
||||
LoadSkillCaps(&content_db, hotfix_name);
|
||||
} catch (std::exception &ex) {
|
||||
LogError("{}", ex.what());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (load_all || load_spells) {
|
||||
LogInfo("Loading spells");
|
||||
try {
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2013 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "skill_caps.h"
|
||||
#include "../common/global_define.h"
|
||||
#include "../common/shareddb.h"
|
||||
#include "../common/ipc_mutex.h"
|
||||
#include "../common/memory_mapped_file.h"
|
||||
#include "../common/eqemu_exception.h"
|
||||
#include "../common/classes.h"
|
||||
#include "../common/features.h"
|
||||
|
||||
void LoadSkillCaps(SharedDatabase *database, const std::string &prefix) {
|
||||
EQ::IPCMutex mutex("skill_caps");
|
||||
mutex.Lock();
|
||||
|
||||
uint32 class_count = Class::PLAYER_CLASS_COUNT;
|
||||
uint32 skill_count = EQ::skills::HIGHEST_SKILL + 1;
|
||||
uint32 level_count = HARD_LEVEL_CAP + 1;
|
||||
uint32 size = (class_count * skill_count * level_count * sizeof(uint16));
|
||||
|
||||
auto Config = EQEmuConfig::get();
|
||||
std::string file_name = Config->SharedMemDir + prefix + std::string("skill_caps");
|
||||
EQ::MemoryMappedFile mmf(file_name, size);
|
||||
mmf.ZeroFile();
|
||||
|
||||
void *ptr = mmf.Get();
|
||||
database->LoadSkillCaps(ptr);
|
||||
mutex.Unlock();
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2013 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __EQEMU_SHARED_MEMORY_SKILL_CAPS_H
|
||||
#define __EQEMU_SHARED_MEMORY_SKILL_CAPS_H
|
||||
|
||||
#include <string>
|
||||
#include "../common/eqemu_config.h"
|
||||
|
||||
class SharedDatabase;
|
||||
void LoadSkillCaps(SharedDatabase *database, const std::string &prefix);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -280,7 +280,7 @@ foreach my $table_to_generate (@tables) {
|
||||
|
||||
# column names (string)
|
||||
$column_names_quoted .= sprintf("\t\t\t\"%s\",\n", format_column_name_for_mysql($column_name));
|
||||
if ($data_type =~ /datetime/) {
|
||||
if ($data_type =~ /datetime|timestamp/) {
|
||||
$select_column_names_quoted .= sprintf("\t\t\t\"UNIX_TIMESTAMP(%s)\",\n", format_column_name_for_mysql($column_name));
|
||||
}
|
||||
else {
|
||||
@@ -293,7 +293,7 @@ foreach my $table_to_generate (@tables) {
|
||||
if ($data_type =~ /int|float|double|decimal/) {
|
||||
$query_value = sprintf('" + std::to_string(e.%s));', $column_name_formatted);
|
||||
}
|
||||
elsif ($data_type =~ /datetime/) {
|
||||
elsif ($data_type =~ /datetime|timestamp/) {
|
||||
$query_value = sprintf('FROM_UNIXTIME(" + (e.%s > 0 ? std::to_string(e.%s) : "null") + ")");', $column_name_formatted, $column_name_formatted);
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ foreach my $table_to_generate (@tables) {
|
||||
if ($data_type =~ /int|float|double|decimal/) {
|
||||
$value = sprintf('std::to_string(e.%s)', $column_name_formatted);
|
||||
}
|
||||
elsif ($data_type =~ /datetime/) {
|
||||
elsif ($data_type =~ /datetime|timestamp/) {
|
||||
$value = sprintf('"FROM_UNIXTIME(" + (e.%s > 0 ? std::to_string(e.%s) : "null") + ")"', $column_name_formatted, $column_name_formatted);
|
||||
}
|
||||
|
||||
@@ -332,7 +332,7 @@ foreach my $table_to_generate (@tables) {
|
||||
$all_entries .= sprintf("\t\t\te.%-${longest_column_length}s = row[%s] ? strtoll(row[%s], nullptr, 10) : %s;\n", $column_name_formatted, $index, $index, $default_value);
|
||||
$find_one_entries .= sprintf("\t\t\te.%-${longest_column_length}s = row[%s] ? strtoll(row[%s], nullptr, 10) : %s;\n", $column_name_formatted, $index, $index, $default_value);
|
||||
}
|
||||
elsif ($data_type =~ /datetime/) {
|
||||
elsif ($data_type =~ /datetime|timestamp/) {
|
||||
$all_entries .= sprintf("\t\t\te.%-${longest_column_length}s = strtoll(row[%s] ? row[%s] : \"-1\", nullptr, 10);\n", $column_name_formatted, $index, $index);
|
||||
$find_one_entries .= sprintf("\t\t\te.%-${longest_column_length}s = strtoll(row[%s] ? row[%s] : \"-1\", nullptr, 10);\n", $column_name_formatted, $index, $index);
|
||||
}
|
||||
@@ -591,7 +591,7 @@ sub translate_mysql_data_type_to_c
|
||||
elsif ($mysql_data_type =~ /double/) {
|
||||
$struct_data_type = 'double';
|
||||
}
|
||||
elsif ($mysql_data_type =~ /datetime/) {
|
||||
elsif ($mysql_data_type =~ /datetime|timestamp/) {
|
||||
$struct_data_type = 'time_t';
|
||||
}
|
||||
|
||||
|
||||
+112
-82
@@ -52,6 +52,10 @@
|
||||
#include "../common/repositories/player_event_logs_repository.h"
|
||||
#include "../common/repositories/inventory_repository.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "../common/content/world_content_service.h"
|
||||
#include "../common/repositories/group_id_repository.h"
|
||||
#include "../common/repositories/character_data_repository.h"
|
||||
#include "../common/skill_caps.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
@@ -189,9 +193,10 @@ bool Client::CanTradeFVNoDropItem()
|
||||
|
||||
void Client::SendEnterWorld(std::string name)
|
||||
{
|
||||
char char_name[64] = { 0 };
|
||||
if (is_player_zoning && database.GetLiveChar(GetAccountID(), char_name)) {
|
||||
if(database.GetAccountIDByChar(char_name) != GetAccountID()) {
|
||||
std::string live_name {};
|
||||
if (is_player_zoning) {
|
||||
live_name = database.GetLiveChar(GetAccountID());
|
||||
if(database.GetAccountIDByChar(live_name) != GetAccountID()) {
|
||||
eqs->Close();
|
||||
return;
|
||||
} else {
|
||||
@@ -199,8 +204,8 @@ void Client::SendEnterWorld(std::string name)
|
||||
}
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_EnterWorld, strlen(char_name) + 1);
|
||||
memcpy(outapp->pBuffer,char_name,strlen(char_name)+1);
|
||||
auto outapp = new EQApplicationPacket(OP_EnterWorld, live_name.length() + 1);
|
||||
memcpy(outapp->pBuffer, live_name.c_str(), live_name.length() + 1);
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
@@ -599,79 +604,63 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app)
|
||||
}
|
||||
|
||||
bool Client::HandleGenerateRandomNamePacket(const EQApplicationPacket *app) {
|
||||
// creates up to a 10 char name
|
||||
char vowels[18]="aeiouyaeiouaeioe";
|
||||
char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd";
|
||||
char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||
char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr";
|
||||
int rndnum=emu_random.Int(0, 75),n=1;
|
||||
bool dlc=false;
|
||||
bool vwl=false;
|
||||
bool dbl=false;
|
||||
if (rndnum>63)
|
||||
{ // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel
|
||||
rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk"
|
||||
rndname[0]=paircons[rndnum];
|
||||
rndname[1]=paircons[rndnum+1];
|
||||
n=2;
|
||||
}
|
||||
else if (rndnum>16)
|
||||
{
|
||||
rndnum-=17;
|
||||
rndname[0]=cons[rndnum];
|
||||
}
|
||||
else
|
||||
{
|
||||
rndname[0]=vowels[rndnum];
|
||||
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];
|
||||
char newName[17] = {0};
|
||||
bool unique = false;
|
||||
|
||||
while (!unique) {
|
||||
std::string cons = "bcdfghjklmnpqrstvwxyz";
|
||||
std::string vows = "aeou";
|
||||
std::string allVows = "aeiou";
|
||||
std::vector<std::string> endPhon = {"a", "e", "i", "o", "u", "os", "as", "us", "is", "y", "an", "en", "in", "on", "un"};
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 gen(rd());
|
||||
|
||||
std::uniform_int_distribution<int> lenDist(5, 10);
|
||||
std::uniform_int_distribution<int> firstCharDist(0, 1);
|
||||
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)];
|
||||
}
|
||||
|
||||
newName[0] = toupper(newName[0]);
|
||||
|
||||
while (len < lenDist(gen) - 1) {
|
||||
if (len % 2 == 0) {
|
||||
newName[len++] = cons[consDist(gen)];
|
||||
} else {
|
||||
newName[len++] = allVows[allVowDist(gen)];
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // select a vowel
|
||||
rndname[i]=vowels[emu_random.Int(0, 16)];
|
||||
|
||||
std::string end = endPhon[endPhonDist(gen)];
|
||||
for (char c : end) {
|
||||
if (len < 10) newName[len++] = c;
|
||||
}
|
||||
vwl=!vwl;
|
||||
if (!dbl && !dlc)
|
||||
{ // one chance at double letters in name
|
||||
if (!emu_random.Int(0, i+9)) // chances decrease towards end of name
|
||||
{
|
||||
rndname[i+1]=rndname[i];
|
||||
dbl=true;
|
||||
i+=1;
|
||||
|
||||
if (database.CheckNameFilter(newName)) {
|
||||
std::string query = StringFormat("SELECT `name` FROM `character_data` WHERE `name` = '%s'", newName);
|
||||
auto res = database.QueryDatabase(query);
|
||||
if (res.Success() && res.RowCount() == 0) {
|
||||
unique = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rndname[0]=toupper(rndname[0]);
|
||||
NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer;
|
||||
memset(ngs->name,0,64);
|
||||
strcpy(ngs->name,rndname);
|
||||
memset(ngs->name, 0, 64);
|
||||
strcpy(ngs->name, newName);
|
||||
|
||||
QueuePacket(app);
|
||||
return true;
|
||||
@@ -779,21 +768,50 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
|
||||
auto ew = (EnterWorld_Struct *) app->pBuffer;
|
||||
strn0cpy(char_name, ew->name, sizeof(char_name));
|
||||
|
||||
uint32 temporary_account_id = 0;
|
||||
charid = database.GetCharacterInfo(char_name, &temporary_account_id, &zone_id, &instance_id);
|
||||
if (!charid) {
|
||||
const auto& l = CharacterDataRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`name` = '{}' LIMIT 1",
|
||||
Strings::Escape(char_name)
|
||||
)
|
||||
);
|
||||
|
||||
if (l.empty()) {
|
||||
LogInfo("Could not get CharInfo for [{}]", char_name);
|
||||
eqs->Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const auto& e = l.front();
|
||||
|
||||
// Make sure this account owns this character
|
||||
if (temporary_account_id != account_id) {
|
||||
LogInfo("Account [{}] does not own the character named [{}] from account [{}]", account_id, char_name, temporary_account_id);
|
||||
if (e.account_id != account_id) {
|
||||
LogInfo(
|
||||
"Account [{}] does not own the character named [{}] from account [{}]",
|
||||
account_id,
|
||||
char_name,
|
||||
e.account_id
|
||||
);
|
||||
eqs->Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
charid = e.id;
|
||||
zone_id = e.zone_id;
|
||||
instance_id = e.zone_instance;
|
||||
|
||||
// This can probably be moved outside and have another method return requested info (don't forget to remove the #include "../common/shareddb.h" above)
|
||||
// (This is a literal translation of the original process..I don't see why it can't be changed to a single-target query over account iteration)
|
||||
if (!is_player_zoning) {
|
||||
@@ -884,7 +902,14 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
|
||||
}
|
||||
|
||||
if(!is_player_zoning) {
|
||||
database.SetGroupID(char_name, 0, charid);
|
||||
GroupIdRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`character_id` = {} AND `name` = '{}'",
|
||||
charid,
|
||||
Strings::Escape(char_name)
|
||||
)
|
||||
);
|
||||
database.SetLoginFlags(charid, false, false, 1);
|
||||
} else {
|
||||
auto group_id = database.GetGroupID(char_name);
|
||||
@@ -903,14 +928,19 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_MOTD);
|
||||
std::string motd_message;
|
||||
if (database.GetVariable("MOTD", motd_message)) {
|
||||
outapp->size = motd_message.length() + 1;
|
||||
std::string motd = RuleS(World, MOTD);
|
||||
if (!motd.empty()) {
|
||||
outapp->size = motd.length() + 1;
|
||||
outapp->pBuffer = new uchar[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. :)
|
||||
outapp->size = 1;
|
||||
outapp->size = 1;
|
||||
outapp->pBuffer = new uchar[outapp->size];
|
||||
outapp->pBuffer[0] = 0;
|
||||
}
|
||||
@@ -2105,7 +2135,7 @@ void Client::SetClassStartingSkills(PlayerProfile_Struct *pp)
|
||||
i == EQ::skills::SkillAlcoholTolerance || i == EQ::skills::SkillBindWound)
|
||||
continue;
|
||||
|
||||
pp->skills[i] = content_db.GetSkillCap(pp->class_, (EQ::skills::SkillType)i, 1);
|
||||
pp->skills[i] = skill_caps.GetSkillCap(pp->class_, (EQ::skills::SkillType)i, 1).cap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ bool ClientListEntry::CheckAuth(uint32 loginserver_account_id, const char *key_p
|
||||
|
||||
paccountid = database.CreateAccount(
|
||||
loginserver_account_name,
|
||||
0,
|
||||
std::string(),
|
||||
default_account_status,
|
||||
source_loginserver,
|
||||
LSID()
|
||||
|
||||
@@ -94,8 +94,25 @@ void ClientList::GetCLEIP(uint32 in_ip) {
|
||||
int count = 0;
|
||||
iterator.Reset();
|
||||
|
||||
const auto& zones = Strings::Split(RuleS(World, IPExemptionZones), ",");
|
||||
|
||||
while (iterator.MoreElements()) {
|
||||
cle = iterator.GetData();
|
||||
|
||||
if (!zones.empty() && cle->zone()) {
|
||||
auto it = std::ranges::find_if(
|
||||
zones,
|
||||
[cle](const auto& z) {
|
||||
return Strings::ToUnsignedInt(z) == cle->zone();
|
||||
}
|
||||
);
|
||||
|
||||
if (it != zones.end()) {
|
||||
iterator.Advance();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
cle->GetIP() == in_ip &&
|
||||
(
|
||||
|
||||
+1
-2
@@ -57,8 +57,7 @@ struct EQ::Net::ConsoleLoginStatus CheckLogin(const std::string &username, const
|
||||
return ret;
|
||||
}
|
||||
|
||||
char account_name[64];
|
||||
database.GetAccountName(ret.account_id, account_name);
|
||||
const std::string& account_name = database.GetAccountName(ret.account_id);
|
||||
|
||||
ret.account_name = account_name;
|
||||
ret.status = database.CheckStatus(ret.account_id);
|
||||
|
||||
@@ -142,6 +142,7 @@ std::vector<Reload> reload_types = {
|
||||
Reload{.command = "base_data", .opcode = ServerOP_ReloadBaseData, .desc = "Base Data"},
|
||||
Reload{.command = "blocked_spells", .opcode = ServerOP_ReloadBlockedSpells, .desc = "Blocked Spells"},
|
||||
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 = "doors", .opcode = ServerOP_ReloadDoors, .desc = "Doors"},
|
||||
Reload{.command = "dztemplates", .opcode = ServerOP_ReloadDzTemplates, .desc = "Dynamic Zone Templates"},
|
||||
@@ -155,6 +156,7 @@ std::vector<Reload> reload_types = {
|
||||
Reload{.command = "opcodes", .opcode = ServerOP_ReloadOpcodes, .desc = "Opcodes"},
|
||||
Reload{.command = "perl_export", .opcode = ServerOP_ReloadPerlExportSettings, .desc = "Perl Event Export Settings"},
|
||||
Reload{.command = "rules", .opcode = ServerOP_ReloadRules, .desc = "Rules"},
|
||||
Reload{.command = "skill_caps", .opcode = ServerOP_ReloadSkillCaps, .desc = "Skill Caps"},
|
||||
Reload{.command = "static", .opcode = ServerOP_ReloadStaticZoneData, .desc = "Static Zone Data"},
|
||||
Reload{.command = "tasks", .opcode = ServerOP_ReloadTasks, .desc = "Tasks"},
|
||||
Reload{.command = "titles", .opcode = ServerOP_ReloadTitles, .desc = "Titles"},
|
||||
|
||||
+9
-1
@@ -86,8 +86,9 @@
|
||||
#include "world_boot.h"
|
||||
#include "../common/path_manager.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "../common/skill_caps.h"
|
||||
|
||||
|
||||
SkillCaps skill_caps;
|
||||
ZoneStore zone_store;
|
||||
ClientList client_list;
|
||||
GroupLFPList LFPGroupList;
|
||||
@@ -188,6 +189,13 @@ int main(int argc, char **argv)
|
||||
RegisterConsoleFunctions(console);
|
||||
}
|
||||
|
||||
content_service.SetDatabase(&database)
|
||||
->SetContentDatabase(&content_db)
|
||||
->SetExpansionContext()
|
||||
->ReloadContentFlags();
|
||||
|
||||
skill_caps.SetContentDatabase(&content_db)->LoadSkillCaps();
|
||||
|
||||
std::unique_ptr<EQ::Net::ServertalkServer> server_connection;
|
||||
server_connection = std::make_unique<EQ::Net::ServertalkServer>();
|
||||
|
||||
|
||||
+11
-1
@@ -230,13 +230,23 @@ void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) {
|
||||
case ServerOP_GuildChannel:
|
||||
case ServerOP_GuildURL:
|
||||
case ServerOP_GuildMemberRemove:
|
||||
case ServerOP_GuildMemberAdd:
|
||||
case ServerOP_GuildSendGuildList:
|
||||
case ServerOP_GuildMembersList:
|
||||
{
|
||||
zoneserver_list.SendPacketToBootedZones(pack);
|
||||
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:
|
||||
LogGuilds("Unknown packet {:#04x} received from zone??", pack->opcode);
|
||||
break;
|
||||
|
||||
@@ -292,10 +292,6 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
|
||||
LogError("Error: Could not load item data. But ignoring");
|
||||
}
|
||||
|
||||
if (!content_db.LoadSkillCaps(std::string(hotfix_name))) {
|
||||
LogError("Error: Could not load skill cap data. But ignoring");
|
||||
}
|
||||
|
||||
guild_mgr.LoadGuilds();
|
||||
guild_mgr.LoadTributes();
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "../common/patches/patches.h"
|
||||
#include "../zone/data_bucket.h"
|
||||
#include "../common/repositories/guild_tributes_repository.h"
|
||||
#include "../common/skill_caps.h"
|
||||
|
||||
extern ClientList client_list;
|
||||
extern GroupLFPList LFPGroupList;
|
||||
@@ -1100,7 +1101,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
}
|
||||
|
||||
auto smotd = (ServerMotd_Struct*) pack->pBuffer;
|
||||
database.SetVariable("MOTD", smotd->motd);
|
||||
RuleManager::Instance()->SetRule("MOTD", smotd->motd, &database, true, true);
|
||||
zoneserver_list.SendPacket(pack);
|
||||
break;
|
||||
}
|
||||
@@ -1436,6 +1437,11 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
zoneserver_list.SendPacket(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_ReloadSkillCaps: {
|
||||
zoneserver_list.SendPacket(pack);
|
||||
skill_caps.ReloadSkillCaps();
|
||||
break;
|
||||
}
|
||||
case ServerOP_ReloadRules: {
|
||||
zoneserver_list.SendPacket(pack);
|
||||
RuleManager::Instance()->LoadRules(&database, "default", true);
|
||||
@@ -1496,11 +1502,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
LogInfo("Error: Could not load item data. But ignoring");
|
||||
}
|
||||
|
||||
LogInfo("Loading skill caps");
|
||||
if (!content_db.LoadSkillCaps(hotfix_name)) {
|
||||
LogInfo("Error: Could not load skill cap data. But ignoring");
|
||||
}
|
||||
|
||||
zoneserver_list.SendPacket(pack);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ SET(zone_sources
|
||||
loot.cpp
|
||||
lua_bot.cpp
|
||||
lua_bit.cpp
|
||||
lua_buff.cpp
|
||||
lua_corpse.cpp
|
||||
lua_client.cpp
|
||||
lua_door.cpp
|
||||
@@ -105,6 +106,7 @@ SET(zone_sources
|
||||
pathfinder_null.cpp
|
||||
pathing.cpp
|
||||
perl_bot.cpp
|
||||
perl_buff.cpp
|
||||
perl_client.cpp
|
||||
perl_doors.cpp
|
||||
perl_entity.cpp
|
||||
@@ -207,6 +209,7 @@ SET(zone_headers
|
||||
horse.h
|
||||
lua_bot.h
|
||||
lua_bit.h
|
||||
lua_buff.h
|
||||
lua_client.h
|
||||
lua_corpse.h
|
||||
lua_door.h
|
||||
|
||||
+25
-15
@@ -621,28 +621,38 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
|
||||
// NPC *npc1, *npc2;
|
||||
int reverse;
|
||||
|
||||
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)){
|
||||
if (!zone->CanDoCombat()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target->GetSpecialAbility(IMMUNE_DAMAGE_CLIENT) && IsClient())
|
||||
// some special cases
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target->GetSpecialAbility(IMMUNE_DAMAGE_NPC) && IsNPC())
|
||||
return false;
|
||||
if (this == target) { // you can attack yourself
|
||||
return true;
|
||||
}
|
||||
|
||||
if (target->IsHorse())
|
||||
if (target->GetSpecialAbility(NO_HARM_FROM_CLIENT)) {
|
||||
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)
|
||||
Mob *target_owner = target->GetOwner();
|
||||
|
||||
@@ -57,9 +57,7 @@ EQ::Net::WebsocketLoginStatus CheckLogin(
|
||||
return ret;
|
||||
}
|
||||
|
||||
char account_name[64];
|
||||
database.GetAccountName(static_cast<uint32>(ret.account_id), account_name);
|
||||
ret.account_name = account_name;
|
||||
ret.account_name = database.GetAccountName(static_cast<uint32>(ret.account_id));
|
||||
ret.logged_in = true;
|
||||
ret.status = database.CheckStatus(ret.account_id);
|
||||
return ret;
|
||||
|
||||
+141
-49
@@ -1005,7 +1005,7 @@ double Mob::RollD20(int offense, int mitigation)
|
||||
auto atk_roll = zone->random.Roll0(offense + 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));
|
||||
|
||||
index = EQ::Clamp((index * 20) / avg, 0, 19);
|
||||
@@ -1687,13 +1687,6 @@ bool Mob::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
|
||||
}
|
||||
}
|
||||
|
||||
//used by complete heal and #heal
|
||||
void Mob::Heal()
|
||||
{
|
||||
SetMaxHP();
|
||||
SendHPUpdate();
|
||||
}
|
||||
|
||||
void Client::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic, eSpecialAttacks special)
|
||||
{
|
||||
if (dead || IsCorpse())
|
||||
@@ -2618,12 +2611,6 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
}
|
||||
|
||||
if (m.member) {
|
||||
m.member->RecordKilledNPCEvent(this);
|
||||
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, m.member, "killed", 0);
|
||||
}
|
||||
|
||||
if (RuleB(NPC, EnableMeritBasedFaction)) {
|
||||
m.member->SetFactionLevel(
|
||||
m.member->CharacterID(),
|
||||
@@ -2683,18 +2670,11 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the EVENT_KILLED_MERIT event and update kill tasks
|
||||
* for all group members */
|
||||
/* Update kill tasks for all group members */
|
||||
for (const auto& m : killer_group->members) {
|
||||
if (m && m->IsClient()) {
|
||||
Client* c = m->CastToClient();
|
||||
|
||||
c->RecordKilledNPCEvent(this);
|
||||
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0);
|
||||
}
|
||||
|
||||
if (RuleB(NPC, EnableMeritBasedFaction)) {
|
||||
c->SetFactionLevel(
|
||||
c->CharacterID(),
|
||||
@@ -2756,13 +2736,6 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
}
|
||||
}
|
||||
|
||||
/* Send the EVENT_KILLED_MERIT event */
|
||||
give_exp_client->RecordKilledNPCEvent(this);
|
||||
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, give_exp_client, "killed", 0);
|
||||
}
|
||||
|
||||
if (RuleB(NPC, EnableMeritBasedFaction)) {
|
||||
give_exp_client->SetFactionLevel(
|
||||
give_exp_client->CharacterID(),
|
||||
@@ -2843,6 +2816,9 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
|
||||
entity_list.RemoveFromAutoXTargets(this);
|
||||
|
||||
// Here we create the corpse.
|
||||
DeleteInvalidQuestLoot();
|
||||
|
||||
corpse = new Corpse(
|
||||
this,
|
||||
&m_loot_items,
|
||||
@@ -2991,6 +2967,17 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
|
||||
m_combat_record.Stop();
|
||||
|
||||
if (give_exp_client && !IsCorpse()) {
|
||||
const auto& v = give_exp_client->GetRaidOrGroupOrSelf(true);
|
||||
for (const auto& m : v) {
|
||||
m->CastToClient()->RecordKilledNPCEvent(this);
|
||||
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, m, "killed", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH_COMPLETE)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {} {} {} {} {} {}",
|
||||
@@ -3091,26 +3078,37 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
TryTriggerOnCastRequirement();
|
||||
}
|
||||
|
||||
if (IsClient() && !IsAIControlled())
|
||||
if (IsClient() && !IsAIControlled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO))
|
||||
if (IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSpecialAbility(IMMUNE_AGGRO_NPC) && other->IsNPC())
|
||||
if (other->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && other->IsClient())
|
||||
if (other->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsValidSpell(spell_id) && IsNoDetrimentalSpellAggroSpell(spell_id))
|
||||
if (other->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (other == myowner)
|
||||
if (IsValidSpell(spell_id) && IsNoDetrimentalSpellAggroSpell(spell_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->GetSpecialAbility(IMMUNE_AGGRO_ON))
|
||||
if (other == myowner) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (other->GetSpecialAbility(IMMUNE_AGGRO_ON)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetSpecialAbility(NPC_TUNNELVISION)) {
|
||||
int tv_mod = GetSpecialAbilityParam(NPC_TUNNELVISION, 0);
|
||||
@@ -3194,8 +3192,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
|
||||
if (
|
||||
!owner->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
!(GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && owner->IsClient()) &&
|
||||
!(GetSpecialAbility(IMMUNE_AGGRO_NPC) && owner->IsNPC())
|
||||
!(owner->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
|
||||
!(owner->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
|
||||
!(owner->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC))
|
||||
) {
|
||||
if (owner->IsClient() && !CheckAggro(owner)) {
|
||||
owner->CastToClient()->AddAutoXTarget(this);
|
||||
@@ -3209,8 +3208,9 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
if (
|
||||
!mypet->IsFamiliar() &&
|
||||
!mypet->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
!(mypet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && IsClient()) &&
|
||||
!(mypet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && IsNPC())
|
||||
!(IsBot() && mypet->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
|
||||
!(IsClient() && mypet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
|
||||
!(IsNPC() && mypet->GetSpecialAbility(IMMUNE_AGGRO_NPC))
|
||||
) {
|
||||
mypet->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
|
||||
}
|
||||
@@ -3219,8 +3219,9 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
if (
|
||||
myowner->IsAIControlled() &&
|
||||
!myowner->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
!(GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && myowner->IsClient()) &&
|
||||
!(GetSpecialAbility(IMMUNE_AGGRO_NPC) && myowner->IsNPC())
|
||||
!(myowner->IsBot() && GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
|
||||
!(myowner->IsClient() && GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
|
||||
!(myowner->IsNPC() && GetSpecialAbility(IMMUNE_AGGRO_NPC))
|
||||
) {
|
||||
myowner->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
|
||||
}
|
||||
@@ -3966,6 +3967,18 @@ bool Mob::CheckDoubleAttack()
|
||||
}
|
||||
|
||||
void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType skill_used, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks special) {
|
||||
#ifdef LUA_EQEMU
|
||||
int64 lua_ret = 0;
|
||||
bool ignore_default = false;
|
||||
lua_ret = LuaParser::Instance()->CommonDamage(this, attacker, damage, spell_id, static_cast<int>(skill_used), avoidable, buffslot, iBuffTic, static_cast<int>(special), ignore_default);
|
||||
if (lua_ret != 0) {
|
||||
damage = lua_ret;
|
||||
}
|
||||
|
||||
if (ignore_default) {
|
||||
//return lua_ret;
|
||||
}
|
||||
#endif
|
||||
// This method is called with skill_used=ABJURE for Damage Shield damage.
|
||||
bool FromDamageShield = (skill_used == EQ::skills::SkillAbjuration);
|
||||
bool ignore_invul = false;
|
||||
@@ -4060,8 +4073,9 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
!pet->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
!pet->IsEngaged() &&
|
||||
attacker &&
|
||||
!(pet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) &&
|
||||
!(pet->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) &&
|
||||
!(attacker->IsBot() && pet->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
|
||||
!(attacker->IsClient() && pet->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
|
||||
!(attacker->IsNPC() && pet->GetSpecialAbility(IMMUNE_AGGRO_NPC)) &&
|
||||
attacker != this &&
|
||||
!attacker->IsCorpse() &&
|
||||
!pet->IsGHeld() &&
|
||||
@@ -4692,6 +4706,19 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
|
||||
void Mob::HealDamage(uint64 amount, Mob* caster, uint16 spell_id)
|
||||
{
|
||||
#ifdef LUA_EQEMU
|
||||
uint64 lua_ret = 0;
|
||||
bool ignore_default = false;
|
||||
|
||||
lua_ret = LuaParser::Instance()->HealDamage(this, caster, amount, spell_id, ignore_default);
|
||||
if (lua_ret != 0) {
|
||||
amount = lua_ret;
|
||||
}
|
||||
|
||||
if (ignore_default) {
|
||||
//return lua_ret;
|
||||
}
|
||||
#endif
|
||||
int64 maxhp = GetMaxHP();
|
||||
int64 curhp = GetHP();
|
||||
uint64 acthealed = 0;
|
||||
@@ -6194,7 +6221,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
|
||||
if (headshot > 0) {
|
||||
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)) {
|
||||
hit.damage_done *= 2;
|
||||
MessageString(Chat::MeleeCrit, BOW_DOUBLE_DAMAGE);
|
||||
@@ -6286,9 +6313,24 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
|
||||
|
||||
hit.damage_done += (hit.damage_done * pct_damage_reduction / 100) + defender->GetPositionalDmgTakenAmt(this);
|
||||
|
||||
if (defender->GetShielderID()) {
|
||||
DoShieldDamageOnShielder(defender, hit.damage_done, hit.skill);
|
||||
hit.damage_done -= hit.damage_done * defender->GetShieldTargetMitigation() / 100; //Default shielded takes 50 pct damage
|
||||
if (defender->GetShielderID() || defender->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT]) {
|
||||
bool use_shield_ability = true;
|
||||
//If defender is being shielded by an ability AND has a shield spell effect buff use highest mitigation value.
|
||||
if ((defender->GetShieldTargetMitigation() && defender->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT]) &&
|
||||
(defender->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT] >= defender->GetShieldTargetMitigation())){
|
||||
bool use_shield_ability = false;
|
||||
}
|
||||
|
||||
//use targeted /shield ability values
|
||||
if (defender->GetShielderID() && use_shield_ability) {
|
||||
DoShieldDamageOnShielder(defender, hit.damage_done, hit.skill);
|
||||
hit.damage_done -= hit.damage_done * defender->GetShieldTargetMitigation() / 100; //Default shielded takes 50 pct damage
|
||||
}
|
||||
//use spell effect SPA 463 values
|
||||
else if (defender->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT]){
|
||||
DoShieldDamageOnShielderSpellEffect(defender, hit.damage_done, hit.skill);
|
||||
hit.damage_done -= hit.damage_done * defender->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT] / 100;
|
||||
}
|
||||
}
|
||||
|
||||
CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
|
||||
@@ -6331,7 +6373,57 @@ void Mob::DoShieldDamageOnShielder(Mob *shield_target, int64 hit_damage_done, EQ
|
||||
|
||||
hit_damage_done -= hit_damage_done * mitigation / 100;
|
||||
shielder->Damage(this, hit_damage_done, SPELL_UNKNOWN, skillInUse, true, -1, false, m_specialattacks);
|
||||
shielder->CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
|
||||
}
|
||||
|
||||
void Mob::DoShieldDamageOnShielderSpellEffect(Mob* shield_target, int64 hit_damage_done, EQ::skills::SkillType skillInUse)
|
||||
{
|
||||
if (!shield_target) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
SPA 463 SE_SHIELD_TARGET
|
||||
|
||||
Live description: "Shields your target, taking a percentage of their damage".
|
||||
Only example spell on live is an NPC who uses it during a raid event "Laurion's Song" expansion. SPA 54492 'Guardian Stance' Described as 100% Melee Shielding
|
||||
|
||||
Example of mechanic. Base value = 70. Caster puts buff on target. Each melee hit Buff Target takes 70% less damage, Buff Caster receives 30% of the melee damage.
|
||||
Added mechanic to cause buff to fade if target or caster are seperated by a distance greater than the casting range of the spell. This allows similiar mechanics
|
||||
to the /shield ability, without a range removal mechanic it would be too easy to abuse if put on a player spell. *can not confirm live does this currently
|
||||
|
||||
Can not be cast on self.
|
||||
*/
|
||||
|
||||
if (shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT])
|
||||
{
|
||||
if (shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT] >= 0)
|
||||
{
|
||||
Mob *shielder = entity_list.GetMob(shield_target->buffs[shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT]].casterid);
|
||||
if (!shielder) {
|
||||
shield_target->BuffFadeBySlot(shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT]);
|
||||
return;
|
||||
}
|
||||
|
||||
int shield_spell_id = shield_target->buffs[shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT]].spellid;
|
||||
if (!IsValidSpell(shield_spell_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
float max_range = spells[shield_spell_id].range;
|
||||
if (spells[shield_spell_id].aoe_range > max_range) {
|
||||
max_range = spells[shield_spell_id].aoe_range;
|
||||
}
|
||||
max_range += 5.0f; //small buffer in case casted at exactly max range.
|
||||
|
||||
if (shield_target->CalculateDistance(shielder->GetX(), shielder->GetY(), shielder->GetZ()) > max_range) {
|
||||
shield_target->BuffFadeBySlot(shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT]);
|
||||
return;
|
||||
}
|
||||
|
||||
int mitigation = 100 - shield_target->spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT];
|
||||
hit_damage_done -= hit_damage_done * mitigation / 100;
|
||||
shielder->Damage(this, hit_damage_done, SPELL_UNKNOWN, skillInUse, true, -1, false, m_specialattacks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::CommonBreakInvisibleFromCombat()
|
||||
|
||||
+24
-1
@@ -2090,7 +2090,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
if (focus)
|
||||
{
|
||||
if (WornType){
|
||||
if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) {
|
||||
if (RuleB(Spells, UseAdditiveFocusFromWornSlotWithLimits)) {
|
||||
new_bonus->FocusEffectsWornWithLimits[focus] = spells[spell_id].effect_id[i];
|
||||
}
|
||||
else if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) {
|
||||
new_bonus->FocusEffectsWorn[focus] += spells[spell_id].base_value[i];
|
||||
}
|
||||
}
|
||||
@@ -3298,6 +3301,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Shield_Target:
|
||||
{
|
||||
if (new_bonus->ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT] < effect_value) {
|
||||
new_bonus->ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT] = effect_value;
|
||||
new_bonus->ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT] = buffslot;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_TriggerMeleeThreshold:
|
||||
new_bonus->TriggerMeleeThreshold = true;
|
||||
break;
|
||||
@@ -4989,6 +5001,7 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id)
|
||||
case SE_MaxHPChange:
|
||||
if (negate_spellbonus) { spellbonuses.MaxHPChange = effect_value; }
|
||||
if (negate_aabonus) { aabonuses.MaxHPChange = effect_value; }
|
||||
if (negate_aabonus) { aabonuses.MaxHP = effect_value; }
|
||||
if (negate_itembonus) { itembonuses.MaxHPChange = effect_value; }
|
||||
break;
|
||||
|
||||
@@ -5916,6 +5929,7 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id)
|
||||
if (negate_itembonus) { itembonuses.SkillProcSuccess[e] = effect_value; }
|
||||
if (negate_aabonus) { aabonuses.SkillProcSuccess[e] = effect_value; }
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_SkillProcAttempt: {
|
||||
@@ -5925,6 +5939,15 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id)
|
||||
if (negate_itembonus) { itembonuses.SkillProc[e] = effect_value; }
|
||||
if (negate_aabonus) { aabonuses.SkillProc[e] = effect_value; }
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Shield_Target: {
|
||||
if (negate_spellbonus) {
|
||||
spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_MITIGATION_PERCENT] = effect_value;
|
||||
spellbonuses.ShieldTargetSpa[SBIndex::SHIELD_TARGET_BUFFSLOT] = effect_value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+179
-121
@@ -27,6 +27,7 @@
|
||||
#include "../common/repositories/bot_starting_items_repository.h"
|
||||
#include "../common/data_verification.h"
|
||||
#include "../common/repositories/criteria/content_filter_criteria.h"
|
||||
#include "../common/skill_caps.h"
|
||||
|
||||
// This constructor is used during the bot create command
|
||||
Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), ping_timer(1) {
|
||||
@@ -1171,7 +1172,7 @@ uint16 Bot::GetPrimarySkillValue() {
|
||||
}
|
||||
|
||||
uint16 Bot::MaxSkill(EQ::skills::SkillType skillid, uint16 class_, uint16 level) const {
|
||||
return(content_db.GetSkillCap(class_, skillid, level));
|
||||
return skill_caps.GetSkillCap(class_, skillid, level).cap;
|
||||
}
|
||||
|
||||
uint32 Bot::GetTotalATK() {
|
||||
@@ -2287,7 +2288,7 @@ bool Bot::TryMeditate() {
|
||||
if (!IsMoving() && !spellend_timer.Enabled()) {
|
||||
if (GetTarget() && AI_EngagedCastCheck()) {
|
||||
BotMeditate(false);
|
||||
} else if (GetArchetype() == ARCHETYPE_CASTER) {
|
||||
} else if (GetArchetype() == Archetype::Caster) {
|
||||
BotMeditate(true);
|
||||
}
|
||||
|
||||
@@ -2684,16 +2685,19 @@ bool Bot::IsValidTarget(
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool valid_target_state = (
|
||||
HOLDING ||
|
||||
bool invalid_target_state = false;
|
||||
if (HOLDING ||
|
||||
!tar->IsNPC() ||
|
||||
tar->IsMezzed() ||
|
||||
lo_distance > leash_distance ||
|
||||
tar_distance > leash_distance
|
||||
);
|
||||
const bool valid_target = !GetAttackingFlag() && !CheckLosFN(tar) && !leash_owner->CheckLosFN(tar);
|
||||
tar_distance > leash_distance ||
|
||||
(!GetAttackingFlag() && !CheckLosFN(tar) && !leash_owner->CheckLosFN(tar)) ||
|
||||
!IsAttackAllowed(tar)
|
||||
) {
|
||||
invalid_target_state = true;
|
||||
}
|
||||
|
||||
if (valid_target_state || valid_target || !IsAttackAllowed(tar)) {
|
||||
if (invalid_target_state) {
|
||||
// Normally, we wouldn't want to do this without class checks..but, too many issues can arise if we let enchanter animation pets run rampant
|
||||
if (HasPet()) {
|
||||
GetPet()->RemoveFromHateList(tar);
|
||||
@@ -2752,7 +2756,7 @@ Mob* Bot::GetBotTarget(Client* bot_owner)
|
||||
}
|
||||
}
|
||||
|
||||
if (GetArchetype() == ARCHETYPE_CASTER) {
|
||||
if (GetArchetype() == Archetype::Caster) {
|
||||
BotMeditate(true);
|
||||
}
|
||||
}
|
||||
@@ -3340,7 +3344,7 @@ void Bot::LoadAndSpawnAllZonedBots(Client* bot_owner) {
|
||||
}
|
||||
|
||||
if (bot_spawn_limit >= 0 && spawned_bots_count >= bot_spawn_limit) {
|
||||
database.SetGroupID(b->GetCleanName(), 0, b->GetBotID());
|
||||
Group::RemoveFromGroup(b);
|
||||
g->UpdatePlayer(bot_owner);
|
||||
continue;
|
||||
}
|
||||
@@ -3352,7 +3356,7 @@ void Bot::LoadAndSpawnAllZonedBots(Client* bot_owner) {
|
||||
bot_spawn_limit_class >= 0 &&
|
||||
spawned_bot_count_class >= bot_spawn_limit_class
|
||||
) {
|
||||
database.SetGroupID(b->GetCleanName(), 0, b->GetBotID());
|
||||
Group::RemoveFromGroup(b);
|
||||
g->UpdatePlayer(bot_owner);
|
||||
continue;
|
||||
}
|
||||
@@ -3372,7 +3376,7 @@ void Bot::LoadAndSpawnAllZonedBots(Client* bot_owner) {
|
||||
}
|
||||
|
||||
if (!bot_owner->HasGroup()) {
|
||||
database.SetGroupID(b->GetCleanName(), 0, b->GetBotID());
|
||||
Group::RemoveFromGroup(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3629,8 +3633,8 @@ bool Bot::RemoveBotFromGroup(Bot* bot, Group* group) {
|
||||
bot->SetFollowID(0);
|
||||
if (group->DelMember(bot)) {
|
||||
group->DelMemberOOZ(bot->GetName());
|
||||
database.SetGroupID(bot->GetCleanName(), 0, bot->GetBotID());
|
||||
if (group->GroupCount() < 1) {
|
||||
Group::RemoveFromGroup(bot);
|
||||
if (group->GroupCount() < 2) {
|
||||
group->DisbandGroup();
|
||||
}
|
||||
}
|
||||
@@ -3643,7 +3647,7 @@ bool Bot::RemoveBotFromGroup(Bot* bot, Group* group) {
|
||||
group->members[i]->SetFollowID(0);
|
||||
}
|
||||
group->DisbandGroup();
|
||||
database.SetGroupID(bot->GetCleanName(), 0, bot->GetBotID());
|
||||
Group::RemoveFromGroup(bot);
|
||||
}
|
||||
Result = true;
|
||||
}
|
||||
@@ -5186,31 +5190,26 @@ void Bot::ProcessBotOwnerRefDelete(Mob* botOwner) {
|
||||
}
|
||||
}
|
||||
|
||||
int64 Bot::CalcMaxMana() {
|
||||
switch(GetCasterClass()) {
|
||||
case 'I':
|
||||
max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + aabonuses.Mana + GroupLeadershipAAManaEnhancement());
|
||||
max_mana += itembonuses.heroic_max_mana;
|
||||
case 'W': {
|
||||
max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + aabonuses.Mana + GroupLeadershipAAManaEnhancement());
|
||||
max_mana += itembonuses.heroic_max_mana;
|
||||
break;
|
||||
}
|
||||
case 'N': {
|
||||
max_mana = 0;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogDebug("Invalid Class [{}] in CalcMaxMana", GetCasterClass());
|
||||
max_mana = 0;
|
||||
break;
|
||||
}
|
||||
int64 Bot::CalcMaxMana()
|
||||
{
|
||||
if (IsIntelligenceCasterClass() || IsWisdomCasterClass()) {
|
||||
max_mana = (
|
||||
GenerateBaseManaPoints() +
|
||||
itembonuses.Mana +
|
||||
spellbonuses.Mana +
|
||||
aabonuses.Mana +
|
||||
GroupLeadershipAAManaEnhancement()
|
||||
);
|
||||
max_mana += itembonuses.heroic_max_mana;
|
||||
} else {
|
||||
max_mana = 0;
|
||||
}
|
||||
|
||||
if (current_mana > max_mana)
|
||||
if (current_mana > max_mana) {
|
||||
current_mana = max_mana;
|
||||
else if (max_mana < 0)
|
||||
} else if (max_mana < 0) {
|
||||
max_mana = 0;
|
||||
}
|
||||
|
||||
return max_mana;
|
||||
}
|
||||
@@ -5525,87 +5524,107 @@ bool Bot::DoCastSpell(uint16 spell_id, uint16 target_id, EQ::spells::CastingSlot
|
||||
return Result;
|
||||
}
|
||||
|
||||
int32 Bot::GenerateBaseManaPoints() {
|
||||
int32 bot_mana = 0;
|
||||
int32 WisInt = 0;
|
||||
int32 Bot::GenerateBaseManaPoints()
|
||||
{
|
||||
int32 bot_mana = 0;
|
||||
int32 WisInt = 0;
|
||||
int32 MindLesserFactor, MindFactor;
|
||||
int wisint_mana = 0;
|
||||
int base_mana = 0;
|
||||
int ConvertedWisInt = 0;
|
||||
switch(GetCasterClass()) {
|
||||
case 'I':
|
||||
WisInt = INT;
|
||||
if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
|
||||
if (WisInt > 100) {
|
||||
ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
|
||||
if (WisInt > 201)
|
||||
ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
|
||||
}
|
||||
else
|
||||
ConvertedWisInt = WisInt;
|
||||
int wisint_mana = 0;
|
||||
int base_mana = 0;
|
||||
int ConvertedWisInt = 0;
|
||||
|
||||
if (GetLevel() < 41) {
|
||||
wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000);
|
||||
base_mana = (GetLevel() * 15);
|
||||
} else if (GetLevel() < 81) {
|
||||
wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100));
|
||||
base_mana = (600 + ((GetLevel() - 40) * 30));
|
||||
} else {
|
||||
wisint_mana = (9 * ConvertedWisInt);
|
||||
base_mana = (1800 + ((GetLevel() - 80) * 18));
|
||||
if (IsIntelligenceCasterClass()) {
|
||||
WisInt = INT;
|
||||
|
||||
if (
|
||||
GetOwner() &&
|
||||
GetOwner()->CastToClient() &&
|
||||
GetOwner()->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD &&
|
||||
RuleB(Character, SoDClientUseSoDHPManaEnd)
|
||||
) {
|
||||
if (WisInt > 100) {
|
||||
ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
|
||||
if (WisInt > 201) {
|
||||
ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
|
||||
}
|
||||
bot_mana = (base_mana + wisint_mana);
|
||||
} else {
|
||||
if (((WisInt - 199) / 2) > 0)
|
||||
MindLesserFactor = ((WisInt - 199) / 2);
|
||||
else
|
||||
MindLesserFactor = 0;
|
||||
|
||||
MindFactor = WisInt - MindLesserFactor;
|
||||
if (WisInt > 100)
|
||||
bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
|
||||
else
|
||||
bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
|
||||
ConvertedWisInt = WisInt;
|
||||
}
|
||||
break;
|
||||
case 'W':
|
||||
WisInt = WIS;
|
||||
if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
|
||||
if (WisInt > 100) {
|
||||
ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
|
||||
if (WisInt > 201)
|
||||
ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
|
||||
} else
|
||||
ConvertedWisInt = WisInt;
|
||||
|
||||
if (GetLevel() < 41) {
|
||||
wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000);
|
||||
base_mana = (GetLevel() * 15);
|
||||
} else if (GetLevel() < 81) {
|
||||
wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100));
|
||||
base_mana = (600 + ((GetLevel() - 40) * 30));
|
||||
} else {
|
||||
wisint_mana = (9 * ConvertedWisInt);
|
||||
base_mana = (1800 + ((GetLevel() - 80) * 18));
|
||||
}
|
||||
bot_mana = (base_mana + wisint_mana);
|
||||
if (GetLevel() < 41) {
|
||||
wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000);
|
||||
base_mana = (GetLevel() * 15);
|
||||
} else if (GetLevel() < 81) {
|
||||
wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100));
|
||||
base_mana = (600 + ((GetLevel() - 40) * 30));
|
||||
} else {
|
||||
if (((WisInt - 199) / 2) > 0)
|
||||
MindLesserFactor = ((WisInt - 199) / 2);
|
||||
else
|
||||
MindLesserFactor = 0;
|
||||
|
||||
MindFactor = (WisInt - MindLesserFactor);
|
||||
if (WisInt > 100)
|
||||
bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
|
||||
else
|
||||
bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
|
||||
wisint_mana = (9 * ConvertedWisInt);
|
||||
base_mana = (1800 + ((GetLevel() - 80) * 18));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
bot_mana = 0;
|
||||
break;
|
||||
|
||||
bot_mana = (base_mana + wisint_mana);
|
||||
} else {
|
||||
if (((WisInt - 199) / 2) > 0) {
|
||||
MindLesserFactor = ((WisInt - 199) / 2);
|
||||
} else {
|
||||
MindLesserFactor = 0;
|
||||
}
|
||||
|
||||
MindFactor = WisInt - MindLesserFactor;
|
||||
if (WisInt > 100) {
|
||||
bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
|
||||
} else {
|
||||
bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
|
||||
}
|
||||
}
|
||||
} else if (IsWisdomCasterClass()) {
|
||||
WisInt = WIS;
|
||||
|
||||
if (
|
||||
GetOwner() &&
|
||||
GetOwner()->CastToClient() &&
|
||||
GetOwner()->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD &&
|
||||
RuleB(Character, SoDClientUseSoDHPManaEnd)
|
||||
) {
|
||||
if (WisInt > 100) {
|
||||
ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100);
|
||||
if (WisInt > 201) {
|
||||
ConvertedWisInt -= ((WisInt - 201) * 5 / 4);
|
||||
}
|
||||
} else {
|
||||
ConvertedWisInt = WisInt;
|
||||
}
|
||||
|
||||
if (GetLevel() < 41) {
|
||||
wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000);
|
||||
base_mana = (GetLevel() * 15);
|
||||
} else if (GetLevel() < 81) {
|
||||
wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100));
|
||||
base_mana = (600 + ((GetLevel() - 40) * 30));
|
||||
} else {
|
||||
wisint_mana = (9 * ConvertedWisInt);
|
||||
base_mana = (1800 + ((GetLevel() - 80) * 18));
|
||||
}
|
||||
|
||||
bot_mana = (base_mana + wisint_mana);
|
||||
} else {
|
||||
if (((WisInt - 199) / 2) > 0) {
|
||||
MindLesserFactor = ((WisInt - 199) / 2);
|
||||
} else {
|
||||
MindLesserFactor = 0;
|
||||
}
|
||||
|
||||
MindFactor = (WisInt - MindLesserFactor);
|
||||
if (WisInt > 100) {
|
||||
bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
|
||||
} else {
|
||||
bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bot_mana = 0;
|
||||
}
|
||||
|
||||
max_mana = bot_mana;
|
||||
return bot_mana;
|
||||
}
|
||||
@@ -6216,11 +6235,10 @@ int64 Bot::CalcMaxHP() {
|
||||
uint32 nd = 10000;
|
||||
bot_hp += (GenerateBaseHitPoints() + itembonuses.HP);
|
||||
bot_hp += itembonuses.heroic_max_hp;
|
||||
nd += aabonuses.MaxHP;
|
||||
nd += aabonuses.MaxHP + spellbonuses.MaxHPChange + itembonuses.MaxHPChange;
|
||||
bot_hp = ((float)bot_hp * (float)nd / (float)10000);
|
||||
bot_hp += (spellbonuses.HP + aabonuses.HP);
|
||||
bot_hp += GroupLeadershipAAHealthEnhancement();
|
||||
bot_hp += (bot_hp * ((spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000.0f));
|
||||
max_hp = bot_hp;
|
||||
if (current_hp > max_hp)
|
||||
current_hp = max_hp;
|
||||
@@ -6582,14 +6600,14 @@ void Bot::ProcessBotGroupInvite(Client* c, std::string const& botName) {
|
||||
entity_list.AddGroup(g);
|
||||
database.SetGroupLeaderName(g->GetID(), c->GetName());
|
||||
g->SaveGroupLeaderAA();
|
||||
database.SetGroupID(c->GetName(), g->GetID(), c->CharacterID());
|
||||
database.SetGroupID(invitedBot->GetCleanName(), g->GetID(), invitedBot->GetBotID());
|
||||
g->AddToGroup(c);
|
||||
g->AddToGroup(invitedBot);
|
||||
} else {
|
||||
delete g;
|
||||
}
|
||||
} else {
|
||||
if (AddBotToGroup(invitedBot, c->GetGroup())) {
|
||||
database.SetGroupID(invitedBot->GetCleanName(), c->GetGroup()->GetID(), invitedBot->GetBotID());
|
||||
c->GetGroup()->AddToGroup(invitedBot);
|
||||
}
|
||||
}
|
||||
} else if (invitedBot->HasGroup()) {
|
||||
@@ -6740,7 +6758,7 @@ void Bot::CalcBotStats(bool showtext) {
|
||||
SetLevel(GetBotOwner()->GetLevel());
|
||||
|
||||
for (int sindex = 0; sindex <= EQ::skills::HIGHEST_SKILL; ++sindex) {
|
||||
skills[sindex] = content_db.GetSkillCap(GetClass(), (EQ::skills::SkillType)sindex, GetLevel());
|
||||
skills[sindex] = skill_caps.GetSkillCap(GetClass(), (EQ::skills::SkillType)sindex, GetLevel()).cap;
|
||||
}
|
||||
|
||||
taunt_timer.Start(1000);
|
||||
@@ -7640,18 +7658,58 @@ void Bot::SetDefaultBotStance() {
|
||||
_botStance = defaultStance;
|
||||
}
|
||||
|
||||
void Bot::BotGroupSay(Mob *speaker, const char *msg, ...) {
|
||||
void Bot::BotGroupSay(Mob* speaker, const char* msg, ...) {
|
||||
char buf[1000];
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(buf, 1000, msg, ap);
|
||||
va_end(ap);
|
||||
if (speaker->HasGroup()) {
|
||||
Group *g = speaker->GetGroup();
|
||||
if (g)
|
||||
g->GroupMessage(speaker->CastToMob(), Language::CommonTongue, Language::MaxValue, buf);
|
||||
} else
|
||||
speaker->Say("%s", buf);
|
||||
|
||||
if (speaker->IsRaidGrouped()) {
|
||||
Raid* r = entity_list.GetRaidByBotName(speaker->GetName());
|
||||
if (r) {
|
||||
for (const auto& m : r->members) {
|
||||
if (m.member && !m.is_bot) {
|
||||
m.member->FilteredMessageString(
|
||||
speaker,
|
||||
Chat::PetResponse,
|
||||
FilterSocials,
|
||||
GENERIC_SAY,
|
||||
speaker->GetCleanName(),
|
||||
buf
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (speaker->HasGroup()) {
|
||||
Group* g = speaker->GetGroup();
|
||||
if (g) {
|
||||
for (auto& m : g->members) {
|
||||
if (m && !m->IsBot()) {
|
||||
m->FilteredMessageString(
|
||||
speaker,
|
||||
Chat::PetResponse,
|
||||
FilterSocials,
|
||||
GENERIC_SAY,
|
||||
speaker->GetCleanName(),
|
||||
buf
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
//speaker->Say("%s", buf);
|
||||
speaker->GetOwner()->FilteredMessageString(
|
||||
speaker,
|
||||
Chat::PetResponse,
|
||||
FilterSocials,
|
||||
GENERIC_SAY,
|
||||
speaker->GetCleanName(),
|
||||
buf
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
bool Bot::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
|
||||
+6
-33
@@ -184,35 +184,11 @@ bool BotDatabase::QueryNameAvailablity(const std::string& bot_name, bool& availa
|
||||
if (
|
||||
bot_name.empty() ||
|
||||
bot_name.size() > 60 ||
|
||||
!database.CheckUsedName(bot_name)
|
||||
database.IsNameUsed(bot_name)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& bot_data = BotDataRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`name` LIKE '{}' LIMIT 1",
|
||||
bot_name
|
||||
)
|
||||
);
|
||||
|
||||
if (!bot_data.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& character_data = CharacterDataRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`name` LIKE '{}' LIMIT 1",
|
||||
bot_name
|
||||
)
|
||||
);
|
||||
|
||||
if (!character_data.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
available_flag = true;
|
||||
|
||||
return true;
|
||||
@@ -1788,7 +1764,8 @@ bool BotDatabase::CreateCloneBotInventory(const uint32 bot_id, const uint32 clon
|
||||
}
|
||||
|
||||
for (auto& e : l) {
|
||||
e.bot_id = clone_id;
|
||||
e.inventories_index = 0;
|
||||
e.bot_id = clone_id;
|
||||
}
|
||||
|
||||
return BotInventoriesRepository::InsertMany(database, l);
|
||||
@@ -1921,18 +1898,14 @@ bool BotDatabase::LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 g
|
||||
const auto& l = GroupIdRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`groupid` = {} AND `name` IN (SELECT `name` FROM `bot_data` WHERE `owner_id` = {})",
|
||||
"`group_id` = {} AND `bot_id` != 0 AND `name` IN (SELECT `name` FROM `bot_data` WHERE `owner_id` = {})",
|
||||
group_id,
|
||||
owner_id
|
||||
)
|
||||
);
|
||||
|
||||
if (l.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& e : l) {
|
||||
group_list.push_back(e.charid);
|
||||
group_list.emplace_back(e.bot_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -2367,7 +2340,7 @@ const uint8 BotDatabase::GetBotLevelByID(const uint32 bot_id)
|
||||
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);
|
||||
|
||||
|
||||
+1
-1
@@ -164,7 +164,7 @@ public:
|
||||
const uint8 GetBotGenderByID(const uint32 bot_id);
|
||||
std::vector<uint32> GetBotIDsByCharacterID(const uint32 character_id, uint8 class_id = Class::None);
|
||||
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);
|
||||
|
||||
class fail {
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ void Raid::HandleBotGroupDisband(uint32 owner, uint32 gid)
|
||||
auto r_group_members = GetRaidGroupMembers(GetGroup(b->GetName()));
|
||||
auto g = new Group(b);
|
||||
entity_list.AddGroup(g);
|
||||
database.SetGroupID(b->GetCleanName(), g->GetID(), b->GetBotID());
|
||||
g->AddToGroup(b);
|
||||
database.SetGroupLeaderName(g->GetID(), b->GetName());
|
||||
|
||||
for (auto m: r_group_members) {
|
||||
|
||||
@@ -591,7 +591,7 @@ bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
|
||||
//Only check archetype if spell is not a group spell
|
||||
//Hybrids get all buffs
|
||||
switch (tar->GetArchetype()) {
|
||||
case ARCHETYPE_CASTER:
|
||||
case Archetype::Caster:
|
||||
//TODO: probably more caster specific spell effects in here
|
||||
if (
|
||||
(
|
||||
@@ -606,7 +606,7 @@ bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case ARCHETYPE_MELEE:
|
||||
case Archetype::Melee:
|
||||
if (
|
||||
(
|
||||
IsEffectInSpell(s.SpellId, SE_IncreaseSpellHaste) ||
|
||||
@@ -899,7 +899,7 @@ bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
|
||||
|
||||
switch (tar->GetArchetype())
|
||||
{
|
||||
case ARCHETYPE_CASTER:
|
||||
case Archetype::Caster:
|
||||
//TODO: probably more caster specific spell effects in here
|
||||
if (IsEffectInSpell(s.SpellId, SE_AttackSpeed) || IsEffectInSpell(s.SpellId, SE_ATK) ||
|
||||
IsEffectInSpell(s.SpellId, SE_STR) || IsEffectInSpell(s.SpellId, SE_ReverseDS))
|
||||
@@ -907,7 +907,7 @@ bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case ARCHETYPE_MELEE:
|
||||
case Archetype::Melee:
|
||||
if (IsEffectInSpell(s.SpellId, SE_IncreaseSpellHaste) || IsEffectInSpell(s.SpellId, SE_ManaPool) ||
|
||||
IsEffectInSpell(s.SpellId, SE_CastingLevel) || IsEffectInSpell(s.SpellId, SE_ManaRegen_v2) ||
|
||||
IsEffectInSpell(s.SpellId, SE_CurrentMana))
|
||||
|
||||
+256
-68
@@ -57,6 +57,7 @@ extern volatile bool RunLoops;
|
||||
#include "queryserv.h"
|
||||
#include "mob_movement_manager.h"
|
||||
#include "cheat_manager.h"
|
||||
#include "lua_parser.h"
|
||||
|
||||
#include "../common/repositories/character_alternate_abilities_repository.h"
|
||||
#include "../common/repositories/account_flags_repository.h"
|
||||
@@ -68,10 +69,12 @@ extern volatile bool RunLoops;
|
||||
#include "../common/repositories/discovered_items_repository.h"
|
||||
#include "../common/repositories/inventory_repository.h"
|
||||
#include "../common/repositories/keyring_repository.h"
|
||||
#include "../common/repositories/tradeskill_recipe_repository.h"
|
||||
#include "../common/events/player_events.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "dialogue_window.h"
|
||||
#include "../common/zone_store.h"
|
||||
#include "../common/skill_caps.h"
|
||||
|
||||
|
||||
extern QueryServ* QServ;
|
||||
@@ -284,6 +287,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
PendingSacrifice = false;
|
||||
controlling_boat_id = 0;
|
||||
controlled_mob_id = 0;
|
||||
qGlobals = nullptr;
|
||||
|
||||
if (!RuleB(Character, PerCharacterQglobalMaxLevel) && !RuleB(Character, PerCharacterBucketMaxLevel)) {
|
||||
SetClientMaxLevel(0);
|
||||
@@ -311,7 +315,6 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
aa_los_them_mob = nullptr;
|
||||
los_status = false;
|
||||
los_status_facing = false;
|
||||
qGlobals = nullptr;
|
||||
HideCorpseMode = HideCorpseNone;
|
||||
PendingGuildInvitation = false;
|
||||
|
||||
@@ -2225,8 +2228,8 @@ void Client::ChangeLastName(std::string last_name) {
|
||||
bool Client::ChangeFirstName(const char* in_firstname, const char* gmname)
|
||||
{
|
||||
// check duplicate name
|
||||
bool usedname = database.CheckUsedName((const char*) in_firstname);
|
||||
if (!usedname) {
|
||||
bool used_name = database.IsNameUsed((const char*) in_firstname);
|
||||
if (used_name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2294,29 +2297,23 @@ void Client::ReadBook(BookRequest_Struct *book) {
|
||||
|
||||
BookText_Struct *out = (BookText_Struct *) outapp->pBuffer;
|
||||
out->window = book->window;
|
||||
|
||||
|
||||
if (ClientVersion() >= EQ::versions::ClientVersion::SoF) {
|
||||
// SoF+ need to look up book type for the output message.
|
||||
const EQ::ItemInstance *inst = nullptr;
|
||||
|
||||
if (book->invslot <= EQ::invbag::GENERAL_BAGS_END)
|
||||
{
|
||||
inst = m_inv[book->invslot];
|
||||
}
|
||||
|
||||
if(inst)
|
||||
out->type = inst->GetItem()->Book;
|
||||
else
|
||||
out->type = book->type;
|
||||
}
|
||||
else {
|
||||
out->type = book->type;
|
||||
}
|
||||
out->type = book->type;
|
||||
out->invslot = book->invslot;
|
||||
out->target_id = book->target_id;
|
||||
out->can_cast = 0; // todo: implement
|
||||
out->can_scribe = 0; // todo: implement
|
||||
out->can_scribe = false;
|
||||
|
||||
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && book->invslot <= EQ::invbag::GENERAL_BAGS_END)
|
||||
{
|
||||
const EQ::ItemInstance* inst = m_inv[book->invslot];
|
||||
if (inst && inst->GetItem())
|
||||
{
|
||||
auto recipe = TradeskillRecipeRepository::GetWhere(content_db,
|
||||
fmt::format("learned_by_item_id = {} LIMIT 1", inst->GetItem()->ID));
|
||||
out->type = inst->GetItem()->Book;
|
||||
out->can_scribe = !recipe.empty();
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(out->booktext, booktxt2.c_str(), length);
|
||||
|
||||
@@ -2761,31 +2758,48 @@ void Client::CheckLanguageSkillIncrease(uint8 language_id, uint8 teacher_skill)
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::HasSkill(EQ::skills::SkillType skill_id) const {
|
||||
return((GetSkill(skill_id) > 0) && CanHaveSkill(skill_id));
|
||||
}
|
||||
|
||||
bool Client::CanHaveSkill(EQ::skills::SkillType skill_id) const {
|
||||
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == Class::Berserker && skill_id == EQ::skills::Skill1HPiercing)
|
||||
skill_id = EQ::skills::Skill2HPiercing;
|
||||
|
||||
return(content_db.GetSkillCap(GetClass(), skill_id, RuleI(Character, MaxLevel)) > 0);
|
||||
//if you don't have it by max level, then odds are you never will?
|
||||
}
|
||||
|
||||
uint16 Client::MaxSkill(EQ::skills::SkillType skillid, uint16 class_, uint16 level) const {
|
||||
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == Class::Berserker && skillid == EQ::skills::Skill1HPiercing)
|
||||
skillid = EQ::skills::Skill2HPiercing;
|
||||
|
||||
return(content_db.GetSkillCap(class_, skillid, level));
|
||||
}
|
||||
|
||||
uint8 Client::SkillTrainLevel(EQ::skills::SkillType skillid, uint16 class_)
|
||||
bool Client::HasSkill(EQ::skills::SkillType skill_id) const
|
||||
{
|
||||
if (ClientVersion() < EQ::versions::ClientVersion::RoF2 && class_ == Class::Berserker && skillid == EQ::skills::Skill1HPiercing)
|
||||
skillid = EQ::skills::Skill2HPiercing;
|
||||
return GetSkill(skill_id) > 0 && CanHaveSkill(skill_id);
|
||||
}
|
||||
|
||||
return(content_db.GetTrainLevel(class_, skillid, RuleI(Character, MaxLevel)));
|
||||
bool Client::CanHaveSkill(EQ::skills::SkillType skill_id) const
|
||||
{
|
||||
if (
|
||||
ClientVersion() < EQ::versions::ClientVersion::RoF2 &&
|
||||
class_ == Class::Berserker &&
|
||||
skill_id == EQ::skills::Skill1HPiercing
|
||||
) {
|
||||
skill_id = EQ::skills::Skill2HPiercing;
|
||||
}
|
||||
|
||||
return skill_caps.GetSkillCap(GetClass(), skill_id, RuleI(Character, MaxLevel)).cap > 0;
|
||||
}
|
||||
|
||||
uint16 Client::MaxSkill(EQ::skills::SkillType skill_id, uint8 class_id, uint8 level) const
|
||||
{
|
||||
if (
|
||||
ClientVersion() < EQ::versions::ClientVersion::RoF2 &&
|
||||
class_id == Class::Berserker &&
|
||||
skill_id == EQ::skills::Skill1HPiercing
|
||||
) {
|
||||
skill_id = EQ::skills::Skill2HPiercing;
|
||||
}
|
||||
|
||||
return skill_caps.GetSkillCap(class_id, skill_id, level).cap;
|
||||
}
|
||||
|
||||
uint8 Client::SkillTrainLevel(EQ::skills::SkillType skill_id, uint8 class_id)
|
||||
{
|
||||
if (
|
||||
ClientVersion() < EQ::versions::ClientVersion::RoF2 &&
|
||||
class_id == Class::Berserker &&
|
||||
skill_id == EQ::skills::Skill1HPiercing
|
||||
) {
|
||||
skill_id = EQ::skills::Skill2HPiercing;
|
||||
}
|
||||
|
||||
return skill_caps.GetTrainLevel(class_id, skill_id, RuleI(Character, MaxLevel));
|
||||
}
|
||||
|
||||
uint16 Client::GetMaxSkillAfterSpecializationRules(EQ::skills::SkillType skillid, uint16 maxSkill)
|
||||
@@ -3791,14 +3805,12 @@ void Client::GetRaidAAs(RaidLeadershipAA_Struct *into) const {
|
||||
|
||||
void Client::EnteringMessages(Client* client)
|
||||
{
|
||||
std::string rules;
|
||||
if (database.GetVariable("Rules", rules)) {
|
||||
uint8 flag = database.GetAgreementFlag(client->AccountID());
|
||||
std::string rules = RuleS(World, Rules);
|
||||
|
||||
if (!rules.empty() || database.GetVariable("Rules", rules)) {
|
||||
const uint8 flag = database.GetAgreementFlag(client->AccountID());
|
||||
if (!flag) {
|
||||
auto rules_link = Saylink::Silent(
|
||||
"#serverrules",
|
||||
"rules"
|
||||
);
|
||||
const std::string& rules_link = Saylink::Silent("#serverrules", "rules");
|
||||
|
||||
client->Message(
|
||||
Chat::White,
|
||||
@@ -3815,9 +3827,9 @@ void Client::EnteringMessages(Client* client)
|
||||
|
||||
void Client::SendRules()
|
||||
{
|
||||
std::string rules;
|
||||
std::string rules = RuleS(World, Rules);
|
||||
|
||||
if (!database.GetVariable("Rules", rules)) {
|
||||
if (rules.empty() && !database.GetVariable("Rules", rules)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4449,7 +4461,7 @@ bool Client::GroupFollow(Client* inviter) {
|
||||
}
|
||||
|
||||
//now we have a group id, can set inviter's id
|
||||
database.SetGroupID(inviter->GetName(), group->GetID(), inviter->CharacterID(), false);
|
||||
group->AddToGroup(inviter);
|
||||
database.SetGroupLeaderName(group->GetID(), inviter->GetName());
|
||||
group->UpdateGroupAAs();
|
||||
|
||||
@@ -6285,7 +6297,17 @@ void Client::SendZonePoints()
|
||||
zp->zpe[i].z = data->target_z;
|
||||
zp->zpe[i].heading = data->target_heading;
|
||||
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++;
|
||||
}
|
||||
iterator.Advance();
|
||||
@@ -6610,13 +6632,72 @@ void Client::SendAltCurrencies() {
|
||||
|
||||
void Client::SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount)
|
||||
{
|
||||
if (!zone->DoesAlternateCurrencyExist(currency_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32 current_amount = alternate_currency[currency_id];
|
||||
|
||||
const bool is_gain = new_amount > current_amount;
|
||||
|
||||
const uint32 change_amount = is_gain ? (new_amount - current_amount) : (current_amount - new_amount);
|
||||
|
||||
if (!change_amount) {
|
||||
return;
|
||||
}
|
||||
|
||||
alternate_currency[currency_id] = new_amount;
|
||||
database.UpdateAltCurrencyValue(CharacterID(), currency_id, new_amount);
|
||||
SendAlternateCurrencyValue(currency_id);
|
||||
|
||||
QuestEventID event_id = is_gain ? EVENT_ALT_CURRENCY_GAIN : EVENT_ALT_CURRENCY_LOSS;
|
||||
if (parse->PlayerHasQuestSub(event_id)) {
|
||||
const std::string &export_string = fmt::format(
|
||||
"{} {} {}",
|
||||
currency_id,
|
||||
change_amount,
|
||||
new_amount
|
||||
);
|
||||
|
||||
parse->EventPlayer(event_id, this, export_string, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool Client::RemoveAlternateCurrencyValue(uint32 currency_id, uint32 amount)
|
||||
{
|
||||
if (!amount || !zone->DoesAlternateCurrencyExist(currency_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32 current_amount = alternate_currency[currency_id];
|
||||
if (current_amount < amount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32 new_amount = (current_amount - amount);
|
||||
|
||||
alternate_currency[currency_id] = new_amount;
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_ALT_CURRENCY_LOSS)) {
|
||||
const std::string &export_string = fmt::format(
|
||||
"{} {} {}",
|
||||
currency_id,
|
||||
amount,
|
||||
new_amount
|
||||
);
|
||||
|
||||
parse->EventPlayer(EVENT_ALT_CURRENCY_LOSS, this, export_string, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int Client::AddAlternateCurrencyValue(uint32 currency_id, int amount, bool is_scripted)
|
||||
{
|
||||
if (!zone->DoesAlternateCurrencyExist(currency_id)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Added via Quest, rest of the logging methods may be done inline due to information available in that area of the code */
|
||||
if (is_scripted) {
|
||||
/* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */
|
||||
@@ -6655,7 +6736,6 @@ int Client::AddAlternateCurrencyValue(uint32 currency_id, int amount, bool is_sc
|
||||
SendAlternateCurrencyValue(currency_id);
|
||||
|
||||
QuestEventID event_id = amount > 0 ? EVENT_ALT_CURRENCY_GAIN : EVENT_ALT_CURRENCY_LOSS;
|
||||
|
||||
if (parse->PlayerHasQuestSub(event_id)) {
|
||||
const std::string &export_string = fmt::format(
|
||||
"{} {} {}",
|
||||
@@ -6696,6 +6776,10 @@ void Client::SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null)
|
||||
|
||||
uint32 Client::GetAlternateCurrencyValue(uint32 currency_id) const
|
||||
{
|
||||
if (!zone->DoesAlternateCurrencyExist(currency_id)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto iter = alternate_currency.find(currency_id);
|
||||
|
||||
return iter == alternate_currency.end() ? 0 : (*iter).second;
|
||||
@@ -6765,23 +6849,36 @@ void Client::UpdateClientXTarget(Client *c)
|
||||
// IT IS NOT SAFE TO CALL THIS IF IT'S NOT INITIAL AGGRO
|
||||
void Client::AddAutoXTarget(Mob *m, bool send)
|
||||
{
|
||||
if (m->IsBot() || ((m->IsPet() || m->IsTempPet()) && m->IsPetOwnerBot())) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_activeautohatermgr->increment_count(m);
|
||||
|
||||
if (!XTargettingAvailable() || !XTargetAutoAddHaters || IsXTarget(m))
|
||||
if (!XTargettingAvailable() || !XTargetAutoAddHaters || IsXTarget(m)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0; i < GetMaxXTargets(); ++i)
|
||||
{
|
||||
if((XTargets[i].Type == Auto) && (XTargets[i].ID == 0))
|
||||
{
|
||||
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
||||
if (XTargets[i].Type == Auto && XTargets[i].ID == 0) {
|
||||
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);
|
||||
else
|
||||
} else {
|
||||
XTargets[i].dirty = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LogXTargets(
|
||||
"Adding [{}] to [{}] ({}) XTargets",
|
||||
m->GetCleanName(),
|
||||
GetCleanName(),
|
||||
GetID()
|
||||
);
|
||||
}
|
||||
|
||||
void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots)
|
||||
@@ -6790,15 +6887,23 @@ void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots)
|
||||
// now we may need to clean up our CurrentTargetNPC entries
|
||||
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
||||
if (XTargets[i].Type == CurrentTargetNPC && XTargets[i].ID == m->GetID()) {
|
||||
XTargets[i].Type = Auto;
|
||||
XTargets[i].ID = 0;
|
||||
XTargets[i].Type = Auto;
|
||||
XTargets[i].ID = 0;
|
||||
XTargets[i].dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto r = GetRaid();
|
||||
if (r) {
|
||||
r->UpdateRaidXTargets();
|
||||
}
|
||||
|
||||
LogXTargets(
|
||||
"Removing [{}] from [{}] ({}) XTargets",
|
||||
m->GetCleanName(),
|
||||
GetCleanName(),
|
||||
GetID()
|
||||
);
|
||||
}
|
||||
|
||||
void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name)
|
||||
@@ -9140,6 +9245,7 @@ void Client::ShowDevToolsMenu()
|
||||
menu_reload_six += " | " + Saylink::Silent("#reload quest", "Quests");
|
||||
|
||||
menu_reload_seven += Saylink::Silent("#reload rules", "Rules");
|
||||
menu_reload_seven += " | " + Saylink::Silent("#reload skill_caps", "Skill Caps");
|
||||
menu_reload_seven += " | " + Saylink::Silent("#reload static", "Static Zone Data");
|
||||
menu_reload_seven += " | " + Saylink::Silent("#reload tasks", "Tasks");
|
||||
|
||||
@@ -11267,6 +11373,16 @@ void Client::SendReloadCommandMessages() {
|
||||
).c_str()
|
||||
);
|
||||
|
||||
auto skill_caps_link = Saylink::Silent("#reload skill_caps");
|
||||
|
||||
Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Usage: {} - Reloads Skill Caps globally",
|
||||
skill_caps_link
|
||||
).c_str()
|
||||
);
|
||||
|
||||
auto static_link = Saylink::Silent("#reload static");
|
||||
|
||||
Message(
|
||||
@@ -11442,6 +11558,39 @@ void Client::SetLockSavePosition(bool lock_save_position)
|
||||
Client::m_lock_save_position = lock_save_position;
|
||||
}
|
||||
|
||||
void Client::SetAAPoints(uint32 points)
|
||||
{
|
||||
const uint32 current_points = m_pp.aapoints;
|
||||
|
||||
m_pp.aapoints = points;
|
||||
|
||||
QuestEventID event_id = points > current_points ? EVENT_AA_GAIN : EVENT_AA_LOSS;
|
||||
const uint32 change = event_id == EVENT_AA_GAIN ? points - current_points : current_points - points;
|
||||
|
||||
if (parse->PlayerHasQuestSub(event_id)) {
|
||||
parse->EventPlayer(event_id, this, std::to_string(change), 0);
|
||||
}
|
||||
|
||||
SendAlternateAdvancementStats();
|
||||
}
|
||||
|
||||
bool Client::RemoveAAPoints(uint32 points)
|
||||
{
|
||||
if (m_pp.aapoints < points) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pp.aapoints -= points;
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_AA_LOSS)) {
|
||||
parse->EventPlayer(EVENT_AA_LOSS, this, std::to_string(points), 0);
|
||||
}
|
||||
|
||||
SendAlternateAdvancementStats();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::AddAAPoints(uint32 points)
|
||||
{
|
||||
m_pp.aapoints += points;
|
||||
@@ -11499,6 +11648,14 @@ void Client::RegisterBug(BugReport_Struct* r) {
|
||||
b.bug_report = r->bug_report;
|
||||
b.system_info = r->system_info;
|
||||
|
||||
#ifdef LUA_EQEMU
|
||||
bool ignore_default = false;
|
||||
LuaParser::Instance()->RegisterBug(this, b, ignore_default);
|
||||
if (ignore_default) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto n = BugReportsRepository::InsertOne(database, b);
|
||||
if (!n.id) {
|
||||
Message(Chat::White, "Failed to created your bug report."); // Client sends success message
|
||||
@@ -11648,7 +11805,7 @@ void Client::MaxSkills()
|
||||
auto current_skill_value = (
|
||||
EQ::skills::IsSpecializedSkill(s.first) ?
|
||||
MAX_SPECIALIZED_SKILL :
|
||||
content_db.GetSkillCap(GetClass(), s.first, GetLevel())
|
||||
skill_caps.GetSkillCap(GetClass(), s.first, GetLevel()).cap
|
||||
);
|
||||
|
||||
if (GetSkill(s.first) < current_skill_value) {
|
||||
@@ -12194,3 +12351,34 @@ int Client::GetEXPPercentage()
|
||||
|
||||
return static_cast<int>(std::round(scaled * 100.0 / 330.0)); // unscaled pct
|
||||
}
|
||||
|
||||
std::vector<Mob*> Client::GetRaidOrGroupOrSelf(bool clients_only)
|
||||
{
|
||||
std::vector<Mob*> v;
|
||||
|
||||
if (IsRaidGrouped()) {
|
||||
Raid* r = GetRaid();
|
||||
|
||||
if (r) {
|
||||
for (const auto& m : r->members) {
|
||||
if (m.member && (!m.is_bot || !clients_only)) {
|
||||
v.emplace_back(m.member);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (IsGrouped()) {
|
||||
Group* g = GetGroup();
|
||||
|
||||
if (g) {
|
||||
for (const auto& m : g->members) {
|
||||
if (m && (m->IsClient() || !clients_only)) {
|
||||
v.emplace_back(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
v.emplace_back(this);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
+10
-5
@@ -272,6 +272,8 @@ public:
|
||||
int GetQuiverHaste(int delay);
|
||||
void DoAttackRounds(Mob *target, int hand, bool IsFromSpell = false);
|
||||
|
||||
std::vector<Mob*> GetRaidOrGroupOrSelf(bool clients_only = false);
|
||||
|
||||
void AI_Init();
|
||||
void AI_Start(uint32 iMoveDelay = 0);
|
||||
void AI_Stop();
|
||||
@@ -348,6 +350,7 @@ public:
|
||||
int GetRecipeMadeCount(uint32 recipe_id);
|
||||
bool HasRecipeLearned(uint32 recipe_id);
|
||||
bool CanIncreaseTradeskill(EQ::skills::SkillType tradeskill);
|
||||
void ScribeRecipes(uint32_t item_id) const;
|
||||
|
||||
bool GetRevoked() const { return revoked; }
|
||||
void SetRevoked(bool rev) { revoked = rev; }
|
||||
@@ -812,9 +815,9 @@ public:
|
||||
void SetHoTT(uint32 mobid);
|
||||
void ShowSkillsWindow();
|
||||
|
||||
uint16 MaxSkill(EQ::skills::SkillType skillid, uint16 class_, uint16 level) const;
|
||||
inline uint16 MaxSkill(EQ::skills::SkillType skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); }
|
||||
uint8 SkillTrainLevel(EQ::skills::SkillType skillid, uint16 class_);
|
||||
uint16 MaxSkill(EQ::skills::SkillType skill_id, uint8 class_id, uint8 level) const;
|
||||
inline uint16 MaxSkill(EQ::skills::SkillType skill_id) const { return MaxSkill(skill_id, GetClass(), GetLevel()); }
|
||||
uint8 SkillTrainLevel(EQ::skills::SkillType skill_id, uint8 class_id);
|
||||
void MaxSkills();
|
||||
|
||||
void SendTradeskillSearchResults(const std::string &query, unsigned long objtype, unsigned long someid);
|
||||
@@ -933,8 +936,9 @@ public:
|
||||
void ResetAlternateAdvancementTimers();
|
||||
void ResetOnDeathAlternateAdvancement();
|
||||
|
||||
void SetAAPoints(uint32 points) { m_pp.aapoints = points; SendAlternateAdvancementStats(); }
|
||||
void SetAAPoints(uint32 points);
|
||||
void AddAAPoints(uint32 points);
|
||||
bool RemoveAAPoints(uint32 points);
|
||||
int GetAAPoints() { return m_pp.aapoints; }
|
||||
int GetSpentAA() { return m_pp.aapoints_spent; }
|
||||
uint32 GetRequiredAAExperience();
|
||||
@@ -1090,7 +1094,7 @@ public:
|
||||
void SetPEQZoneFlag(uint32 zone_id);
|
||||
|
||||
bool CanFish();
|
||||
void GoFish();
|
||||
void GoFish(bool guarantee = false, bool use_bait = true);
|
||||
void ForageItem(bool guarantee = false);
|
||||
//Calculate vendor price modifier based on CHA: (reverse==selling)
|
||||
float CalcPriceMod(Mob* other = 0, bool reverse = false);
|
||||
@@ -1539,6 +1543,7 @@ public:
|
||||
void SendAltCurrencies();
|
||||
void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount);
|
||||
int AddAlternateCurrencyValue(uint32 currency_id, int amount, bool is_scripted = false);
|
||||
bool RemoveAlternateCurrencyValue(uint32 currency_id, uint32 amount);
|
||||
void SendAlternateCurrencyValues();
|
||||
void SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null = true);
|
||||
uint32 GetAlternateCurrencyValue(uint32 currency_id) const;
|
||||
|
||||
+86
-93
@@ -322,11 +322,12 @@ int64 Client::CalcMaxHP()
|
||||
//but the actual effect sent on live causes the client
|
||||
//to apply it to (basehp + itemhp).. I will oblige to the client's whims over
|
||||
//the aa description
|
||||
nd += aabonuses.MaxHP; //Natural Durability, Physical Enhancement, Planar Durability
|
||||
|
||||
nd += aabonuses.MaxHP + spellbonuses.MaxHPChange + itembonuses.MaxHPChange; //Natural Durability, Physical Enhancement, Planar Durability (MaxHP and MaxHPChange are SPA214)
|
||||
max_hp = (float)max_hp * (float)nd / (float)10000; //this is to fix the HP-above-495k issue
|
||||
max_hp += spellbonuses.HP + aabonuses.HP;
|
||||
|
||||
max_hp += GroupLeadershipAAHealthEnhancement();
|
||||
max_hp += max_hp * ((spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000.0f);
|
||||
if (current_hp > max_hp) {
|
||||
current_hp = max_hp;
|
||||
}
|
||||
@@ -523,28 +524,26 @@ int32 Client::GetRawItemAC()
|
||||
|
||||
int64 Client::CalcMaxMana()
|
||||
{
|
||||
switch (GetCasterClass()) {
|
||||
case 'I':
|
||||
case 'W': {
|
||||
max_mana = (CalcBaseMana() + itembonuses.Mana + spellbonuses.Mana + aabonuses.Mana + GroupLeadershipAAManaEnhancement());
|
||||
break;
|
||||
}
|
||||
case 'N': {
|
||||
max_mana = 0;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogSpells("Invalid Class [{}] in CalcMaxMana", GetCasterClass());
|
||||
max_mana = 0;
|
||||
break;
|
||||
}
|
||||
if (IsIntelligenceCasterClass() || IsWisdomCasterClass()) {
|
||||
max_mana = (
|
||||
CalcBaseMana() +
|
||||
itembonuses.Mana +
|
||||
spellbonuses.Mana +
|
||||
aabonuses.Mana +
|
||||
GroupLeadershipAAManaEnhancement()
|
||||
);
|
||||
} else {
|
||||
max_mana = 0;
|
||||
}
|
||||
|
||||
if (max_mana < 0) {
|
||||
max_mana = 0;
|
||||
}
|
||||
|
||||
if (current_mana > max_mana) {
|
||||
current_mana = max_mana;
|
||||
}
|
||||
|
||||
int mana_perc_cap = spellbonuses.ManaPercCap[SBIndex::RESOURCE_PERCENT_CAP];
|
||||
if (mana_perc_cap) {
|
||||
int curMana_cap = (max_mana * mana_perc_cap) / 100;
|
||||
@@ -552,96 +551,90 @@ int64 Client::CalcMaxMana()
|
||||
current_mana = curMana_cap;
|
||||
}
|
||||
}
|
||||
|
||||
LogSpells("for [{}] returning [{}]", GetName(), max_mana);
|
||||
return max_mana;
|
||||
}
|
||||
|
||||
int64 Client::CalcBaseMana()
|
||||
{
|
||||
int ConvertedWisInt = 0;
|
||||
int MindLesserFactor, MindFactor;
|
||||
int WisInt = 0;
|
||||
int64 base_mana = 0;
|
||||
int wisint_mana = 0;
|
||||
int64 max_m = 0;
|
||||
switch (GetCasterClass()) {
|
||||
case 'I':
|
||||
WisInt = GetINT();
|
||||
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
|
||||
ConvertedWisInt = WisInt;
|
||||
int over200 = WisInt;
|
||||
if (WisInt > 100) {
|
||||
if (WisInt > 200) {
|
||||
over200 = (WisInt - 200) / -2 + WisInt;
|
||||
}
|
||||
ConvertedWisInt = (3 * over200 - 300) / 2 + over200;
|
||||
}
|
||||
auto base_data = zone->GetBaseData(GetLevel(), GetClass());
|
||||
if (base_data.level == GetLevel()) {
|
||||
max_m = base_data.mana + (ConvertedWisInt * base_data.mana_fac) + itembonuses.heroic_max_mana;
|
||||
int ConvertedWisInt = 0;
|
||||
int MindLesserFactor, MindFactor;
|
||||
int WisInt = 0;
|
||||
int64 base_mana = 0;
|
||||
int wisint_mana = 0;
|
||||
int64 max_m = 0;
|
||||
|
||||
if (IsIntelligenceCasterClass()) {
|
||||
WisInt = GetINT();
|
||||
|
||||
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
|
||||
ConvertedWisInt = WisInt;
|
||||
|
||||
int over200 = WisInt;
|
||||
if (WisInt > 100) {
|
||||
if (WisInt > 200) {
|
||||
over200 = (WisInt - 200) / -2 + WisInt;
|
||||
}
|
||||
ConvertedWisInt = (3 * over200 - 300) / 2 + over200;
|
||||
}
|
||||
else {
|
||||
if ((( WisInt - 199 ) / 2) > 0) {
|
||||
MindLesserFactor = ( WisInt - 199 ) / 2;
|
||||
}
|
||||
else {
|
||||
MindLesserFactor = 0;
|
||||
}
|
||||
MindFactor = WisInt - MindLesserFactor;
|
||||
if (WisInt > 100) {
|
||||
max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
|
||||
}
|
||||
else {
|
||||
max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
|
||||
}
|
||||
|
||||
auto base_data = zone->GetBaseData(GetLevel(), GetClass());
|
||||
if (base_data.level == GetLevel()) {
|
||||
max_m = base_data.mana + (ConvertedWisInt * base_data.mana_fac) + itembonuses.heroic_max_mana;
|
||||
}
|
||||
break;
|
||||
case 'W':
|
||||
WisInt = GetWIS();
|
||||
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
|
||||
ConvertedWisInt = WisInt;
|
||||
int over200 = WisInt;
|
||||
if (WisInt > 100) {
|
||||
if (WisInt > 200) {
|
||||
over200 = (WisInt - 200) / -2 + WisInt;
|
||||
}
|
||||
ConvertedWisInt = (3 * over200 - 300) / 2 + over200;
|
||||
}
|
||||
auto base_data = zone->GetBaseData(GetLevel(), GetClass());
|
||||
if (base_data.level == GetLevel()) {
|
||||
max_m = base_data.mana + (ConvertedWisInt * base_data.mana_fac) + itembonuses.heroic_max_mana;
|
||||
}
|
||||
} else {
|
||||
if (((WisInt - 199) / 2) > 0) {
|
||||
MindLesserFactor = (WisInt - 199) / 2;
|
||||
} else {
|
||||
MindLesserFactor = 0;
|
||||
}
|
||||
else {
|
||||
if ((( WisInt - 199 ) / 2) > 0) {
|
||||
MindLesserFactor = ( WisInt - 199 ) / 2;
|
||||
}
|
||||
else {
|
||||
MindLesserFactor = 0;
|
||||
}
|
||||
MindFactor = WisInt - MindLesserFactor;
|
||||
if (WisInt > 100) {
|
||||
max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
|
||||
}
|
||||
else {
|
||||
max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
|
||||
}
|
||||
|
||||
MindFactor = WisInt - MindLesserFactor;
|
||||
|
||||
if (WisInt > 100) {
|
||||
max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
|
||||
} else {
|
||||
max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
|
||||
}
|
||||
break;
|
||||
case 'N': {
|
||||
max_m = 0;
|
||||
break;
|
||||
}
|
||||
} else if (IsWisdomCasterClass()) {
|
||||
WisInt = GetWIS();
|
||||
|
||||
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
|
||||
ConvertedWisInt = WisInt;
|
||||
|
||||
int over200 = WisInt;
|
||||
if (WisInt > 100) {
|
||||
if (WisInt > 200) {
|
||||
over200 = (WisInt - 200) / -2 + WisInt;
|
||||
}
|
||||
ConvertedWisInt = (3 * over200 - 300) / 2 + over200;
|
||||
}
|
||||
default: {
|
||||
LogDebug("Invalid Class [{}] in CalcMaxMana", GetCasterClass());
|
||||
max_m = 0;
|
||||
break;
|
||||
|
||||
auto base_data = zone->GetBaseData(GetLevel(), GetClass());
|
||||
if (base_data.level == GetLevel()) {
|
||||
max_m = base_data.mana + (ConvertedWisInt * base_data.mana_fac) + itembonuses.heroic_max_mana;
|
||||
}
|
||||
} else {
|
||||
if (((WisInt - 199) / 2) > 0) {
|
||||
MindLesserFactor = (WisInt - 199) / 2;
|
||||
} else {
|
||||
MindLesserFactor = 0;
|
||||
}
|
||||
|
||||
MindFactor = WisInt - MindLesserFactor;
|
||||
|
||||
if (WisInt > 100) {
|
||||
max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40);
|
||||
} else {
|
||||
max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
max_m = 0;
|
||||
}
|
||||
#if EQDEBUG >= 11
|
||||
LogDebug("Client::CalcBaseMana() called for [{}] - returning [{}]", GetName(), max_m);
|
||||
#endif
|
||||
|
||||
return max_m;
|
||||
}
|
||||
|
||||
|
||||
+22
-9
@@ -1530,8 +1530,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
||||
}
|
||||
} //else, somebody from our group is already here...
|
||||
|
||||
if (!group)
|
||||
database.SetGroupID(GetName(), 0, CharacterID(), false); //cannot re-establish group, kill it
|
||||
if (!group) { //cannot re-establish group, kill it
|
||||
Group::RemoveFromGroup(this);
|
||||
}
|
||||
|
||||
}
|
||||
else { //no group id
|
||||
@@ -4168,10 +4169,11 @@ void Client::Handle_OP_BookButton(const EQApplicationPacket* app)
|
||||
BookButton_Struct* book = reinterpret_cast<BookButton_Struct*>(app->pBuffer);
|
||||
|
||||
const EQ::ItemInstance* const inst = GetInv().GetItem(book->invslot);
|
||||
if (inst && inst->GetItem()->Book)
|
||||
if (inst && inst->GetItem())
|
||||
{
|
||||
// todo: if scribe book learn recipes and delete book from inventory
|
||||
// todo: if cast book use its spell on target and delete book from inventory (unless reusable?)
|
||||
// todo: cast spell button (unknown if anything on live uses this)
|
||||
ScribeRecipes(inst->GetItem()->ID);
|
||||
DeleteItemInInventory(book->invslot, 1, true);
|
||||
}
|
||||
|
||||
EQApplicationPacket outapp(OP_FinishWindow, 0);
|
||||
@@ -5595,9 +5597,20 @@ void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app)
|
||||
}
|
||||
|
||||
// Prevent the client from creating more than they have.
|
||||
const uint32 amount = EQ::ClampUpper(quantity, current_quantity);
|
||||
uint32 amount = EQ::ClampUpper(quantity, current_quantity);
|
||||
const uint32 item_id = is_radiant ? RuleI(Zone, RadiantCrystalItemID) : RuleI(Zone, EbonCrystalItemID);
|
||||
|
||||
const auto item = database.GetItem(item_id);
|
||||
// Prevent pulling more than max stack size or 1,000 (if stackable), whichever is lesser
|
||||
const uint32 max_reclaim_amount = EQ::Clamp(
|
||||
item && item->Stackable ? item->StackSize : ItemStackSizeConstraint::Minimum,
|
||||
ItemStackSizeConstraint::Minimum,
|
||||
ItemStackSizeConstraint::Maximum
|
||||
);
|
||||
if (amount > max_reclaim_amount) {
|
||||
amount = max_reclaim_amount;
|
||||
}
|
||||
|
||||
const bool success = SummonItem(item_id, amount);
|
||||
if (!success) {
|
||||
return;
|
||||
@@ -6862,7 +6875,7 @@ void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app)
|
||||
Client *c = entity_list.GetClientByName(gmn->oldname);
|
||||
LogInfo("GM([{}]) changeing players name. Old:[{}] New:[{}]", GetName(), gmn->oldname, gmn->newname);
|
||||
|
||||
const bool used_name = database.CheckUsedName(gmn->newname);
|
||||
const bool used_name = database.IsNameUsed(gmn->newname);
|
||||
if (!c) {
|
||||
Message(Chat::Red, fmt::format("{} not found for name change. Operation failed!", gmn->oldname).c_str());
|
||||
return;
|
||||
@@ -6873,7 +6886,7 @@ void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!used_name) {
|
||||
if (used_name) {
|
||||
Message(Chat::Red, fmt::format("{} is already in use. Operation failed!", gmn->newname).c_str());
|
||||
return;
|
||||
}
|
||||
@@ -7177,7 +7190,7 @@ void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app)
|
||||
|
||||
if (!GetMerc())
|
||||
{
|
||||
database.SetGroupID(GetName(), 0, CharacterID(), false);
|
||||
Group::RemoveFromGroup(this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1064,9 +1064,9 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I
|
||||
);
|
||||
SpellOnTarget(resurrection_sickness_spell_id, this);
|
||||
} else if (SpellID == SPELL_DIVINE_REZ) {
|
||||
SetHP(GetMaxHP());
|
||||
SetMana(GetMaxMana());
|
||||
SetEndurance(GetMaxEndurance());
|
||||
RestoreHealth();
|
||||
RestoreMana();
|
||||
RestoreEndurance();
|
||||
} else {
|
||||
SetHP(GetMaxHP() / 20);
|
||||
SetMana(GetMaxMana() / 20);
|
||||
@@ -2177,9 +2177,9 @@ void Client::HandleRespawnFromHover(uint32 Option)
|
||||
FastQueuePacket(&outapp);
|
||||
|
||||
CalcBonuses();
|
||||
SetHP(GetMaxHP());
|
||||
SetMana(GetMaxMana());
|
||||
SetEndurance(GetMaxEndurance());
|
||||
RestoreHealth();
|
||||
RestoreMana();
|
||||
RestoreEndurance();
|
||||
|
||||
m_Position.x = chosen->x;
|
||||
m_Position.y = chosen->y;
|
||||
|
||||
@@ -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("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("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("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("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) ||
|
||||
@@ -206,6 +208,7 @@ int command_init(void)
|
||||
command_add("scribespells", "[Max level] [Min level] - Scribe all spells for you or your player target that are usable by them, up to level specified. (may freeze client for a few seconds)", AccountStatus::GMLeadAdmin, command_scribespells) ||
|
||||
command_add("sendzonespawns", "Refresh spawn list for all clients in zone", AccountStatus::GMLeadAdmin, command_sendzonespawns) ||
|
||||
command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", AccountStatus::Player, command_sensetrap) ||
|
||||
command_add("serverrules", "Show server rules", AccountStatus::Player, command_serverrules) ||
|
||||
command_add("set", "Set command used to set various things", AccountStatus::Guide, command_set) ||
|
||||
command_add("show", "Show command used to show various things", AccountStatus::Guide, command_show) ||
|
||||
command_add("shutdown", "Shut this zone process down", AccountStatus::GMLeadAdmin, command_shutdown) ||
|
||||
@@ -822,8 +825,10 @@ void command_bot(Client *c, const Seperator *sep)
|
||||
#include "gm_commands/faction.cpp"
|
||||
#include "gm_commands/feature.cpp"
|
||||
#include "gm_commands/find.cpp"
|
||||
#include "gm_commands/fish.cpp"
|
||||
#include "gm_commands/fixmob.cpp"
|
||||
#include "gm_commands/flagedit.cpp"
|
||||
#include "gm_commands/forage.cpp"
|
||||
#include "gm_commands/gearup.cpp"
|
||||
#include "gm_commands/giveitem.cpp"
|
||||
#include "gm_commands/givemoney.cpp"
|
||||
@@ -893,6 +898,7 @@ void command_bot(Client *c, const Seperator *sep)
|
||||
#include "gm_commands/scribespells.cpp"
|
||||
#include "gm_commands/sendzonespawns.cpp"
|
||||
#include "gm_commands/sensetrap.cpp"
|
||||
#include "gm_commands/serverrules.cpp"
|
||||
#include "gm_commands/set.cpp"
|
||||
#include "gm_commands/show.cpp"
|
||||
#include "gm_commands/shutdown.cpp"
|
||||
|
||||
@@ -83,8 +83,10 @@ void command_faction(Client *c, const Seperator *sep);
|
||||
void command_faction_association(Client *c, const Seperator *sep);
|
||||
void command_feature(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_flagedit(Client *c, const Seperator *sep);
|
||||
void command_forage(Client* c, const Seperator* sep);
|
||||
void command_gearup(Client *c, const Seperator *sep);
|
||||
void command_giveitem(Client *c, const Seperator *sep);
|
||||
void command_givemoney(Client *c, const Seperator *sep);
|
||||
|
||||
+14
-7
@@ -19,9 +19,11 @@
|
||||
#define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from
|
||||
#define SEE_POSITION 0.5f //ratio of GetSize() where NPCs try to see for LOS
|
||||
|
||||
#define ARCHETYPE_HYBRID 1
|
||||
#define ARCHETYPE_CASTER 2
|
||||
#define ARCHETYPE_MELEE 3
|
||||
namespace Archetype {
|
||||
constexpr uint8 Hybrid = 1;
|
||||
constexpr uint8 Caster = 2;
|
||||
constexpr uint8 Melee = 3;
|
||||
};
|
||||
|
||||
#define CON_GREEN 2
|
||||
#define CON_LIGHTBLUE 18
|
||||
@@ -285,7 +287,7 @@ struct StatBonuses {
|
||||
int32 AC;
|
||||
int64 HP;
|
||||
int64 HPRegen;
|
||||
int64 MaxHP;
|
||||
int64 MaxHP; //same bonus as MaxHPChange when applied to spells and item bonuses
|
||||
int64 ManaRegen;
|
||||
int64 EnduranceRegen;
|
||||
int64 Mana;
|
||||
@@ -411,7 +413,7 @@ struct StatBonuses {
|
||||
int32 MeleeLifetap; //i
|
||||
int32 Vampirism; //i
|
||||
int32 HealRate; // Spell effect that influences effectiveness of heals
|
||||
int32 MaxHPChange; // Spell Effect
|
||||
int32 MaxHPChange; // percent change in hit points (aabonuses use variable MaxHP)
|
||||
int16 SkillDmgTaken[EQ::skills::HIGHEST_SKILL + 2]; // All Skills + -1
|
||||
int32 HealAmt; // Item Effect
|
||||
int32 SpellDmg; // Item Effect
|
||||
@@ -443,7 +445,8 @@ struct StatBonuses {
|
||||
int32 SongRange; // increases range of beneficial bard songs
|
||||
uint32 HPToManaConvert; // Uses HP to cast spells at specific conversion
|
||||
int32 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have.
|
||||
int16 FocusEffectsWorn[HIGHEST_FOCUS+1]; // Optional to allow focus effects to be applied additively from worn slot
|
||||
int16 FocusEffectsWorn[HIGHEST_FOCUS+1]; // Optional to allow focus effects to be applied additively from worn slot, limits do not apply
|
||||
int32 FocusEffectsWornWithLimits[HIGHEST_FOCUS + 1];// Optional to allow focus effects to be applied additively from worn slot, limits apply
|
||||
bool NegateEffects; // Check if you contain a buff with negate effect. (only spellbonuses)
|
||||
int32 SkillDamageAmount2[EQ::skills::HIGHEST_SKILL + 2]; // Adds skill specific damage
|
||||
uint32 NegateAttacks[3]; // 0 = bool HasEffect 1 = Buff Slot 2 = Max damage absorbed per hit
|
||||
@@ -509,6 +512,7 @@ struct StatBonuses {
|
||||
uint8 invisibility; // invisibility level
|
||||
uint8 invisibility_verse_undead; // IVU level
|
||||
uint8 invisibility_verse_animal; // IVA level
|
||||
int32 ShieldTargetSpa[2]; // [0] base = % mitigation amount, [1] buff slot
|
||||
|
||||
// AAs
|
||||
int32 TrapCircumvention; // reduce chance to trigger a trap.
|
||||
@@ -654,6 +658,8 @@ namespace SBIndex {
|
||||
constexpr uint16 COMBAT_PROC_SPELL_ID = 1; // SPA
|
||||
constexpr uint16 COMBAT_PROC_RATE_MOD = 2; // SPA
|
||||
constexpr uint16 COMBAT_PROC_REUSE_TIMER = 3; // SPA
|
||||
constexpr uint16 SHIELD_TARGET_MITIGATION_PERCENT = 0; // SPA 463
|
||||
constexpr uint16 SHIELD_TARGET_BUFFSLOT = 1; // SPA 463
|
||||
};
|
||||
|
||||
|
||||
@@ -826,7 +832,8 @@ struct ExtraAttackOptions {
|
||||
armor_pen_percent(0.0f), armor_pen_flat(0),
|
||||
crit_percent(1.0f), crit_flat(0.0f),
|
||||
hate_percent(1.0f), hate_flat(0), hit_chance(0),
|
||||
melee_damage_bonus_flat(0), skilldmgtaken_bonus_flat(0)
|
||||
melee_damage_bonus_flat(0), skilldmgtaken_bonus_flat(0),
|
||||
range_percent(0)
|
||||
{ }
|
||||
|
||||
float damage_percent;
|
||||
|
||||
@@ -334,7 +334,7 @@ void DialogueWindow::Render(Client *c, std::string markdown)
|
||||
if (responses.size() > 1) {
|
||||
for (auto &r: responses) {
|
||||
bracket_responses.emplace_back(
|
||||
fmt::format("[{}]", Saylink::Create(r, false))
|
||||
fmt::format("[{}]", Saylink::Create(r))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,15 @@ Doors::Doors(const DoorsRepository::Doors &door) :
|
||||
m_door_param = door.door_param;
|
||||
m_size = door.size;
|
||||
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_is_ldon_door = door.is_ldon_door;
|
||||
m_dz_switch_id = door.dz_switch_id;
|
||||
|
||||
@@ -56,6 +56,7 @@ void perl_register_doors();
|
||||
void perl_register_expedition();
|
||||
void perl_register_expedition_lock_messages();
|
||||
void perl_register_bot();
|
||||
void perl_register_buff();
|
||||
#endif // EMBPERL_XS_CLASSES
|
||||
#endif // EMBPERL_XS
|
||||
|
||||
@@ -200,6 +201,8 @@ const char* QuestEventSubroutines[_LargestEventID] = {
|
||||
"EVENT_ENTITY_VARIABLE_DELETE",
|
||||
"EVENT_ENTITY_VARIABLE_SET",
|
||||
"EVENT_ENTITY_VARIABLE_UPDATE",
|
||||
"EVENT_AA_LOSS",
|
||||
"EVENT_SPELL_BLOCKED",
|
||||
|
||||
// Add new events before these or Lua crashes
|
||||
"EVENT_SPELL_EFFECT_BOT",
|
||||
@@ -1174,6 +1177,7 @@ void PerlembParser::MapFunctions()
|
||||
perl_register_expedition();
|
||||
perl_register_expedition_lock_messages();
|
||||
perl_register_bot();
|
||||
perl_register_buff();
|
||||
#endif // EMBPERL_XS_CLASSES
|
||||
}
|
||||
|
||||
@@ -1934,6 +1938,25 @@ void PerlembParser::ExportEventVariables(
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_SPELL_BLOCKED: {
|
||||
Seperator sep(data);
|
||||
const uint32 blocking_spell_id = Strings::ToUnsignedInt(sep.arg[0]);
|
||||
const uint32 cast_spell_id = Strings::ToUnsignedInt(sep.arg[1]);
|
||||
|
||||
ExportVar(package_name.c_str(), "blocking_spell_id", blocking_spell_id);
|
||||
ExportVar(package_name.c_str(), "cast_spell_id", cast_spell_id);
|
||||
|
||||
if (IsValidSpell(blocking_spell_id)) {
|
||||
ExportVar(package_name.c_str(), "blocking_spell", "Spell", (void*) &spells[blocking_spell_id]);
|
||||
}
|
||||
|
||||
if (IsValidSpell(cast_spell_id)) {
|
||||
ExportVar(package_name.c_str(), "cast_spell", "Spell", (void*) &spells[cast_spell_id]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//tradeskill events
|
||||
case EVENT_COMBINE_SUCCESS:
|
||||
case EVENT_COMBINE_FAILURE: {
|
||||
@@ -2273,6 +2296,11 @@ void PerlembParser::ExportEventVariables(
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_AA_LOSS: {
|
||||
ExportVar(package_name.c_str(), "aa_lost", data);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_AA_EXP_GAIN: {
|
||||
ExportVar(package_name.c_str(), "aa_exp_gained", data);
|
||||
break;
|
||||
|
||||
+45
-18
@@ -380,12 +380,12 @@ void Perl__settimer(std::string timer_name, uint32 seconds)
|
||||
|
||||
void Perl__settimer(std::string timer_name, uint32 seconds, Mob* m)
|
||||
{
|
||||
quest_manager.settimer(timer_name, seconds);
|
||||
quest_manager.settimer(timer_name, seconds, m);
|
||||
}
|
||||
|
||||
void Perl__settimer(std::string timer_name, uint32 seconds, EQ::ItemInstance* inst)
|
||||
{
|
||||
quest_manager.settimer(timer_name, seconds);
|
||||
quest_manager.settimerMS(timer_name, seconds * 1000, inst);
|
||||
}
|
||||
|
||||
void Perl__settimerMS(std::string timer_name, uint32 milliseconds)
|
||||
@@ -1579,9 +1579,7 @@ std::string Perl__GetCharactersInInstance(uint16 instance_id)
|
||||
char_id_string = fmt::format("{} player(s) in instance: ", character_ids.size());
|
||||
auto iter = character_ids.begin();
|
||||
while (iter != character_ids.end()) {
|
||||
char char_name[64];
|
||||
database.GetCharName(*iter, char_name);
|
||||
char_id_string += char_name;
|
||||
char_id_string += database.GetCharName(*iter);
|
||||
char_id_string += "(";
|
||||
char_id_string += itoa(*iter);
|
||||
char_id_string += ")";
|
||||
@@ -1654,20 +1652,19 @@ void Perl__FlagInstanceByRaidLeader(uint32 zone, uint16 version)
|
||||
quest_manager.FlagInstanceByRaidLeader(zone, version);
|
||||
}
|
||||
|
||||
std::string Perl__saylink(const char* text)
|
||||
std::string Perl__saylink(std::string text)
|
||||
{
|
||||
// const cast is safe since, target api doesn't modify it
|
||||
return quest_manager.saylink(const_cast<char*>(text), false, text);
|
||||
return Saylink::Create(text);
|
||||
}
|
||||
|
||||
std::string Perl__saylink(const char* text, bool silent)
|
||||
std::string Perl__saylink(std::string text, bool silent)
|
||||
{
|
||||
return quest_manager.saylink(const_cast<char*>(text), silent, text);
|
||||
return Saylink::Create(text, silent);
|
||||
}
|
||||
|
||||
std::string Perl__saylink(const char* text, bool silent, const char* link_name)
|
||||
std::string Perl__saylink(std::string text, bool silent, std::string link_name)
|
||||
{
|
||||
return quest_manager.saylink(const_cast<char*>(text), silent, link_name);
|
||||
return Saylink::Create(text, silent, link_name);
|
||||
}
|
||||
|
||||
std::string Perl__getcharnamebyid(uint32 char_id)
|
||||
@@ -5833,6 +5830,31 @@ uint16 Perl__GetBotRaceByID(uint32 bot_id)
|
||||
return database.botdb.GetBotRaceByID(bot_id);
|
||||
}
|
||||
|
||||
std::string Perl__silent_saylink(std::string text)
|
||||
{
|
||||
return Saylink::Silent(text);
|
||||
}
|
||||
|
||||
std::string Perl__silent_saylink(std::string text, std::string link_name)
|
||||
{
|
||||
return Saylink::Silent(text, link_name);
|
||||
}
|
||||
|
||||
uint16 Perl__get_class_bitmask(uint8 class_id)
|
||||
{
|
||||
return GetPlayerClassBit(class_id);
|
||||
}
|
||||
|
||||
uint32 Perl__get_deity_bitmask(uint16 deity_id)
|
||||
{
|
||||
return static_cast<uint32>(EQ::deity::GetDeityBitmask(static_cast<EQ::deity::DeityType>(deity_id)));
|
||||
}
|
||||
|
||||
uint16 Perl__get_race_bitmask(uint16 race_id)
|
||||
{
|
||||
return GetPlayerRaceBit(race_id);
|
||||
}
|
||||
|
||||
void perl_register_quest()
|
||||
{
|
||||
perl::interpreter perl(PERL_GET_THX);
|
||||
@@ -6479,9 +6501,11 @@ void perl_register_quest()
|
||||
package.add("getconsiderlevelname", &Perl__getconsiderlevelname);
|
||||
package.add("gethexcolorcode", &Perl__gethexcolorcode);
|
||||
package.add("getcurrencyid", &Perl__getcurrencyid);
|
||||
package.add("get_class_bitmask", &Perl__get_class_bitmask);
|
||||
package.add("get_data", &Perl__get_data);
|
||||
package.add("get_data_expires", &Perl__get_data_expires);
|
||||
package.add("get_data_remaining", &Perl__get_data_remaining);
|
||||
package.add("get_deity_bitmask", &Perl__get_deity_bitmask);
|
||||
package.add("get_dz_task_id", &Perl__get_dz_task_id);
|
||||
package.add("getexpmodifierbycharid", (double(*)(uint32, uint32))&Perl__getexpmodifierbycharid);
|
||||
package.add("getexpmodifierbycharid", (double(*)(uint32, uint32, int16))&Perl__getexpmodifierbycharid);
|
||||
@@ -6514,6 +6538,7 @@ void perl_register_quest()
|
||||
package.add("getgroupidbycharid", &Perl__getgroupidbycharid);
|
||||
package.add("getinventoryslotname", &Perl__getinventoryslotname);
|
||||
package.add("getraididbycharid", &Perl__getraididbycharid);
|
||||
package.add("get_race_bitmask", &Perl__get_race_bitmask);
|
||||
package.add("get_recipe_component_item_ids", &Perl__GetRecipeComponentItemIDs);
|
||||
package.add("get_recipe_container_item_ids", &Perl__GetRecipeContainerItemIDs);
|
||||
package.add("get_recipe_fail_item_ids", &Perl__GetRecipeFailItemIDs);
|
||||
@@ -6644,9 +6669,9 @@ void perl_register_quest()
|
||||
package.add("say", (void(*)(const char*, uint8, int))&Perl__say);
|
||||
package.add("say", (void(*)(const char*, uint8, int, int))&Perl__say);
|
||||
package.add("say", (void(*)(const char*, uint8, int, int, int))&Perl__say);
|
||||
package.add("saylink", (std::string(*)(const char*))&Perl__saylink);
|
||||
package.add("saylink", (std::string(*)(const char*, bool))&Perl__saylink);
|
||||
package.add("saylink", (std::string(*)(const char*, bool, const char*))&Perl__saylink);
|
||||
package.add("saylink", (std::string(*)(std::string))&Perl__saylink);
|
||||
package.add("saylink", (std::string(*)(std::string, bool))&Perl__saylink);
|
||||
package.add("saylink", (std::string(*)(std::string, bool, std::string))&Perl__saylink);
|
||||
package.add("scribespells", (int(*)(int))&Perl__scribespells);
|
||||
package.add("scribespells", (int(*)(int, int))&Perl__scribespells);
|
||||
package.add("secondstotime", &Perl__secondstotime);
|
||||
@@ -6684,9 +6709,9 @@ void perl_register_quest()
|
||||
package.add("settarget", &Perl__settarget);
|
||||
package.add("settime", (void(*)(int, int))&Perl__settime);
|
||||
package.add("settime", (void(*)(int, int, bool))&Perl__settime);
|
||||
package.add("settimer", (void(*)(std::string, uint32))&Perl__settimer),
|
||||
package.add("settimer", (void(*)(std::string, uint32, EQ::ItemInstance*))&Perl__settimer),
|
||||
package.add("settimer", (void(*)(std::string, uint32, Mob*))&Perl__settimer),
|
||||
package.add("settimer", (void(*)(std::string, uint32))&Perl__settimer);
|
||||
package.add("settimer", (void(*)(std::string, uint32, EQ::ItemInstance*))&Perl__settimer);
|
||||
package.add("settimer", (void(*)(std::string, uint32, Mob*))&Perl__settimer);
|
||||
package.add("settimerMS", (void(*)(std::string, uint32))&Perl__settimerMS);
|
||||
package.add("settimerMS", (void(*)(std::string, uint32, EQ::ItemInstance*))&Perl__settimerMS);
|
||||
package.add("settimerMS", (void(*)(std::string, uint32, Mob*))&Perl__settimerMS);
|
||||
@@ -6698,6 +6723,8 @@ void perl_register_quest()
|
||||
package.add("signal", (void(*)(int, int))&Perl__signal);
|
||||
package.add("signalwith", (void(*)(int, int))&Perl__signalwith);
|
||||
package.add("signalwith", (void(*)(int, int, int))&Perl__signalwith);
|
||||
package.add("silent_saylink", (std::string(*)(std::string))&Perl__silent_saylink);
|
||||
package.add("silent_saylink", (std::string(*)(std::string, std::string))&Perl__silent_saylink);
|
||||
package.add("snow", &Perl__snow);
|
||||
package.add("spawn", &Perl__spawn);
|
||||
package.add("spawn2", &Perl__spawn2);
|
||||
|
||||
+14
-5
@@ -2152,7 +2152,7 @@ Group *EntityList::GetGroupByLeaderName(const char *leader)
|
||||
iterator = group_list.begin();
|
||||
|
||||
while (iterator != group_list.end()) {
|
||||
if (!strcmp((*iterator)->GetLeaderName(), leader))
|
||||
if (!strcmp((*iterator)->GetLeaderName().c_str(), leader))
|
||||
return *iterator;
|
||||
++iterator;
|
||||
}
|
||||
@@ -2675,6 +2675,10 @@ void EntityList::RemoveAllMobs()
|
||||
{
|
||||
auto it = mob_list.begin();
|
||||
while (it != mob_list.end()) {
|
||||
if (!it->second) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
safe_delete(it->second);
|
||||
free_ids.push(it->first);
|
||||
it = mob_list.erase(it);
|
||||
@@ -2812,6 +2816,9 @@ bool EntityList::RemoveMob(uint16 delete_id)
|
||||
|
||||
auto it = mob_list.find(delete_id);
|
||||
if (it != mob_list.end()) {
|
||||
if (!it->second) {
|
||||
return false;
|
||||
}
|
||||
if (npc_list.count(delete_id)) {
|
||||
entity_list.RemoveNPC(delete_id);
|
||||
}
|
||||
@@ -4379,8 +4386,9 @@ void EntityList::AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy)
|
||||
if (n->GetSwarmInfo()->owner_id == owner->GetID()) {
|
||||
if (
|
||||
!n->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
!(n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && other->IsClient()) &&
|
||||
!(n->GetSpecialAbility(IMMUNE_AGGRO_NPC) && other->IsNPC())
|
||||
!(other->IsBot() && n->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
|
||||
!(other->IsClient() && n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
|
||||
!(other->IsNPC() && n->GetSpecialAbility(IMMUNE_AGGRO_NPC))
|
||||
) {
|
||||
n->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
|
||||
}
|
||||
@@ -4405,8 +4413,9 @@ void EntityList::AddTempPetsToHateListOnOwnerDamage(Mob *owner, Mob* attacker, i
|
||||
attacker != n &&
|
||||
!n->IsEngaged() &&
|
||||
!n->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
!(n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT) && attacker->IsClient()) &&
|
||||
!(n->GetSpecialAbility(IMMUNE_AGGRO_NPC) && attacker->IsNPC()) &&
|
||||
!(attacker->IsBot() && n->GetSpecialAbility(IMMUNE_AGGRO_BOT)) &&
|
||||
!(attacker->IsClient() && n->GetSpecialAbility(IMMUNE_AGGRO_CLIENT)) &&
|
||||
!(attacker->IsNPC() && n->GetSpecialAbility(IMMUNE_AGGRO_NPC)) &&
|
||||
!attacker->IsTrap() &&
|
||||
!attacker->IsCorpse()
|
||||
) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user