diff --git a/changelog.txt b/changelog.txt
index f708219e8..f8ca4c917 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,5 +1,59 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
+== 12/29/2015 ==
+Akkadius: Implemented standardized zone controller scripts (Rule Zone, UseZoneController) Defaulted to true
+ - When a zone boots, it will spawn an invisible npc by the name of zone_controller
+ - Lua and Perl scripts can be represented with this npc as zone_controller.pl/lua
+ - This NPC's ID is ruled be define ZONE_CONTROLLER_NPC_ID 10
+ - Two EVENT's uniquely are handled with this NPC/controller (They only work with the zone_controller NPC)
+ - EVENT_SPAWN_ZONE :: All NPC spawns in the zone trigger the controller and pass the following variables:
+ $spawned_entity_id
+ $spawned_npc_id
+ - EVENT_DEATH_ZONE :: All NPC deaths in the zone trigger the controller event and pass the following variables:
+ $killer_id
+ $killer_damage
+ $killer_spell
+ $killer_skill
+ $killed_npc_id
+
+== 12/28/2015 ==
+Kinglykrab: Added GetInstanceTimer() to Perl and Lua.
+ - Added GetInstanceTimerByID(instance_id) to Perl and Lua.
+ - Note: If you do not provide an instance id in the method it defaults to instance id 0 and returns 0 for time remaining.
+ - Added UpdateZoneHeader(type, value) to Perl and Lua.
+ - Note: UpdateZoneHeader allows you to manipulate fog color, fog density, and many other zone header settings on the fly in Perl and Lua.
+
+== 12/21/2015 ==
+Natedog: Updated item table fields and added a few missing fields for evolving items
+ -DO NOT implement Heirloom items till the inventory code is fixed to allow placing NO DROP
+ items in your shared bank. (but item field located on items table)
+ -NYI - SkillModMax: Max skill point modification from the percent mods. EX:
+ 100% 2HSlashing (Max 50) - can only increase 2hslash by 50 MAX! (item field located though)
+Kinglykrab: Added GetMeleeMitigation() for NPCs and Clients in Perl and Lua.
+ - This allows you to check total item, spell, and AA melee mitigation contribution.
+
+== 12/19/2015 ==
+Kinglykrab: Added many methods to Perl and Lua, list below:
+ - SeeInvisible()
+ - SeeInvisibleUndead()
+ - SeeHide()
+ - SeeImprovedHide()
+ - GetNimbusEffect1() - returns first nimbus effect
+ - GetNimbusEffect2() - returns second nimbus effect
+ - GetNimbusEffect3() - returns third nimbus effect
+ - IsTargetable()
+ - HasShieldEquiped()
+ - HasTwoHandBluntEquiped()
+ - HasTwoHanderEquiped()
+ - GetHerosForgeModel() - returns int32 Hero's Forge model
+ - IsEliteMaterialItem() - returns uint32 Hero's Forge Model
+ - GetBaseSize() - returns Mob's base size
+ - HasOwner()
+ - IsPet()
+ - HasPet()
+ - IsSilenced()
+ - IsAmnesiad()
+
== 12/16/2015 ==
Noudess: Repaired issue with Bind Wounds on someone else. Message was not coming out on client (hold still) and a bind wounds on someone already binding their wounds would interrupt their bind and make them stand. Also removed some duplicate messaging.
diff --git a/common/clientversions.h b/common/clientversions.h
index 4e575f8f4..308a5f091 100644
--- a/common/clientversions.h
+++ b/common/clientversions.h
@@ -24,20 +24,27 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "types.h"
-static const uint32 BIT_Client62 = 1;
-static const uint32 BIT_Titanium = 2;
-static const uint32 BIT_SoF = 4;
-static const uint32 BIT_SoD = 8;
-static const uint32 BIT_UF = 16;
-static const uint32 BIT_RoF = 32;
-static const uint32 BIT_RoF2 = 64;
+static const uint32 BIT_Client62 = 0x00000001; // 1 (unsupported - placeholder for scripts)
+
+static const uint32 BIT_Titanium = 0x00000002; // 2
+static const uint32 BIT_SoF = 0x00000004; // 4
+static const uint32 BIT_SoD = 0x00000008; // 8
+static const uint32 BIT_UF = 0x00000010; // 16
+static const uint32 BIT_RoF = 0x00000020; // 32
+static const uint32 BIT_RoF2 = 0x00000040; // 64
+
+static const uint32 BIT_TitaniumAndEarlier = 0x00000003; // 3
+static const uint32 BIT_SoFAndEarlier = 0x00000007; // 7
+static const uint32 BIT_SoDAndEarlier = 0x0000000F; // 15
+static const uint32 BIT_UFAndEarlier = 0x0000001F; // 31
+static const uint32 BIT_RoFAndEarlier = 0x0000003F; // 63
+
+static const uint32 BIT_SoFAndLater = 0xFFFFFFFC; // 4294967292
+static const uint32 BIT_SoDAndLater = 0xFFFFFFF8; // 4294967288
+static const uint32 BIT_UFAndLater = 0xFFFFFFF0; // 4294967280
+static const uint32 BIT_RoFAndLater = 0xFFFFFFE0; // 4294967264
+static const uint32 BIT_RoF2AndLater = 0xFFFFFFC0; // 4294967232
-static const uint32 BIT_TitaniumAndEarlier = 0x00000003;
-static const uint32 BIT_SoFAndLater = 0xFFFFFFFC;
-static const uint32 BIT_SoDAndLater = 0xFFFFFFF8;
-static const uint32 BIT_UFAndLater = 0xFFFFFFF0;
-static const uint32 BIT_RoFAndLater = 0xFFFFFFE0;
-static const uint32 BIT_RoF2AndLater = 0xFFFFFFC0;
static const uint32 BIT_AllClients = 0xFFFFFFFF;
enum class ClientVersion
diff --git a/common/database.cpp b/common/database.cpp
index 475b48258..932aa4229 100644
--- a/common/database.cpp
+++ b/common/database.cpp
@@ -1183,21 +1183,16 @@ bool Database::CheckNameFilter(const char* name, bool surname)
{
std::string str_name = name;
- if(surname)
+ // the minimum 4 is enforced by the client too
+ if (!name || strlen(name) < 4)
{
- // the minimum 4 is enforced by the client too
- if(!name || strlen(name) < 3)
- {
- return false;
- }
+ return false;
}
- else
+
+ // Given name length is enforced by the client too
+ if (!surname && strlen(name) > 15)
{
- // the minimum 4 is enforced by the client too
- if(!name || strlen(name) < 4 || strlen(name) > 15)
- {
- return false;
- }
+ return false;
}
for (size_t i = 0; i < str_name.size(); i++)
@@ -1564,7 +1559,7 @@ void Database::AddReport(std::string who, std::string against, std::string lines
char *escape_str = new char[lines.size()*2+1];
DoEscapeString(escape_str, lines.c_str(), lines.size());
- std::string query = StringFormat("INSERT INTO reports (name, reported, reported_text) VALUES('%s', '%s', '%s')", who.c_str(), against.c_str(), escape_str);
+ std::string query = StringFormat("INSERT INTO reports (name, reported, reported_text) VALUES('%s', '%s', '%s')", EscapeString(who).c_str(), EscapeString(against).c_str(), escape_str);
QueryDatabase(query);
safe_delete_array(escape_str);
}
@@ -2182,3 +2177,42 @@ void Database::ClearInvSnapshots(bool use_rule)
std::string query = StringFormat("DELETE FROM inventory_snapshots WHERE time_index <= %lu", (unsigned long)del_time);
QueryDatabase(query);
}
+
+struct TimeOfDay_Struct Database::LoadTime(time_t &realtime)
+{
+
+ TimeOfDay_Struct eqTime;
+ std::string query = StringFormat("SELECT minute,hour,day,month,year,realtime FROM eqtime limit 1");
+ auto results = QueryDatabase(query);
+
+ if (!results.Success() || results.RowCount() == 0)
+ {
+ Log.Out(Logs::Detail, Logs::World_Server, "Loading EQ time of day failed. Using defaults.");
+ eqTime.minute = 0;
+ eqTime.hour = 9;
+ eqTime.day = 1;
+ eqTime.month = 1;
+ eqTime.year = 3100;
+ realtime = time(0);
+ }
+
+ auto row = results.begin();
+
+ eqTime.minute = atoi(row[0]);
+ eqTime.hour = atoi(row[1]);
+ eqTime.day = atoi(row[2]);
+ eqTime.month = atoi(row[3]);
+ eqTime.year = atoi(row[4]);
+ realtime = atoi(row[5]);
+
+ return eqTime;
+}
+
+bool Database::SaveTime(int8 minute, int8 hour, int8 day, int8 month, int16 year)
+{
+ std::string query = StringFormat("UPDATE eqtime set minute = %d, hour = %d, day = %d, month = %d, year = %d, realtime = %d limit 1", minute, hour, day, month, year, time(0));
+ auto results = QueryDatabase(query);
+
+ return results.Success();
+
+}
\ No newline at end of file
diff --git a/common/database.h b/common/database.h
index f066cb417..02110b9a6 100644
--- a/common/database.h
+++ b/common/database.h
@@ -241,6 +241,8 @@ public:
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);
diff --git a/common/database_conversions.cpp b/common/database_conversions.cpp
index fa7c57a12..c7428c07e 100644
--- a/common/database_conversions.cpp
+++ b/common/database_conversions.cpp
@@ -493,7 +493,7 @@ bool Database::CheckDatabaseConversions() {
/* Check for a new version of this script, the arg passed
would have to be higher than the copy they have downloaded
locally and they will re fetch */
- system("perl eqemu_update.pl V 13");
+ system("perl eqemu_update.pl V 14");
/* Run Automatic Database Upgrade Script */
system("perl eqemu_update.pl ran_from_world");
diff --git a/common/eqemu_config.cpp b/common/eqemu_config.cpp
index 6e1400904..fb295cec1 100644
--- a/common/eqemu_config.cpp
+++ b/common/eqemu_config.cpp
@@ -254,10 +254,6 @@ void EQEmuConfig::do_files(TiXmlElement *ele)
if (text) {
OpCodesFile = text;
}
- text = ParseTextBlock(ele, "eqtime", true);
- if (text) {
- EQTimeFile = text;
- }
}
void EQEmuConfig::do_directories(TiXmlElement *ele)
@@ -408,9 +404,6 @@ std::string EQEmuConfig::GetByName(const std::string &var_name) const
if (var_name == "OpCodesFile") {
return (OpCodesFile);
}
- if (var_name == "EQTimeFile") {
- return (EQTimeFile);
- }
if (var_name == "MapDir") {
return (MapDir);
}
@@ -475,7 +468,6 @@ void EQEmuConfig::Dump() const
std::cout << "QSDatabasePort = " << QSDatabasePort << std::endl;
std::cout << "SpellsFile = " << SpellsFile << std::endl;
std::cout << "OpCodesFile = " << OpCodesFile << std::endl;
- std::cout << "EQTimeFile = " << EQTimeFile << std::endl;
std::cout << "MapDir = " << MapDir << std::endl;
std::cout << "QuestDir = " << QuestDir << std::endl;
std::cout << "PluginDir = " << PluginDir << std::endl;
diff --git a/common/eqemu_config.h b/common/eqemu_config.h
index 1ad2174dc..039b6c327 100644
--- a/common/eqemu_config.h
+++ b/common/eqemu_config.h
@@ -79,7 +79,6 @@ class EQEmuConfig : public XMLParser
// From
std::string SpellsFile;
std::string OpCodesFile;
- std::string EQTimeFile;
// From
std::string MapDir;
@@ -154,7 +153,6 @@ class EQEmuConfig : public XMLParser
// Files
SpellsFile = "spells_us.txt";
OpCodesFile = "opcodes.conf";
- EQTimeFile = "eqtime.cfg";
// Dirs
MapDir = "Maps";
QuestDir = "quests";
diff --git a/common/eqtime.cpp b/common/eqtime.cpp
index e504964b8..8ece4ad69 100644
--- a/common/eqtime.cpp
+++ b/common/eqtime.cpp
@@ -133,72 +133,6 @@ int EQTime::SetCurrentEQTimeOfDay(TimeOfDay_Struct start_eq, time_t start_real)
return 1;
}
-//saveFile and loadFile need to use long for the save datatype...
-//For some reason, ifstream/ofstream have problems with EQEmu datatypes in files.
-bool EQTime::saveFile(const char *filename)
-{
- std::ofstream of;
- of.open(filename);
- if (!of)
- {
- Log.Out(Logs::General, Logs::Error, "EQTime::saveFile failed: Unable to open file '%s'", filename);
- return false;
- }
- //Enable for debugging
- of << EQT_VERSION << std::endl;
- of << (long)eqTime.start_eqtime.day << std::endl;
- of << (long)eqTime.start_eqtime.hour << std::endl;
- of << (long)eqTime.start_eqtime.minute << std::endl;
- of << (long)eqTime.start_eqtime.month << std::endl;
- of << eqTime.start_eqtime.year << std::endl;
- of << eqTime.start_realtime << std::endl;
- of.close();
- return true;
-}
-
-bool EQTime::loadFile(const char *filename)
-{
- int version=0;
- long in_data=0;
- std::ifstream in;
- in.open(filename);
- if(!in)
- {
- Log.Out(Logs::General, Logs::Error, "Could not load EQTime file %s", filename);
- return false;
- }
- in >> version;
- in.ignore(80, '\n');
- if(version != EQT_VERSION)
- {
- Log.Out(Logs::General, Logs::Error, "'%s' is NOT a valid EQTime file. File version is %i, EQTime version is %i", filename, version, EQT_VERSION);
- return false;
- }
- //in >> eqTime.start_eqtime.day;
- in >> in_data;
- in.ignore(80, '\n');
- eqTime.start_eqtime.day = in_data;
- //in >> eqTime.start_eqtime.hour;
- in >> in_data;
- eqTime.start_eqtime.hour = in_data;
- in.ignore(80, '\n');
- //in >> eqTime.start_eqtime.minute;
- in >> in_data;
- in.ignore(80, '\n');
- eqTime.start_eqtime.minute = in_data;
- //in >> eqTime.start_eqtime.month;
- in >> in_data;
- in.ignore(80, '\n');
- eqTime.start_eqtime.month = in_data;
- in >> eqTime.start_eqtime.year;
- in.ignore(80, '\n');
- in >> eqTime.start_realtime;
- //Enable for debugging...
- in.close();
- return true;
-}
-
-
bool EQTime::IsTimeBefore(TimeOfDay_Struct *base, TimeOfDay_Struct *test) {
if (base->year > test->year)
return(true);
diff --git a/common/eqtime.h b/common/eqtime.h
index aeda9f0f6..f40b855f6 100644
--- a/common/eqtime.h
+++ b/common/eqtime.h
@@ -39,12 +39,6 @@ public:
static void ToString(TimeOfDay_Struct *t, std::string &str);
- //Database functions
- //bool loadDB(Database q);
- //bool setDB(Database q);
- bool loadFile(const char *filename);
- bool saveFile(const char *filename);
-
private:
//This is our reference clock.
eqTimeOfDay eqTime;
diff --git a/common/features.h b/common/features.h
index 2ede4b4be..8115efd82 100644
--- a/common/features.h
+++ b/common/features.h
@@ -233,6 +233,8 @@ enum { //some random constants
#define GROUP_EXP_PER_POINT 1000
#define RAID_EXP_PER_POINT 2000
+#define ZONE_CONTROLLER_NPC_ID 10
+
//Some hard coded statuses from commands and other places:
enum {
minStatusToBeGM = 40,
diff --git a/common/item_fieldlist.h b/common/item_fieldlist.h
index 1b3fe0cb0..665783498 100644
--- a/common/item_fieldlist.h
+++ b/common/item_fieldlist.h
@@ -41,6 +41,7 @@ F(ac)
F(deity)
F(skillmodvalue)
F(UNK033)
+F(skillmodmax)
F(skillmodtype)
F(banedmgrace)
F(banedmgamt)
@@ -172,7 +173,10 @@ F(bardlevel)
F(questitemflag)
F(svcorruption)
F(purity)
+F(evoitem)
+F(evoid)
F(evolvinglevel)
+F(evomax)
F(backstabdmg)
F(dsmitigation)
F(heroic_str)
diff --git a/common/item_struct.h b/common/item_struct.h
index 64292f490..ba8afcd14 100644
--- a/common/item_struct.h
+++ b/common/item_struct.h
@@ -130,6 +130,7 @@ struct Item_Struct {
uint32 Deity; // Bitmask of Deities that can equip this item
//uint32 Unk033
int32 SkillModValue; // % Mod to skill specified in SkillModType
+ int32 SkillModMax; // Max skill point modification
uint32 SkillModType; // Type of skill for SkillModValue to apply to
uint32 BaneDmgRace; // Bane Damage Race
int8 BaneDmgAmt; // Bane Damage Body Amount
@@ -218,7 +219,10 @@ struct Item_Struct {
// Begin SoF Fields
int32 SVCorruption;
uint32 Purity;
+ uint8 EvolvingItem;
+ uint32 EvolvingID;
uint8 EvolvingLevel;
+ uint8 EvolvingMax;
uint32 BackstabDmg;
uint32 DSMitigation;
int32 HeroicStr;
diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp
index 79c7271fd..76d62e3a4 100644
--- a/common/patches/rof.cpp
+++ b/common/patches/rof.cpp
@@ -5231,19 +5231,19 @@ namespace RoF
hdr.unknown044 = 0;
hdr.unknown048 = 0;
hdr.unknown052 = 0;
- hdr.isEvolving = item->EvolvingLevel > 0 ? 1 : 0;
+ hdr.isEvolving = item->EvolvingItem;
ss.write((const char*)&hdr, sizeof(RoF::structs::ItemSerializationHeader));
- if (item->EvolvingLevel > 0) {
+ if (item->EvolvingItem > 0) {
RoF::structs::EvolvingItem evotop;
evotop.unknown001 = 0;
evotop.unknown002 = 0;
evotop.unknown003 = 0;
evotop.unknown004 = 0;
evotop.evoLevel = item->EvolvingLevel;
- evotop.progress = 95.512;
+ evotop.progress = 0;
evotop.Activated = 1;
- evotop.evomaxlevel = 7;
+ evotop.evomaxlevel = item->EvolvingMax;
ss.write((const char*)&evotop, sizeof(RoF::structs::EvolvingItem));
}
//ORNAMENT IDFILE / ICON
@@ -5353,7 +5353,7 @@ namespace RoF
ibs.Races = item->Races;
ibs.Deity = item->Deity;
ibs.SkillModValue = item->SkillModValue;
- ibs.SkillModMax = 0xffffffff;
+ ibs.SkillModMax = item->SkillModMax;
ibs.SkillModType = (int8)(item->SkillModType);
ibs.SkillModExtra = 0;
ibs.BaneDmgRace = item->BaneDmgRace;
diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp
index e5cc77791..a8b231300 100644
--- a/common/patches/rof2.cpp
+++ b/common/patches/rof2.cpp
@@ -5432,19 +5432,19 @@ namespace RoF2
hdr.unknown044 = 0;
hdr.unknown048 = 0;
hdr.unknown052 = 0;
- hdr.isEvolving = item->EvolvingLevel > 0 ? 1 : 0;
+ hdr.isEvolving = item->EvolvingItem;
ss.write((const char*)&hdr, sizeof(RoF2::structs::ItemSerializationHeader));
- if (item->EvolvingLevel > 0) {
+ if (item->EvolvingItem > 0) {
RoF2::structs::EvolvingItem evotop;
evotop.unknown001 = 0;
evotop.unknown002 = 0;
evotop.unknown003 = 0;
evotop.unknown004 = 0;
evotop.evoLevel = item->EvolvingLevel;
- evotop.progress = 95.512;
+ evotop.progress = 0;
evotop.Activated = 1;
- evotop.evomaxlevel = 7;
+ evotop.evomaxlevel = item->EvolvingMax;
ss.write((const char*)&evotop, sizeof(RoF2::structs::EvolvingItem));
}
//ORNAMENT IDFILE / ICON
@@ -5554,7 +5554,7 @@ namespace RoF2
ibs.Races = item->Races;
ibs.Deity = item->Deity;
ibs.SkillModValue = item->SkillModValue;
- ibs.SkillModMax = 0xffffffff;
+ ibs.SkillModMax = item->SkillModMax;
ibs.SkillModType = (int8)(item->SkillModType);
ibs.SkillModExtra = 0;
ibs.BaneDmgRace = item->BaneDmgRace;
diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp
index 0e88a2a09..3535503a0 100644
--- a/common/patches/uf.cpp
+++ b/common/patches/uf.cpp
@@ -3835,19 +3835,19 @@ namespace UF
hdr.unknown044 = 0;
hdr.unknown048 = 0;
hdr.unknown052 = 0;
- hdr.isEvolving = item->EvolvingLevel > 0 ? 1 : 0;
+ hdr.isEvolving = item->EvolvingItem;
ss.write((const char*)&hdr, sizeof(UF::structs::ItemSerializationHeader));
- if (item->EvolvingLevel > 0) {
+ if (item->EvolvingItem > 0) {
UF::structs::EvolvingItem evotop;
evotop.unknown001 = 0;
evotop.unknown002 = 0;
evotop.unknown003 = 0;
evotop.unknown004 = 0;
evotop.evoLevel = item->EvolvingLevel;
- evotop.progress = 95.512;
+ evotop.progress = 0;
evotop.Activated = 1;
- evotop.evomaxlevel = 7;
+ evotop.evomaxlevel = item->EvolvingMax;
ss.write((const char*)&evotop, sizeof(UF::structs::EvolvingItem));
}
//ORNAMENT IDFILE / ICON -
@@ -3947,7 +3947,7 @@ namespace UF
ibs.Races = item->Races;
ibs.Deity = item->Deity;
ibs.SkillModValue = item->SkillModValue;
- ibs.unknown5 = 0;
+ ibs.SkillModMax = item->SkillModMax;
ibs.SkillModType = item->SkillModType;
ibs.BaneDmgRace = item->BaneDmgRace;
ibs.BaneDmgBody = item->BaneDmgBody;
diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h
index 3631edea3..7f3a7dcc0 100644
--- a/common/patches/uf_structs.h
+++ b/common/patches/uf_structs.h
@@ -4103,7 +4103,7 @@ struct ItemBodyStruct
uint32 Races;
uint32 Deity;
int32 SkillModValue;
- uint32 unknown5;
+ uint32 SkillModMax;
uint32 SkillModType;
uint32 BaneDmgRace;
uint32 BaneDmgBody;
diff --git a/common/rulesys.cpp b/common/rulesys.cpp
index 13e342fe5..56289b09a 100644
--- a/common/rulesys.cpp
+++ b/common/rulesys.cpp
@@ -236,7 +236,7 @@ void RuleManager::SaveRules(Database *database, const char *ruleset_name) {
}
bool RuleManager::LoadRules(Database *database, const char *ruleset_name) {
-
+
int ruleset_id = GetRulesetID(database, ruleset_name);
if (ruleset_id < 0) {
Log.Out(Logs::Detail, Logs::Rules, "Failed to find ruleset '%s' for load operation. Canceling.", ruleset_name);
@@ -248,6 +248,26 @@ bool RuleManager::LoadRules(Database *database, const char *ruleset_name) {
m_activeRuleset = ruleset_id;
m_activeName = ruleset_name;
+ /* Load default ruleset values first if we're loading something other than default */
+ if (strcasecmp(ruleset_name, "default") != 0){
+ std::string default_ruleset_name = "default";
+ int default_ruleset_id = GetRulesetID(database, default_ruleset_name.c_str());
+ if (default_ruleset_id < 0) {
+ Log.Out(Logs::Detail, Logs::Rules, "Failed to find default ruleset '%s' for load operation. Canceling.", default_ruleset_name.c_str());
+ return(false);
+ }
+ Log.Out(Logs::Detail, Logs::Rules, "Loading rule set '%s' (%d)", default_ruleset_name.c_str(), default_ruleset_id);
+
+ std::string query = StringFormat("SELECT rule_name, rule_value FROM rule_values WHERE ruleset_id = %d", default_ruleset_id);
+ auto results = database->QueryDatabase(query);
+ if (!results.Success())
+ return false;
+
+ for (auto row = results.begin(); row != results.end(); ++row)
+ if (!SetRule(row[0], row[1], nullptr, false))
+ Log.Out(Logs::Detail, Logs::Rules, "Unable to interpret rule record for %s", row[0]);
+ }
+
std::string query = StringFormat("SELECT rule_name, rule_value FROM rule_values WHERE ruleset_id=%d", ruleset_id);
auto results = database->QueryDatabase(query);
if (!results.Success())
diff --git a/common/ruletypes.h b/common/ruletypes.h
index 91d3479be..41e4e22a4 100644
--- a/common/ruletypes.h
+++ b/common/ruletypes.h
@@ -233,6 +233,7 @@ RULE_BOOL(Zone, LevelBasedEXPMods, false) // Allows you to use the level_exp_mod
RULE_INT(Zone, WeatherTimer, 600) // Weather timer when no duration is available
RULE_BOOL(Zone, EnableLoggedOffReplenishments, true)
RULE_INT(Zone, MinOfflineTimeToReplenishments, 21600) // 21600 seconds is 6 Hours
+RULE_BOOL(Zone, UseZoneController, true) // Enables the ability to use persistent quest based zone controllers (zone_controller.pl/lua)
RULE_CATEGORY_END()
RULE_CATEGORY(Map)
@@ -459,6 +460,8 @@ RULE_BOOL(Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arr
RULE_BOOL(Combat, MeleePush, true) // enable melee push
RULE_INT(Combat, MeleePushChance, 50) // (NPCs) chance the target will be pushed. Made up, 100 actually isn't that bad
RULE_BOOL(Combat, UseLiveCombatRounds, true) // turn this false if you don't want to worry about fixing up combat rounds for NPCs
+RULE_INT(Combat, NPCAssistCap, 5) // Maxiumium number of NPCs that will assist another NPC at once
+RULE_INT(Combat, NPCAssistCapTimer, 6000) // Time in milliseconds a NPC will take to clear assist aggro cap space
RULE_CATEGORY_END()
RULE_CATEGORY(NPC)
@@ -494,6 +497,7 @@ RULE_INT(Aggro, PetSpellAggroMod, 10)
RULE_REAL(Aggro, TunnelVisionAggroMod, 0.75) //people not currently the top hate generate this much hate on a Tunnel Vision mob
RULE_INT(Aggro, MaxScalingProcAggro, 400) // Set to -1 for no limit. Maxmimum amount of aggro that HP scaling SPA effect in a proc will add.
RULE_INT(Aggro, IntAggroThreshold, 75) // Int <= this will aggro regardless of level difference.
+RULE_BOOL(Aggro, AllowTickPulling, false) // tick pulling is an exploit in an NPC's call for help fixed sometime in 2006 on live
RULE_CATEGORY_END()
RULE_CATEGORY(TaskSystem)
diff --git a/common/shareddb.cpp b/common/shareddb.cpp
index 59a2a118b..f82d54fad 100644
--- a/common/shareddb.cpp
+++ b/common/shareddb.cpp
@@ -1,6 +1,10 @@
#include
#include
+#if defined(_MSC_VER) && _MSC_VER >= 1800
+ #include
+#endif
+
#include "classes.h"
#include "eq_packet_structs.h"
#include "eqemu_exception.h"
@@ -802,36 +806,37 @@ bool SharedDatabase::LoadItems(const std::string &prefix) {
return true;
}
-void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_item_id) {
- EQEmu::FixedMemoryHashSet hash(reinterpret_cast(data), size, items, max_item_id);
+void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_item_id)
+{
+ EQEmu::FixedMemoryHashSet hash(reinterpret_cast(data), size, items, max_item_id);
char ndbuffer[4];
bool disableNoRent = false;
- if(GetVariable("disablenorent", ndbuffer, 4)) {
- if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') {
+ if (GetVariable("disablenorent", ndbuffer, 4)) {
+ if (ndbuffer[0] == '1' && ndbuffer[1] == '\0') {
disableNoRent = true;
}
}
bool disableNoDrop = false;
- if(GetVariable("disablenodrop", ndbuffer, 4)) {
- if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') {
+ if (GetVariable("disablenodrop", ndbuffer, 4)) {
+ if (ndbuffer[0] == '1' && ndbuffer[1] == '\0') {
disableNoDrop = true;
}
}
bool disableLoreGroup = false;
- if(GetVariable("disablelore", ndbuffer, 4)) {
- if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') {
+ if (GetVariable("disablelore", ndbuffer, 4)) {
+ if (ndbuffer[0] == '1' && ndbuffer[1] == '\0') {
disableLoreGroup = true;
}
}
bool disableNoTransfer = false;
- if(GetVariable("disablenotransfer", ndbuffer, 4)) {
- if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') {
+ if (GetVariable("disablenotransfer", ndbuffer, 4)) {
+ if (ndbuffer[0] == '1' && ndbuffer[1] == '\0') {
disableNoTransfer = true;
}
}
- Item_Struct item;
+ Item_Struct item;
const std::string query = "SELECT source,"
#define F(x) "`"#x"`,"
@@ -839,224 +844,226 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
#undef F
"updated FROM items ORDER BY id";
auto results = QueryDatabase(query);
- if (!results.Success()) {
- return;
- }
+ if (!results.Success()) {
+ return;
+ }
- for(auto row = results.begin(); row != results.end(); ++row) {
- memset(&item, 0, sizeof(Item_Struct));
+ for (auto row = results.begin(); row != results.end(); ++row) {
+ memset(&item, 0, sizeof(Item_Struct));
- item.ItemClass = (uint8)atoi(row[ItemField::itemclass]);
- strcpy(item.Name,row[ItemField::name]);
- strcpy(item.Lore,row[ItemField::lore]);
- strcpy(item.IDFile,row[ItemField::idfile]);
+ item.ItemClass = (uint8)atoi(row[ItemField::itemclass]);
+ strcpy(item.Name, row[ItemField::name]);
+ strcpy(item.Lore, row[ItemField::lore]);
+ strcpy(item.IDFile, row[ItemField::idfile]);
- item.ID = (uint32)atoul(row[ItemField::id]);
- item.Weight = (uint8)atoi(row[ItemField::weight]);
- item.NoRent = disableNoRent ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::norent]);
- item.NoDrop = disableNoDrop ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::nodrop]);
- item.Size = (uint8)atoi(row[ItemField::size]);
- item.Slots = (uint32)atoul(row[ItemField::slots]);
- item.Price = (uint32)atoul(row[ItemField::price]);
- item.Icon = (uint32)atoul(row[ItemField::icon]);
- item.BenefitFlag = (atoul(row[ItemField::benefitflag]) != 0);
- item.Tradeskills = (atoi(row[ItemField::tradeskills])==0) ? false : true;
- item.CR = (int8)atoi(row[ItemField::cr]);
- item.DR = (int8)atoi(row[ItemField::dr]);
- item.PR = (int8)atoi(row[ItemField::pr]);
- item.MR = (int8)atoi(row[ItemField::mr]);
- item.FR = (int8)atoi(row[ItemField::fr]);
- item.AStr = (int8)atoi(row[ItemField::astr]);
- item.ASta = (int8)atoi(row[ItemField::asta]);
- item.AAgi = (int8)atoi(row[ItemField::aagi]);
- item.ADex = (int8)atoi(row[ItemField::adex]);
- item.ACha = (int8)atoi(row[ItemField::acha]);
- item.AInt = (int8)atoi(row[ItemField::aint]);
- item.AWis = (int8)atoi(row[ItemField::awis]);
- item.HP = (int32)atoul(row[ItemField::hp]);
- item.Mana = (int32)atoul(row[ItemField::mana]);
- item.AC = (int32)atoul(row[ItemField::ac]);
- item.Deity = (uint32)atoul(row[ItemField::deity]);
- item.SkillModValue = (int32)atoul(row[ItemField::skillmodvalue]);
+ item.ID = (uint32)atoul(row[ItemField::id]);
+ item.Weight = (uint8)atoi(row[ItemField::weight]);
+ item.NoRent = disableNoRent ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::norent]);
+ item.NoDrop = disableNoDrop ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::nodrop]);
+ item.Size = (uint8)atoi(row[ItemField::size]);
+ item.Slots = (uint32)atoul(row[ItemField::slots]);
+ item.Price = (uint32)atoul(row[ItemField::price]);
+ item.Icon = (uint32)atoul(row[ItemField::icon]);
+ item.BenefitFlag = (atoul(row[ItemField::benefitflag]) != 0);
+ item.Tradeskills = (atoi(row[ItemField::tradeskills]) == 0) ? false : true;
+ item.CR = (int8)atoi(row[ItemField::cr]);
+ item.DR = (int8)atoi(row[ItemField::dr]);
+ item.PR = (int8)atoi(row[ItemField::pr]);
+ item.MR = (int8)atoi(row[ItemField::mr]);
+ item.FR = (int8)atoi(row[ItemField::fr]);
+ item.AStr = (int8)atoi(row[ItemField::astr]);
+ item.ASta = (int8)atoi(row[ItemField::asta]);
+ item.AAgi = (int8)atoi(row[ItemField::aagi]);
+ item.ADex = (int8)atoi(row[ItemField::adex]);
+ item.ACha = (int8)atoi(row[ItemField::acha]);
+ item.AInt = (int8)atoi(row[ItemField::aint]);
+ item.AWis = (int8)atoi(row[ItemField::awis]);
+ item.HP = (int32)atoul(row[ItemField::hp]);
+ item.Mana = (int32)atoul(row[ItemField::mana]);
+ item.AC = (int32)atoul(row[ItemField::ac]);
+ item.Deity = (uint32)atoul(row[ItemField::deity]);
+ item.SkillModValue = (int32)atoul(row[ItemField::skillmodvalue]);
+ item.SkillModMax = (int32)atoul(row[ItemField::skillmodmax]);
+ item.SkillModType = (uint32)atoul(row[ItemField::skillmodtype]);
+ item.BaneDmgRace = (uint32)atoul(row[ItemField::banedmgrace]);
+ item.BaneDmgAmt = (int8)atoi(row[ItemField::banedmgamt]);
+ item.BaneDmgBody = (uint32)atoul(row[ItemField::banedmgbody]);
+ item.Magic = (atoi(row[ItemField::magic]) == 0) ? false : true;
+ item.CastTime_ = (int32)atoul(row[ItemField::casttime_]);
+ item.ReqLevel = (uint8)atoi(row[ItemField::reqlevel]);
+ item.BardType = (uint32)atoul(row[ItemField::bardtype]);
+ item.BardValue = (int32)atoul(row[ItemField::bardvalue]);
+ item.Light = (int8)atoi(row[ItemField::light]);
+ item.Delay = (uint8)atoi(row[ItemField::delay]);
+ item.RecLevel = (uint8)atoi(row[ItemField::reclevel]);
+ item.RecSkill = (uint8)atoi(row[ItemField::recskill]);
+ item.ElemDmgType = (uint8)atoi(row[ItemField::elemdmgtype]);
+ item.ElemDmgAmt = (uint8)atoi(row[ItemField::elemdmgamt]);
+ item.Range = (uint8)atoi(row[ItemField::range]);
+ item.Damage = (uint32)atoi(row[ItemField::damage]);
+ item.Color = (uint32)atoul(row[ItemField::color]);
+ item.Classes = (uint32)atoul(row[ItemField::classes]);
+ item.Races = (uint32)atoul(row[ItemField::races]);
- item.SkillModType = (uint32)atoul(row[ItemField::skillmodtype]);
- item.BaneDmgRace = (uint32)atoul(row[ItemField::banedmgrace]);
- item.BaneDmgAmt = (int8)atoi(row[ItemField::banedmgamt]);
- item.BaneDmgBody = (uint32)atoul(row[ItemField::banedmgbody]);
- item.Magic = (atoi(row[ItemField::magic])==0) ? false : true;
- item.CastTime_ = (int32)atoul(row[ItemField::casttime_]);
- item.ReqLevel = (uint8)atoi(row[ItemField::reqlevel]);
- item.BardType = (uint32)atoul(row[ItemField::bardtype]);
- item.BardValue = (int32)atoul(row[ItemField::bardvalue]);
- item.Light = (int8)atoi(row[ItemField::light]);
- item.Delay = (uint8)atoi(row[ItemField::delay]);
- item.RecLevel = (uint8)atoi(row[ItemField::reclevel]);
- item.RecSkill = (uint8)atoi(row[ItemField::recskill]);
- item.ElemDmgType = (uint8)atoi(row[ItemField::elemdmgtype]);
- item.ElemDmgAmt = (uint8)atoi(row[ItemField::elemdmgamt]);
- item.Range = (uint8)atoi(row[ItemField::range]);
- item.Damage = (uint32)atoi(row[ItemField::damage]);
- item.Color = (uint32)atoul(row[ItemField::color]);
- item.Classes = (uint32)atoul(row[ItemField::classes]);
- item.Races = (uint32)atoul(row[ItemField::races]);
-
- item.MaxCharges = (int16)atoi(row[ItemField::maxcharges]);
- item.ItemType = (uint8)atoi(row[ItemField::itemtype]);
+ item.MaxCharges = (int16)atoi(row[ItemField::maxcharges]);
+ item.ItemType = (uint8)atoi(row[ItemField::itemtype]);
item.Material = (uint8)atoi(row[ItemField::material]);
item.HerosForgeModel = (uint32)atoi(row[ItemField::herosforgemodel]);
- item.SellRate = (float)atof(row[ItemField::sellrate]);
- item.CastTime = (uint32)atoul(row[ItemField::casttime]);
- item.EliteMaterial = (uint32)atoul(row[ItemField::elitematerial]);
- item.ProcRate = (int32)atoi(row[ItemField::procrate]);
- item.CombatEffects = (int8)atoi(row[ItemField::combateffects]);
- item.Shielding = (int8)atoi(row[ItemField::shielding]);
- item.StunResist = (int8)atoi(row[ItemField::stunresist]);
- item.StrikeThrough = (int8)atoi(row[ItemField::strikethrough]);
- item.ExtraDmgSkill = (uint32)atoul(row[ItemField::extradmgskill]);
- item.ExtraDmgAmt = (uint32)atoul(row[ItemField::extradmgamt]);
- item.SpellShield = (int8)atoi(row[ItemField::spellshield]);
- item.Avoidance = (int8)atoi(row[ItemField::avoidance]);
- item.Accuracy = (int8)atoi(row[ItemField::accuracy]);
- item.CharmFileID = (uint32)atoul(row[ItemField::charmfileid]);
- item.FactionMod1 = (int32)atoul(row[ItemField::factionmod1]);
- item.FactionMod2 = (int32)atoul(row[ItemField::factionmod2]);
- item.FactionMod3 = (int32)atoul(row[ItemField::factionmod3]);
- item.FactionMod4 = (int32)atoul(row[ItemField::factionmod4]);
- item.FactionAmt1 = (int32)atoul(row[ItemField::factionamt1]);
- item.FactionAmt2 = (int32)atoul(row[ItemField::factionamt2]);
- item.FactionAmt3 = (int32)atoul(row[ItemField::factionamt3]);
- item.FactionAmt4 = (int32)atoul(row[ItemField::factionamt4]);
+ item.SellRate = (float)atof(row[ItemField::sellrate]);
+ item.CastTime = (uint32)atoul(row[ItemField::casttime]);
+ item.EliteMaterial = (uint32)atoul(row[ItemField::elitematerial]);
+ item.ProcRate = (int32)atoi(row[ItemField::procrate]);
+ item.CombatEffects = (int8)atoi(row[ItemField::combateffects]);
+ item.Shielding = (int8)atoi(row[ItemField::shielding]);
+ item.StunResist = (int8)atoi(row[ItemField::stunresist]);
+ item.StrikeThrough = (int8)atoi(row[ItemField::strikethrough]);
+ item.ExtraDmgSkill = (uint32)atoul(row[ItemField::extradmgskill]);
+ item.ExtraDmgAmt = (uint32)atoul(row[ItemField::extradmgamt]);
+ item.SpellShield = (int8)atoi(row[ItemField::spellshield]);
+ item.Avoidance = (int8)atoi(row[ItemField::avoidance]);
+ item.Accuracy = (int8)atoi(row[ItemField::accuracy]);
+ item.CharmFileID = (uint32)atoul(row[ItemField::charmfileid]);
+ item.FactionMod1 = (int32)atoul(row[ItemField::factionmod1]);
+ item.FactionMod2 = (int32)atoul(row[ItemField::factionmod2]);
+ item.FactionMod3 = (int32)atoul(row[ItemField::factionmod3]);
+ item.FactionMod4 = (int32)atoul(row[ItemField::factionmod4]);
+ item.FactionAmt1 = (int32)atoul(row[ItemField::factionamt1]);
+ item.FactionAmt2 = (int32)atoul(row[ItemField::factionamt2]);
+ item.FactionAmt3 = (int32)atoul(row[ItemField::factionamt3]);
+ item.FactionAmt4 = (int32)atoul(row[ItemField::factionamt4]);
- strcpy(item.CharmFile,row[ItemField::charmfile]);
+ strcpy(item.CharmFile, row[ItemField::charmfile]);
- item.AugType = (uint32)atoul(row[ItemField::augtype]);
- item.AugSlotType[0] = (uint8)atoi(row[ItemField::augslot1type]);
- item.AugSlotVisible[0] = (uint8)atoi(row[ItemField::augslot1visible]);
- item.AugSlotUnk2[0] = 0;
- item.AugSlotType[1] = (uint8)atoi(row[ItemField::augslot2type]);
- item.AugSlotVisible[1] = (uint8)atoi(row[ItemField::augslot2visible]);
- item.AugSlotUnk2[1] = 0;
- item.AugSlotType[2] = (uint8)atoi(row[ItemField::augslot3type]);
- item.AugSlotVisible[2] = (uint8)atoi(row[ItemField::augslot3visible]);
- item.AugSlotUnk2[2] = 0;
- item.AugSlotType[3] = (uint8)atoi(row[ItemField::augslot4type]);
- item.AugSlotVisible[3] = (uint8)atoi(row[ItemField::augslot4visible]);
- item.AugSlotUnk2[3] = 0;
- item.AugSlotType[4] = (uint8)atoi(row[ItemField::augslot5type]);
- item.AugSlotVisible[4] = (uint8)atoi(row[ItemField::augslot5visible]);
- item.AugSlotUnk2[4] = 0;
+ item.AugType = (uint32)atoul(row[ItemField::augtype]);
+ item.AugSlotType[0] = (uint8)atoi(row[ItemField::augslot1type]);
+ item.AugSlotVisible[0] = (uint8)atoi(row[ItemField::augslot1visible]);
+ item.AugSlotUnk2[0] = 0;
+ item.AugSlotType[1] = (uint8)atoi(row[ItemField::augslot2type]);
+ item.AugSlotVisible[1] = (uint8)atoi(row[ItemField::augslot2visible]);
+ item.AugSlotUnk2[1] = 0;
+ item.AugSlotType[2] = (uint8)atoi(row[ItemField::augslot3type]);
+ item.AugSlotVisible[2] = (uint8)atoi(row[ItemField::augslot3visible]);
+ item.AugSlotUnk2[2] = 0;
+ item.AugSlotType[3] = (uint8)atoi(row[ItemField::augslot4type]);
+ item.AugSlotVisible[3] = (uint8)atoi(row[ItemField::augslot4visible]);
+ item.AugSlotUnk2[3] = 0;
+ item.AugSlotType[4] = (uint8)atoi(row[ItemField::augslot5type]);
+ item.AugSlotVisible[4] = (uint8)atoi(row[ItemField::augslot5visible]);
+ item.AugSlotUnk2[4] = 0;
item.AugSlotType[5] = (uint8)atoi(row[ItemField::augslot6type]);
item.AugSlotVisible[5] = (uint8)atoi(row[ItemField::augslot6visible]);
item.AugSlotUnk2[5] = 0;
- item.LDoNTheme = (uint32)atoul(row[ItemField::ldontheme]);
- item.LDoNPrice = (uint32)atoul(row[ItemField::ldonprice]);
- item.LDoNSold = (uint32)atoul(row[ItemField::ldonsold]);
- item.BagType = (uint8)atoi(row[ItemField::bagtype]);
- item.BagSlots = (uint8)atoi(row[ItemField::bagslots]);
- item.BagSize = (uint8)atoi(row[ItemField::bagsize]);
- item.BagWR = (uint8)atoi(row[ItemField::bagwr]);
- item.Book = (uint8)atoi(row[ItemField::book]);
- item.BookType = (uint32)atoul(row[ItemField::booktype]);
+ item.LDoNTheme = (uint32)atoul(row[ItemField::ldontheme]);
+ item.LDoNPrice = (uint32)atoul(row[ItemField::ldonprice]);
+ item.LDoNSold = (uint32)atoul(row[ItemField::ldonsold]);
+ item.BagType = (uint8)atoi(row[ItemField::bagtype]);
+ item.BagSlots = (uint8)std::min(atoi(row[ItemField::bagslots]), 10); // FIXME: remove when big bags supported
+ item.BagSize = (uint8)atoi(row[ItemField::bagsize]);
+ item.BagWR = (uint8)atoi(row[ItemField::bagwr]);
+ item.Book = (uint8)atoi(row[ItemField::book]);
+ item.BookType = (uint32)atoul(row[ItemField::booktype]);
- strcpy(item.Filename,row[ItemField::filename]);
+ strcpy(item.Filename, row[ItemField::filename]);
- item.BaneDmgRaceAmt = (uint32)atoul(row[ItemField::banedmgraceamt]);
- item.AugRestrict = (uint32)atoul(row[ItemField::augrestrict]);
- item.LoreGroup = disableLoreGroup ? (uint8)atoi("0") : atoi(row[ItemField::loregroup]);
- item.LoreFlag = item.LoreGroup!=0;
- item.PendingLoreFlag = (atoi(row[ItemField::pendingloreflag])==0) ? false : true;
- item.ArtifactFlag = (atoi(row[ItemField::artifactflag])==0) ? false : true;
- item.SummonedFlag = (atoi(row[ItemField::summonedflag])==0) ? false : true;
- item.Favor = (uint32)atoul(row[ItemField::favor]);
- item.FVNoDrop = (atoi(row[ItemField::fvnodrop])==0) ? false : true;
- item.Endur = (uint32)atoul(row[ItemField::endur]);
- item.DotShielding = (uint32)atoul(row[ItemField::dotshielding]);
- item.Attack = (uint32)atoul(row[ItemField::attack]);
- item.Regen = (uint32)atoul(row[ItemField::regen]);
- item.ManaRegen = (uint32)atoul(row[ItemField::manaregen]);
- item.EnduranceRegen = (uint32)atoul(row[ItemField::enduranceregen]);
- item.Haste = (uint32)atoul(row[ItemField::haste]);
- item.DamageShield = (uint32)atoul(row[ItemField::damageshield]);
- item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]);
- item.RecastType = (uint32)atoul(row[ItemField::recasttype]);
- item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]);
- item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]);
- item.Attuneable = (atoi(row[ItemField::attuneable])==0) ? false : true;
- item.NoPet = (atoi(row[ItemField::nopet])==0) ? false : true;
- item.PointType = (uint32)atoul(row[ItemField::pointtype]);
- item.PotionBelt = (atoi(row[ItemField::potionbelt])==0) ? false : true;
- item.PotionBeltSlots = (atoi(row[ItemField::potionbeltslots])==0) ? false : true;
- item.StackSize = (uint16)atoi(row[ItemField::stacksize]);
- item.NoTransfer = disableNoTransfer ? false : (atoi(row[ItemField::notransfer])==0) ? false : true;
- item.Stackable = (atoi(row[ItemField::stackable])==0) ? false : true;
- item.Click.Effect = (uint32)atoul(row[ItemField::clickeffect]);
- item.Click.Type = (uint8)atoul(row[ItemField::clicktype]);
- item.Click.Level = (uint8)atoul(row[ItemField::clicklevel]);
- item.Click.Level2 = (uint8)atoul(row[ItemField::clicklevel2]);
+ item.BaneDmgRaceAmt = (uint32)atoul(row[ItemField::banedmgraceamt]);
+ item.AugRestrict = (uint32)atoul(row[ItemField::augrestrict]);
+ item.LoreGroup = disableLoreGroup ? (uint8)atoi("0") : atoi(row[ItemField::loregroup]);
+ item.LoreFlag = item.LoreGroup != 0;
+ item.PendingLoreFlag = (atoi(row[ItemField::pendingloreflag]) == 0) ? false : true;
+ item.ArtifactFlag = (atoi(row[ItemField::artifactflag]) == 0) ? false : true;
+ item.SummonedFlag = (atoi(row[ItemField::summonedflag]) == 0) ? false : true;
+ item.Favor = (uint32)atoul(row[ItemField::favor]);
+ item.FVNoDrop = (atoi(row[ItemField::fvnodrop]) == 0) ? false : true;
+ item.Endur = (uint32)atoul(row[ItemField::endur]);
+ item.DotShielding = (uint32)atoul(row[ItemField::dotshielding]);
+ item.Attack = (uint32)atoul(row[ItemField::attack]);
+ item.Regen = (uint32)atoul(row[ItemField::regen]);
+ item.ManaRegen = (uint32)atoul(row[ItemField::manaregen]);
+ item.EnduranceRegen = (uint32)atoul(row[ItemField::enduranceregen]);
+ item.Haste = (uint32)atoul(row[ItemField::haste]);
+ item.DamageShield = (uint32)atoul(row[ItemField::damageshield]);
+ item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]);
+ item.RecastType = (uint32)atoul(row[ItemField::recasttype]);
+ item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]);
+ item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]);
+ item.Attuneable = (atoi(row[ItemField::attuneable]) == 0) ? false : true;
+ item.NoPet = (atoi(row[ItemField::nopet]) == 0) ? false : true;
+ item.PointType = (uint32)atoul(row[ItemField::pointtype]);
+ item.PotionBelt = (atoi(row[ItemField::potionbelt]) == 0) ? false : true;
+ item.PotionBeltSlots = (atoi(row[ItemField::potionbeltslots]) == 0) ? false : true;
+ item.StackSize = (uint16)atoi(row[ItemField::stacksize]);
+ item.NoTransfer = disableNoTransfer ? false : (atoi(row[ItemField::notransfer]) == 0) ? false : true;
+ item.Stackable = (atoi(row[ItemField::stackable]) == 0) ? false : true;
+ item.Click.Effect = (uint32)atoul(row[ItemField::clickeffect]);
+ item.Click.Type = (uint8)atoul(row[ItemField::clicktype]);
+ item.Click.Level = (uint8)atoul(row[ItemField::clicklevel]);
+ item.Click.Level2 = (uint8)atoul(row[ItemField::clicklevel2]);
- strcpy(item.CharmFile,row[ItemField::charmfile]);
+ strcpy(item.CharmFile, row[ItemField::charmfile]);
- item.Proc.Effect = (int32)atoul(row[ItemField::proceffect]);
- item.Proc.Type = (uint8)atoul(row[ItemField::proctype]);
- item.Proc.Level = (uint8)atoul(row[ItemField::proclevel]);
- item.Proc.Level2 = (uint8)atoul(row[ItemField::proclevel2]);
- item.Worn.Effect = (int32)atoul(row[ItemField::worneffect]);
- item.Worn.Type = (uint8)atoul(row[ItemField::worntype]);
- item.Worn.Level = (uint8)atoul(row[ItemField::wornlevel]);
- item.Worn.Level2 = (uint8)atoul(row[ItemField::wornlevel2]);
- item.Focus.Effect = (int32)atoul(row[ItemField::focuseffect]);
- item.Focus.Type = (uint8)atoul(row[ItemField::focustype]);
- item.Focus.Level = (uint8)atoul(row[ItemField::focuslevel]);
- item.Focus.Level2 = (uint8)atoul(row[ItemField::focuslevel2]);
- item.Scroll.Effect = (int32)atoul(row[ItemField::scrolleffect]);
- item.Scroll.Type = (uint8)atoul(row[ItemField::scrolltype]);
- item.Scroll.Level = (uint8)atoul(row[ItemField::scrolllevel]);
- item.Scroll.Level2 = (uint8)atoul(row[ItemField::scrolllevel2]);
- item.Bard.Effect = (int32)atoul(row[ItemField::bardeffect]);
- item.Bard.Type = (uint8)atoul(row[ItemField::bardtype]);
- item.Bard.Level = (uint8)atoul(row[ItemField::bardlevel]);
- item.Bard.Level2 = (uint8)atoul(row[ItemField::bardlevel2]);
- item.QuestItemFlag = (atoi(row[ItemField::questitemflag])==0) ? false : true;
- item.SVCorruption = (int32)atoi(row[ItemField::svcorruption]);
- item.Purity = (uint32)atoul(row[ItemField::purity]);
- item.EvolvingLevel = (uint8)atoul(row[ItemField::evolvinglevel]);
- item.BackstabDmg = (uint32)atoul(row[ItemField::backstabdmg]);
- item.DSMitigation = (uint32)atoul(row[ItemField::dsmitigation]);
- item.HeroicStr = (int32)atoi(row[ItemField::heroic_str]);
- item.HeroicInt = (int32)atoi(row[ItemField::heroic_int]);
- item.HeroicWis = (int32)atoi(row[ItemField::heroic_wis]);
- item.HeroicAgi = (int32)atoi(row[ItemField::heroic_agi]);
- item.HeroicDex = (int32)atoi(row[ItemField::heroic_dex]);
- item.HeroicSta = (int32)atoi(row[ItemField::heroic_sta]);
- item.HeroicCha = (int32)atoi(row[ItemField::heroic_cha]);
- item.HeroicMR = (int32)atoi(row[ItemField::heroic_mr]);
- item.HeroicFR = (int32)atoi(row[ItemField::heroic_fr]);
- item.HeroicCR = (int32)atoi(row[ItemField::heroic_cr]);
- item.HeroicDR = (int32)atoi(row[ItemField::heroic_dr]);
- item.HeroicPR = (int32)atoi(row[ItemField::heroic_pr]);
- item.HeroicSVCorrup = (int32)atoi(row[ItemField::heroic_svcorrup]);
- item.HealAmt = (int32)atoi(row[ItemField::healamt]);
- item.SpellDmg = (int32)atoi(row[ItemField::spelldmg]);
- item.LDoNSellBackRate = (uint32)atoul(row[ItemField::ldonsellbackrate]);
- item.ScriptFileID = (uint32)atoul(row[ItemField::scriptfileid]);
- item.ExpendableArrow = (uint16)atoul(row[ItemField::expendablearrow]);
- item.Clairvoyance = (uint32)atoul(row[ItemField::clairvoyance]);
+ item.Proc.Effect = (int32)atoul(row[ItemField::proceffect]);
+ item.Proc.Type = (uint8)atoul(row[ItemField::proctype]);
+ item.Proc.Level = (uint8)atoul(row[ItemField::proclevel]);
+ item.Proc.Level2 = (uint8)atoul(row[ItemField::proclevel2]);
+ item.Worn.Effect = (int32)atoul(row[ItemField::worneffect]);
+ item.Worn.Type = (uint8)atoul(row[ItemField::worntype]);
+ item.Worn.Level = (uint8)atoul(row[ItemField::wornlevel]);
+ item.Worn.Level2 = (uint8)atoul(row[ItemField::wornlevel2]);
+ item.Focus.Effect = (int32)atoul(row[ItemField::focuseffect]);
+ item.Focus.Type = (uint8)atoul(row[ItemField::focustype]);
+ item.Focus.Level = (uint8)atoul(row[ItemField::focuslevel]);
+ item.Focus.Level2 = (uint8)atoul(row[ItemField::focuslevel2]);
+ item.Scroll.Effect = (int32)atoul(row[ItemField::scrolleffect]);
+ item.Scroll.Type = (uint8)atoul(row[ItemField::scrolltype]);
+ item.Scroll.Level = (uint8)atoul(row[ItemField::scrolllevel]);
+ item.Scroll.Level2 = (uint8)atoul(row[ItemField::scrolllevel2]);
+ item.Bard.Effect = (int32)atoul(row[ItemField::bardeffect]);
+ item.Bard.Type = (uint8)atoul(row[ItemField::bardtype]);
+ item.Bard.Level = (uint8)atoul(row[ItemField::bardlevel]);
+ item.Bard.Level2 = (uint8)atoul(row[ItemField::bardlevel2]);
+ item.QuestItemFlag = (atoi(row[ItemField::questitemflag]) == 0) ? false : true;
+ item.SVCorruption = (int32)atoi(row[ItemField::svcorruption]);
+ item.Purity = (uint32)atoul(row[ItemField::purity]);
+ item.EvolvingItem = (uint8)atoul(row[ItemField::evoitem]);
+ item.EvolvingID = (uint8)atoul(row[ItemField::evoid]);
+ item.EvolvingLevel = (uint8)atoul(row[ItemField::evolvinglevel]);
+ item.EvolvingMax = (uint8)atoul(row[ItemField::evomax]);
+ item.BackstabDmg = (uint32)atoul(row[ItemField::backstabdmg]);
+ item.DSMitigation = (uint32)atoul(row[ItemField::dsmitigation]);
+ item.HeroicStr = (int32)atoi(row[ItemField::heroic_str]);
+ item.HeroicInt = (int32)atoi(row[ItemField::heroic_int]);
+ item.HeroicWis = (int32)atoi(row[ItemField::heroic_wis]);
+ item.HeroicAgi = (int32)atoi(row[ItemField::heroic_agi]);
+ item.HeroicDex = (int32)atoi(row[ItemField::heroic_dex]);
+ item.HeroicSta = (int32)atoi(row[ItemField::heroic_sta]);
+ item.HeroicCha = (int32)atoi(row[ItemField::heroic_cha]);
+ item.HeroicMR = (int32)atoi(row[ItemField::heroic_mr]);
+ item.HeroicFR = (int32)atoi(row[ItemField::heroic_fr]);
+ item.HeroicCR = (int32)atoi(row[ItemField::heroic_cr]);
+ item.HeroicDR = (int32)atoi(row[ItemField::heroic_dr]);
+ item.HeroicPR = (int32)atoi(row[ItemField::heroic_pr]);
+ item.HeroicSVCorrup = (int32)atoi(row[ItemField::heroic_svcorrup]);
+ item.HealAmt = (int32)atoi(row[ItemField::healamt]);
+ item.SpellDmg = (int32)atoi(row[ItemField::spelldmg]);
+ item.LDoNSellBackRate = (uint32)atoul(row[ItemField::ldonsellbackrate]);
+ item.ScriptFileID = (uint32)atoul(row[ItemField::scriptfileid]);
+ item.ExpendableArrow = (uint16)atoul(row[ItemField::expendablearrow]);
+ item.Clairvoyance = (uint32)atoul(row[ItemField::clairvoyance]);
- strcpy(item.ClickName,row[ItemField::clickname]);
- strcpy(item.ProcName,row[ItemField::procname]);
- strcpy(item.WornName,row[ItemField::wornname]);
- strcpy(item.FocusName,row[ItemField::focusname]);
- strcpy(item.ScrollName,row[ItemField::scrollname]);
-
- try {
- hash.insert(item.ID, item);
- } catch(std::exception &ex) {
- Log.Out(Logs::General, Logs::Error, "Database::LoadItems: %s", ex.what());
- break;
- }
- }
+ strcpy(item.ClickName, row[ItemField::clickname]);
+ strcpy(item.ProcName, row[ItemField::procname]);
+ strcpy(item.WornName, row[ItemField::wornname]);
+ strcpy(item.FocusName, row[ItemField::focusname]);
+ strcpy(item.ScrollName, row[ItemField::scrollname]);
+ try {
+ hash.insert(item.ID, item);
+ } catch (std::exception &ex) {
+ Log.Out(Logs::General, Logs::Error, "Database::LoadItems: %s", ex.what());
+ break;
+ }
+ }
}
const Item_Struct* SharedDatabase::GetItem(uint32 id) {
diff --git a/common/version.h b/common/version.h
index 2e5af617c..00a999add 100644
--- a/common/version.h
+++ b/common/version.h
@@ -30,7 +30,7 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
-#define CURRENT_BINARY_DATABASE_VERSION 9091
+#define CURRENT_BINARY_DATABASE_VERSION 9094
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9000
#else
diff --git a/utils/scripts/eqemu_update.pl b/utils/scripts/eqemu_update.pl
index 8fd3af540..6d9be10a2 100644
--- a/utils/scripts/eqemu_update.pl
+++ b/utils/scripts/eqemu_update.pl
@@ -23,7 +23,7 @@ if($Config{osname}=~/linux/i){ $OS = "Linux"; }
if($Config{osname}=~/Win|MS/i){ $OS = "Windows"; }
#::: If current version is less than what world is reporting, then download a new one...
-$current_version = 13;
+$current_version = 14;
if($ARGV[0] eq "V"){
if($ARGV[1] > $current_version){
@@ -107,6 +107,38 @@ if($path eq ""){
exit;
}
+if($ARGV[0] eq "install_peq_db"){
+
+ $db_name = "peq";
+ if($ARGV[1]){
+ $db_name = $ARGV[1];
+ }
+
+ $db = $db_name;
+
+ #::: Database Routines
+ print "MariaDB :: Creating Database '" . $db_name . "'\n";
+ print `"$path" --host $host --user $user --password="$pass" -N -B -e "DROP DATABASE IF EXISTS $db_name;"`;
+ print `"$path" --host $host --user $user --password="$pass" -N -B -e "CREATE DATABASE $db_name"`;
+ if($OS eq "Windows"){ @db_version = split(': ', `world db_version`); }
+ if($OS eq "Linux"){ @db_version = split(': ', `./world db_version`); }
+ $bin_db_ver = trim($db_version[1]);
+ check_db_version_table();
+ $local_db_ver = trim(get_mysql_result("SELECT version FROM db_version LIMIT 1"));
+ fetch_peq_db_full();
+ print "\nFetching Latest Database Updates...\n";
+ main_db_management();
+ print "\nApplying Latest Database Updates...\n";
+ main_db_management();
+
+ print get_mysql_result("UPDATE `launcher` SET `dynamics` = 30 WHERE `name` = 'zone'");
+}
+
+if($ARGV[0] eq "remove_duplicate_rules"){
+ remove_duplicate_rule_values();
+ exit;
+}
+
if($ARGV[0] eq "installer"){
print "Running EQEmu Server installer routines...\n";
mkdir('logs');
@@ -152,6 +184,7 @@ if($ARGV[0] eq "installer"){
if($OS eq "Windows"){
check_windows_firewall_rules();
+ do_windows_login_server_setup();
}
exit;
}
@@ -251,6 +284,7 @@ sub show_menu_prompt {
11 => \&fetch_latest_windows_binaries,
12 => \&fetch_server_dlls,
13 => \&do_windows_login_server_setup,
+ 14 => \&remove_duplicate_rule_values,
19 => \&do_bots_db_schema_drop,
20 => \&do_update_self,
0 => \&script_exit,
@@ -328,6 +362,7 @@ return < 1000; $i--){
+ for($i = $bin_db_ver; $i > $revision_check; $i--){
if(!defined($m_d{$i}[0])){ next; }
$file_name = trim($m_d{$i}[1]);
diff --git a/utils/scripts/import_13th_floor.pl b/utils/scripts/import_13th_floor.pl
new file mode 100644
index 000000000..6a66892f9
--- /dev/null
+++ b/utils/scripts/import_13th_floor.pl
@@ -0,0 +1,255 @@
+#! /usr/bin/perl
+
+########################################################################
+#::: 13th floor import script
+#::: Current Source: http://items.sodeq.org/download.php
+#::: Authors: (Natedog, Akkadius)
+########################################################################
+
+use DBI;
+use DBD::mysql;
+
+my $database_name = "";
+my $total_items = 0;
+my $read_items_file = "items.txt"; #default
+my $dbh = LoadMysql();
+
+read_items_file_from_13th_floor_text();
+update_items_table();
+
+sub LoadMysql{
+ #::: Config Variables
+ my $confile = "eqemu_config.xml";
+ open(F, "<$confile") or die "Unable to open config: $confile\n";
+ my $indb = 0;
+ while() {
+ s/\r//g;
+ if(//i) { $indb = 1; }
+ next unless($indb == 1);
+ if(/<\/database>/i) { $indb = 0; last; }
+ if(/(.*)<\/host>/i) { $host = $1; }
+ elsif(/(.*)<\/username>/i) { $user = $1; }
+ elsif(/(.*)<\/password>/i) { $pass = $1; }
+ elsif(/(.*)<\/db>/i) { $db = $1; }
+ }
+ $database_name = $db;
+ #::: DATA SOURCE NAME
+ $dsn = "dbi:mysql:$db:localhost:3306";
+ #::: PERL DBI CONNECT
+ $connect = DBI->connect($dsn, $user, $pass);
+ return $connect;
+}
+
+sub read_items_file_from_13th_floor_text {
+
+ #::: Read from file and place into array
+ open(F, "<" . $read_items_file) or die "Unable to open itemfile: " . $read_items_file . "\n";
+ my @item_file_lines = ;
+ close(F);
+
+ #::: Chomp this array...
+ my @newitem_file_lines;
+ chomp($item_file_lines[0]);
+ @fields = split("(?prepare("SHOW TABLES LIKE 'items_floor'");
+ $sth->execute();
+ my $has_items_floor = $sth->fetchrow_array();
+
+ #::: If we have items_floor
+ if ($has_items_floor eq '') {
+ $dbh->do("CREATE TABLE `items_floor` (`" . join("` VARCHAR(64) NOT NULL DEFAULT '', `", @fields). "` VARCHAR(64) NOT NULL DEFAULT '', UNIQUE INDEX `ID` (`id`)) COLLATE='latin1_swedish_ci' ENGINE=MyISAM");
+ $dbh->do("ALTER TABLE `items_floor` CHANGE `id` `id` INT(11) NOT NULL DEFAULT '0'");
+ printf "Database items_floor created\n";
+ }
+
+ #::: Create REPLACE INTO header and define worker variables...
+ $master_insert = "REPLACE INTO `items_floor` (" . join(",", @fields) . ") VALUES ";
+ $query_insert_ph = ""; #::: Used for building placeholder values in query Ex: (?, ?, ?)
+ @field_values = (); #::: Used for stuffing mysql field values
+ $query_count = 0; #::: Used for chunking query updates
+ $print_cycle = 0; #::: Counter for console updates
+ $start_time = time(); #::: Start time for import
+ $total_items_file = scalar(grep $_, @item_file_lines) - 1; #::: Total items in text file
+
+ #::: Iterate through each item in items.txt
+ for (1 .. $#item_file_lines) {
+ @f = split("(? 500){
+ $query_insert_ph = substr($query_insert_ph, 0, -2);
+ $dbh->prepare($master_insert . " " . $query_insert_ph)->execute(@field_values);
+ $query_count = 0;
+ $query_insert_ph = "";
+ @field_values = ();
+ }
+
+ #::: Print updates to console
+ if($print_cycle > 25){
+ print "Processing (" . $read_items_file . ") :: (Items: " . $total_items . "/" . $total_items_file . ") \r";
+ $print_cycle = 0;
+ }
+
+ #::: Counters
+ $total_items++;
+ $query_count++;
+ $print_cycle++;
+ }
+
+ #::: One last processing print
+ print "Processing (" . $read_items_file . ") :: (Items: " . $total_items . "/" . $total_items_file . ") \r";
+
+ printf "\n" . $total_items . " items added to database... Took " . (time() - $start_time) . " second(s)... \n";
+
+ print "Flipping slots 21 and 22...";
+ $rows_affected = $dbh->prepare("
+ UPDATE `items_floor`
+ SET `slots` = (`slots` ^ 6291456)
+ WHERE (`slots` & 6291456)
+ IN (2097152, 4194304)")->execute();
+ print " Rows affected (" . $rows_affected . ")\n";
+}
+
+sub update_items_table {
+
+ #::: Keep Items table sane
+ $query_handle = $dbh->prepare("
+ ALTER TABLE `items`
+ MODIFY COLUMN `UNK132` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL;
+ ");
+ $query_handle->execute();
+
+ my @matching_table;
+ my @missing_items_table;
+ my @missing_items_floor_table;
+
+ #::: Get columns from `items`
+ my $sth = $dbh->prepare("SHOW COLUMNS FROM `items`;");
+ $sth->execute();
+ my @items_table;
+ while (my @row = $sth->fetchrow_array()) {
+ push(@items_table, $row[0]);
+ }
+
+ #::: Get columns from `items_floor`
+ $sth2 = $dbh->prepare("SHOW COLUMNS FROM `items_floor`");
+ $sth2->execute();
+ my @items_floor_table;
+ while (my @row = $sth2->fetchrow_array()) {
+ push(@items_floor_table, $row[0]);
+ }
+
+ #::: Go through the original items table columns and line them up with what columns match on 13th floor
+ #::: This is so we can use the matching columns to update and insert item data into `items` table
+ foreach $value (@items_table) {
+ if ( grep( /^$value$/i, @items_floor_table ) ) {
+ push(@matching_table, $value);
+ } else {
+ #::: What values are we missing from EMU items table..
+ push(@missing_items_table, $value);
+ }
+ }
+
+ #::: What values are we missing from.. 13thFloor
+ foreach $value (@items_floor_table) {
+ if ( grep( /^$value$/i, @items_table ) ) {
+ #DO NOTHING...
+ } else {
+ push(@missing_items_floor_table, $value);
+ }
+ }
+
+ #::: Go through the matched columns and build our query strings...
+
+ my $items_field_list = ""; #::: Build the field list for the INSERT (field1, field2)
+ my $items_floor_field_list = ""; #::: What fields we will select from items_floor table to insert into items (matched columns)
+ my $update_fields = ""; #::: To update an existing item entry if it exists...
+
+ foreach $match (@matching_table) {
+ $match = lc($match);
+ $update_fields .= "`" . $match . "` = fi.`" . $match . "`, ";
+ $items_field_list .= "`" . $match . "`, ";
+ $items_floor_field_list .= "fi.`" . $match . "`, ";
+ }
+ #::: Trim ', ' off the ends
+ $update_fields = substr($update_fields, 0, -2);
+ $items_field_list = substr($items_field_list, 0, -2);
+ $items_floor_field_list = substr($items_floor_field_list, 0, -2);
+
+ #::: Mixed up fields...
+ $items_floor_field_list =~ s/booktype/booklang/g; #our booktype is mixed with theirs...
+ $update_fields =~ s/`booktype` = fi.`booktype`/`booktype` = fi.`booklang`/g;
+
+ #::: FIELDS THAT DO NOT MATCH GO HERE
+ my @items_add = (
+ "casttime_", "endur", "range", "attuneable", "evolvinglevel", "herosforgemodel", "scrolltype",
+ "scriptfileid", "powersourcecapacity", "augslot1unk2", "augslot2unk2", "augslot3unk2", "augslot4unk2",
+ "augslot5unk2", "augslot6unk2", "recskill", "book"
+ );
+ my @items_floor_add = (
+ "foodduration", "endurance", "therange", "attunable", "evolvl", "heroforge1", "scrolleffecttype",
+ "rightclickscriptid", "powersourcecap", "augslot1unk", "augslot2unk", "augslot3unk", "augslot4unk",
+ "augslot5unk", "augslot6unk", "reqskill", "booktype"
+ );
+
+ #::: Match the mis-matched fields...
+ my $spot = 0;
+ foreach $value (@items_add) {
+ $items_field_list .= ", `" . $value . "`";
+ $update_fields .= ", `" . $value . "` = fi.`" . $items_floor_add[$spot] . "`";
+ $spot++;
+ @missing_items_table = grep {$_ ne $value} @missing_items_table;
+ }
+ foreach $value (@items_floor_add) {
+ $items_floor_field_list .= ", fi.`" . $value . "`";
+ @missing_items_floor_table = grep {$_ ne $value} @missing_items_floor_table;
+ }
+
+ my $update_query = "
+ INSERT INTO items (" . $items_field_list . ")
+ SELECT " . $items_floor_field_list . "
+ FROM items_floor fi
+ ON DUPLICATE KEY UPDATE " . $update_fields;
+
+ #::: Print missing fields to file
+ my $write_file = "missing_item_fields.txt";
+
+ open(F, ">$write_file") or die "Unable to open questfile: $write_file\n";
+ print F "$update_query \n\n";
+ print F "EQEMU items Table missing fields\n";
+ foreach $value (@missing_items_table) {
+ print F "$value\n";
+ }
+ print F "\n\n13thFloor items Table missing fields\n";
+ foreach $value (@missing_items_floor_table) {
+ print F "$value\n";
+ }
+ close(F);
+
+ #::: Number of rows affected by query
+ $rows = $dbh->do($update_query);
+
+ #::: Update stackables
+ $dbh->do("UPDATE items i SET i.stackable = 1 WHERE i.stacksize > 1");
+
+ print "Added all new items to Items table (" . $rows . ")!\n";
+
+}
+
+sub trim($) {
+ my $string = shift;
+ $string =~ s/^\s+//;
+ $string =~ s/\s+$//;
+ return $string;
+}
\ No newline at end of file
diff --git a/utils/scripts/load_13thfloor_items.pl b/utils/scripts/load_13thfloor_items.pl
deleted file mode 100644
index 03fbfe5fb..000000000
--- a/utils/scripts/load_13thfloor_items.pl
+++ /dev/null
@@ -1,77 +0,0 @@
-#! /usr/bin/perl
-
-use DBI;
-use Getopt::Std;
-
-getopts('d:h');
-$conf = "eqemu_config.xml";
-if($opt_h) {
- die "Usage: load_13thfloor_items.pl [-d path/to/eqemu_config.xml]\n";
-}
-if($opt_d) {
- $conf = $opt_d;
-}
-
-$db = "eq";
-$user = "eq";
-$pass = "eq";
-$host = "localhost";
-open(F, "<$conf") or die "Unable to open config $conf\n";
-$indb = 0;
-while() {
- s/\r//g;
- if(//i) {
- $indb = 1;
- }
- next unless($indb == 1);
- if(/<\/database>/i) {
- $indb = 0;
- last;
- }
- if(/(.*)<\/host>/i) {
- $host = $1;
- } elsif(/(.*)<\/username>/i) {
- $user = $1;
- } elsif(/(.*)<\/password>/i) {
- $pass = $1;
- } elsif(/(.*)<\/db>/i) {
- $db = $1;
- }
-}
-if(!$db || !$user || !$pass || !$host) {
- die "Invalid db.ini, missing one of: host, user, password, database\n";
-}
-
-$source="DBI:mysql:database=$db;host=$host";
-
-my $dbh = DBI->connect($source, $user, $pass) || die "Could not create db handle\n";
-
-$_=;
-chomp();
-s/'/\\'/g;
-@fields=split("(? "itemuse"
-);
-
-$insert="replace into items (".join(",",@fields).",source,updated) values ('";
-$insert=~s/UNK130/potionbeltslots/;
-$insert=~s/UNK133/stackable/;
-
-#select(STDOUT); $|=1;
-while() {
- chomp();
- s/'/\\'/g;
- @f=split("(?do($statement);
- printf("Processing: %d %s \r",$f[4],$f[1]);
- ++$count;
-}
-printf("Processed: %d items(s) \n",$count);
-
diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt
index 2f37dfe84..86f7ebe7c 100644
--- a/utils/sql/db_update_manifest.txt
+++ b/utils/sql/db_update_manifest.txt
@@ -1,261 +1,261 @@
-5001|1_task_system.sql
+5001|1_task_system.sql|SHOW TABLES LIKE 'tasks'|empty|
# 5002|2_optional_maxclients.sql
# 5003|14_optional_merchantlist.sql
-5004|35_task_stepped.sql
-5005|42_task_min_maxlevel.sql
-5006|55_zone_shutdowndeleay.sql
+5004|35_task_stepped.sql|SHOW COLUMNS FROM `tasks` LIKE 'stepped'|not_empty|
+5005|42_task_min_maxlevel.sql|SHOW COLUMNS FROM `tasks` LIKE 'minlevel'|empty|
+5006|55_zone_shutdowndeleay.sql|SHOW COLUMNS FROM `zone` LIKE 'shutdowndelay'|empty|
# 5007|68_optional_character_maxexplevel.sql
# 5008|103_optional_chat_rules.sql
-5009|104_traps.sql
+5009|104_traps.sql|SHOW COLUMNS FROM `traps` LIKE 'respawn_time'|empty|
# 5010|106_optional_proc_rules.sql
-5011|120_damageshieldtypes.sql
-5012|125_aggrozone.sql
+5011|120_damageshieldtypes.sql|SHOW TABLES LIKE 'damageshieldtypes'|empty|
+# 5012|125_aggrozone.sql
# 5013|127_optional_spell_rules.sql
# 5014|129_optional_shared_plat_rule.sql
# 5015|131_optional_combat_rules.sql
-5016|133_task_repeatable.sql
-5017|142_deathpeace_and_lifetap_aas.sql
+5016|133_task_repeatable.sql|SHOW COLUMNS FROM `tasks` LIKE 'repeatable'|empty|
+5017|142_deathpeace_and_lifetap_aas.sql|SELECT * FROM db_version WHERE version > 5016|empty|
# 5018|158_optional_death_exp_loss.sql
-5019|176_melody.sql
-5020|189_character_.sql
-5021|196_trader.sql
-5022|210_undyeme.sql
-5023|222_buyer.sql
-5024|226_account_limiting.sql
-5025|230_spells_table.sql
-5026|235_horses_table.sql
-5027|243_spawn_timers.sql
-5028|247_mail.sql
-5029|249_chatchannels.sql
-5030|250_bot_spell_update.sql
+# 5019|176_melody.sql
+5020|189_character_.sql|SELECT * FROM db_version WHERE version >= 5020|empty|
+5021|196_trader.sql|SHOW TABLES LIKE 'trader'|empty|
+# 5022|210_undyeme.sql
+5023|222_buyer.sql|SHOW TABLES LIKE 'buyer'|empty|
+# 5024|226_account_limiting.sql
+5025|230_spells_table.sql|SHOW TABLES LIKE 'spells_new'|empty|
+5026|235_horses_table.sql|SHOW TABLES LIKE 'horses'|empty|
+5027|243_spawn_timers.sql|SHOW TABLES LIKE 'respawn_times'|empty|
+5028|247_mail.sql|SHOW TABLES LIKE 'mail'|empty|
+5029|249_chatchannels.sql|SHOW TABLES LIKE 'chatchannels'|empty|
+# 5030|250_bot_spell_update.sql
# 5031|250_optional_bot_spell_update.sql
# 5032|285_optional_bot_spell_update.sql
-5033|292_augslots.sql
-5034|294_merchant_logging.sql
-5035|304_faction_list.sql
-5036|326_aas.sql
-5037|328_bot_management.sql
+# 5033|292_augslots.sql|SELECT * FROM db_version WHERE version >= 5033|empty|
+5034|294_merchant_logging.sql|SHOW COLUMNS FROM `eventlog` LIKE 'event_nid'|empty|
+5035|304_faction_list.sql|SELECT * FROM db_version WHERE version >= 5035|empty|
+5036|326_aas.sql|SELECT * FROM db_version WHERE version > 5035|empty|
+# 5037|328_bot_management.sql
# 5038|328_optional_bot_management.sql
-5039|340_gm_ips.sql
-5040|356_combat.sql
-5041|360_peqzone.sql
-5042|364_ranged_dist_rule.sql
-5043|386_bot_save_raid.sql
+5039|340_gm_ips.sql|SHOW TABLES LIKE 'gm_ips'|empty|
+# 5040|356_combat.sql
+# 5041|360_peqzone.sql
+# 5042|364_ranged_dist_rule.sql
+# 5043|386_bot_save_raid.sql
# 5044|434_optional_rest_state_rules.sql
-5045|447_sof_startzone_rule.sql
-5046|463_altadv_vars.sql
-5047|475_aa_actions.sql
-5048|500_spawn2_optimization.sql
-5049|503_bugs.sql
-5050|518_drakkin_npc_type_features.sql
-5051|524_rule_values_notes.sql
-5052|527_npc_armor_tint.sql
-5053|553_saylink_table.sql
-5054|564_nokeyring.sql
-5055|600_group_leadership.sql
-5056|612_instance_changes.sql
-5057|615_adventure_assassination.sql
-5058|619_Adventure_Recruiter_Flavor.sql
-5059|621_LDoNTraps.sql
-5060|633_ucs.sql
-5061|634_TrapTemplateDefaultValue.sql
-5062|643_BotsTable.sql
-5063|646_archery_penalty_rule.sql
-5064|665_heroic_resists.sql
-5065|667_titles.sql
-5066|687_aa_table_changes.sql
-5067|699_peqzone_rule.sql
-5068|702_aashieldblock_tint_table.sql
-5069|703_peqzone_rule.sql
-5070|704_rules.sql
-5071|710_tint_set_naming.sql
-5072|721_pathing_rules.sql
-5073|730_smart_delay_moving.sql
-5074|731_rule_assist_notarget_self.sql
-5075|732_sacrifice_rules.sql
-5076|745_slow_mitigation.sql
-5077|754_archery_base_damage_rule.sql
-5078|755_sof_altadv_vars_updates.sql
-5079|773_monk_rules.sql
+# 5045|447_sof_startzone_rule.sql
+# 5046|463_altadv_vars.sql
+# 5047|475_aa_actions.sql
+5048|500_spawn2_optimization.sql|SELECT * FROM db_version WHERE version >= 5048|empty|
+5049|503_bugs.sql|SHOW TABLES LIKE 'bugs'|empty|
+5050|518_drakkin_npc_type_features.sql|SHOW TABLES LIKE 'bugs'|empty|
+5051|524_rule_values_notes.sql|SELECT * FROM db_version WHERE version >= 5051|empty|
+5052|527_npc_armor_tint.sql|SELECT * FROM db_version WHERE version >= 5052|empty|
+5053|553_saylink_table.sql|SHOW TABLES LIKE 'saylink'|empty|
+5054|564_nokeyring.sql|SHOW COLUMNS FROM `doors` LIKE 'nokeyring'|empty|
+5055|600_group_leadership.sql|SELECT * FROM db_version WHERE version >= 5055|empty|
+5056|612_instance_changes.sql|SELECT * FROM db_version WHERE version >= 5056|empty|
+5057|615_adventure_assassination.sql|SELECT * FROM db_version WHERE version >= 5057|empty|
+5058|619_Adventure_Recruiter_Flavor.sql|SELECT * FROM db_version WHERE version >= 5058|empty|
+5059|621_LDoNTraps.sql|SHOW TABLES LIKE 'ldon_trap_templates'|empty|
+5060|633_ucs.sql|SHOW TABLES LIKE 'friends'|empty|
+5061|634_TrapTemplateDefaultValue.sql|SHOW COLUMNS FROM `npc_types` LIKE 'trap_template'|empty|
+# 5062|643_BotsTable.sql
+# 5063|646_archery_penalty_rule.sql
+5064|665_heroic_resists.sql|SELECT * FROM db_version WHERE version >= 5064|empty|
+5065|667_titles.sql|SHOW TABLES LIKE 'titles'|empty|
+5066|687_aa_table_changes.sql|SELECT * FROM db_version WHERE version >= 5066|empty|
+# 5067|699_peqzone_rule.sql
+5068|702_aashieldblock_tint_table.sql|SHOW TABLES LIKE 'npc_types_tint'|empty|
+# 5069|703_peqzone_rule.sql
+# 5070|704_rules.sql
+5071|710_tint_set_naming.sql|SELECT * FROM db_version WHERE version >= 5071|empty|
+5072|721_pathing_rules.sql|SELECT * FROM db_version WHERE version >= 5072|empty|
+# 5073|730_smart_delay_moving.sql
+# 5074|731_rule_assist_notarget_self.sql
+# 5075|732_sacrifice_rules.sql
+5076|745_slow_mitigation.sql|SELECT * FROM db_version WHERE version >= 5076|empty|
+# 5077|754_archery_base_damage_rule.sql
+5078|755_sof_altadv_vars_updates.sql|SELECT * FROM db_version WHERE version >= 5078|empty|
+# 5079|773_monk_rules.sql
# 5080|853_optional_rule_aaexp.sql
# 5081|858_optional_rule_ip_limit_by_status.sql
# 5082|892_optional_bots_table_mod.sql
# 5083|893_optional_bots_table_mod.sql
-5084|898_npc_maxlevel_scalerate.sql
+5084|898_npc_maxlevel_scalerate.sql|SHOW COLUMNS FROM `npc_types` LIKE 'maxlevel'|empty|
# 5085|902_optional_rule_snareflee.sql
-5086|923_spawn2_enabled.sql
-5087|962_hot_zone.sql
-5088|964_reports.sql
-5089|971_veteran_rewards.sql
-5090|977_raid_npc_private_corpses.sql
-5091|979_unique_spawn_by_name.sql
-5092|980_account_ip.sql
-5093|1022_botadventuring.sql
-5094|1027_botactives.sql
-5095|1030_botzoningsupport.sql
-5096|1036_botbuffs.sql
-5097|1038_botpetstatepersists.sql
-5098|1038_grouptablesuniquecolumndefinitions.sql
-5099|1039_botguilds.sql
-5100|1040_DeprecatedBotRaidsSystems.sql
-5101|1057_titles.sql
-5102|1077_botgroups.sql
-5103|1136_spell_globals.sql
+5086|923_spawn2_enabled.sql|SHOW COLUMNS FROM `spawn2` LIKE 'enabled'|empty|
+5087|962_hot_zone.sql|SHOW COLUMNS FROM `zone` LIKE 'hotzone'|empty|
+5088|964_reports.sql|SHOW TABLES LIKE 'reports'|empty|
+5089|971_veteran_rewards.sql|SHOW TABLES LIKE 'veteran_reward_templates'|empty|
+5090|977_raid_npc_private_corpses.sql|SELECT * FROM db_version WHERE version >= 5090|empty|
+5091|979_unique_spawn_by_name.sql|SHOW COLUMNS FROM `npc_types` LIKE 'unique_spawn_by_name'|empty|
+5092|980_account_ip.sql|SHOW TABLES LIKE 'account_ip'|empty|
+# 5093|1022_botadventuring.sql
+# 5094|1027_botactives.sql
+# 5095|1030_botzoningsupport.sql
+# 5096|1036_botbuffs.sql
+# 5097|1038_botpetstatepersists.sql
+5098|1038_grouptablesuniquecolumndefinitions.sql|SELECT * FROM db_version WHERE version >= 5098|empty|
+# 5099|1039_botguilds.sql
+# 5100|1040_DeprecatedBotRaidsSystems.sql
+5101|1057_titles.sql|SHOW TABLES LIKE 'player_titlesets'|empty|
+# 5102|1077_botgroups.sql
+5103|1136_spell_globals.sql|SHOW TABLES LIKE 'spell_globals'|empty|
# 5104|1144_optional_rule_return_nodrop.sql
-5105|1195_account_suspendeduntil.sql
-5106|1259_npc_skill_types.sql
-5107|1280_bot_augs.sql
+5105|1195_account_suspendeduntil.sql|SELECT * FROM db_version WHERE version >= 5105|empty|
+5106|1259_npc_skill_types.sql|SHOW COLUMNS FROM `npc_types` LIKE 'prim_melee_type'|empty|
+# 5107|1280_bot_augs.sql
# 5108|1290_optional_exp_loss_rule.sql
-5109|1293_guild_bank.sql
-5110|1379_loginserver_trusted_server.sql
-5111|1392_recipe_learning.sql
+5109|1293_guild_bank.sql|SHOW TABLES LIKE 'guild_bank'|empty|
+# 5110|1379_loginserver_trusted_server.sql
+5111|1392_recipe_learning.sql|SELECT * FROM db_version WHERE version >= 5111|empty|
# 5112|1394_optional_rule_sod_hp_mana_end.sql
-5113|1404_faction_list.sql
+5113|1404_faction_list.sql|SELECT * FROM db_version WHERE version >= 5113|empty|
# 5114|1410_optional_sod_aas_ht_and_loh.sql
-5115|1436_login_server_table_fix.sql
-5116|1446_allowrest_optional.sql
-5117|1446_allowrest_required.sql
-5118|1450_cvs.sql
-5119|1451_guilds.sql
-5120|1498_instance_adventure.sql
-5121|1510_global_instances.sql
-5122|1511_map_path_loading.sql
-5123|1513_zone_points.sql
-5124|1519_zone_primary_key_id.sql
-5125|1542_items_table_cleanup.sql
-5126|1548_nimbuseffect_required.sql
-5127|1562_instanced_spawnconditions.sql
-5128|1586_waypoints_optional.sql
-5129|1610_tradeskill_required.sql
-5130|1618_zone.sql
+# 5115|1436_login_server_table_fix.sql
+# 5116|1446_allowrest_optional.sql
+5117|1446_allowrest_required.sql|SELECT * FROM db_version WHERE version >= 5117|empty|
+# 5118|1450_cvs.sql
+5119|1451_guilds.sql|SELECT * FROM db_version WHERE version >= 5119|empty|
+5120|1498_instance_adventure.sql|SELECT * FROM db_version WHERE version >= 5120|empty|
+5121|1510_global_instances.sql|SELECT * FROM db_version WHERE version >= 5121|empty|
+5122|1511_map_path_loading.sql|SHOW COLUMNS FROM `zone` LIKE 'map_file_name'|empty|
+5123|1513_zone_points.sql|SELECT * FROM db_version WHERE version >= 5123|empty|
+5124|1519_zone_primary_key_id.sql|SELECT * FROM db_version WHERE version >= 5124|empty|
+5125|1542_items_table_cleanup.sql|SELECT * FROM db_version WHERE version >= 5125|empty|
+5126|1548_nimbuseffect_required.sql|SELECT * FROM db_version WHERE version >= 5126|empty|
+5127|1562_instanced_spawnconditions.sql|SHOW TABLES LIKE 'spawn_condition_values'|empty|
+# 5128|1586_waypoints_optional.sql
+5129|1610_tradeskill_required.sql|SELECT * FROM db_version WHERE version >= 5129|empty|
+5130|1618_zone.sql|SELECT * FROM db_version WHERE version >= 5130|empty|
# 5131|1625_optional_rule_class_race_exp_bonus.sql
# 5132|1672_optional_rules_respawn_window.sql
# 5133|1679_optional_rules_blocked_buffs.sql
-5134|1696_modify_zone_and_object_tables.sql
-5135|1711_account_restricted_aa.sql
+5134|1696_modify_zone_and_object_tables.sql|SELECT * FROM db_version WHERE version >= 5134|empty|
+5135|1711_account_restricted_aa.sql|SHOW COLUMNS FROM `account` LIKE 'time_creation'|empty|
# 5136|1717_optional_rule_bash_stun_chance.sql
# 5137|1718_optional_rules_mod3s.sql
# 5138|1719_optional_triggerOnCastAAs.sql
# 5139|1720_optional_sql_AAs.sql
-5140|1720_required_sql_AA_effects_update.sql
+# 5140|1720_required_sql_AA_effects_update.sql
# 5141|1721_optional_sql_drakkin_breath_update.sql
-5142|1721_required_sql_altadv_vars_update.sql
+# 5142|1721_required_sql_altadv_vars_update.sql
# 5143|1723_optional_sql_new_stats_window_rule.sql
-5144|1723_required_sql_corruption.sql
+5144|1723_required_sql_corruption.sql|SELECT * FROM db_version WHERE version >= 5144|empty|
# 5145|1736_optional_sql_feral_swipe.sql
-5146|1737_required_sql_rule_and_aa_update.sql
+# 5146|1737_required_sql_rule_and_aa_update.sql
# 5147|1746_optional_sql_bot_manaregen.sql
# 5148|1747_optional_HoT_zone_and_zonepoints.sql
# 5149|1750_optional_sql_reflect_rule.sql
# 5150|1753_optional_haste_cap_rule.sql
-5151|1753_required_sql_healing_adept_aa.sql
-5152|1754_required_sql_healing_adept_aa_fix.sql
-5153|1755_required_sql_fear_resist_aas.sql
+# 5151|1753_required_sql_healing_adept_aa.sql
+# 5152|1754_required_sql_healing_adept_aa_fix.sql
+# 5153|1755_required_sql_fear_resist_aas.sql
# 5154|1784_optional_corpsedrag_rules.sql
-5155|1786_required_update_to_aas.sql
-5156|1790_required_aa_required_level_cost.sql
-5157|1793_resist_adjust.sql
+# 5155|1786_required_update_to_aas.sql
+# 5156|1790_required_aa_required_level_cost.sql
+5157|1793_resist_adjust.sql|SHOW COLUMNS FROM `npc_spells_entries` LIKE 'resist_adjust'|empty|
# 5158|1799_optional_rest_regen_endurance_rule.sql
-5159|1802_required_doppelganger.sql
-5160|1803_required_tasks_xpreward_signed.sql
-5161|1804_required_ae_melee_updates.sql
+5159|1802_required_doppelganger.sql|SELECT * FROM db_version WHERE version >= 5159|empty|
+5160|1803_required_tasks_xpreward_signed.sql|SELECT * FROM db_version WHERE version >= 5160|empty|
+5161|1804_required_ae_melee_updates.sql|SELECT * FROM db_version WHERE version >= 5161|empty|
# 5162|1809_optional_rules.sql
-5163|1813_required_doppelganger_npcid_change.sql
+5163|1813_required_doppelganger_npcid_change.sql|SELECT * FROM db_version WHERE version >= 5163|empty|
# 5164|1817_optional_npc_archery_bonus_rule.sql
# 5165|1823_optional_delay_death.sql
-5166|1847_required_doors_dest_zone_size_32.sql
+5166|1847_required_doors_dest_zone_size_32.sql|SELECT * FROM db_version WHERE version >= 5166|empty|
# 5167|1859_optional_item_casts_use_focus_rule.sql
# 5168|1884_optional_bot_spells_update.sql
# 5169|1885_optional_rules_fv_pvp_expansions.sql
# 5170|1889_optional_skill_cap_rule.sql
-5171|1908_required_npc_types_definitions.sql
+5171|1908_required_npc_types_definitions.sql|SHOW COLUMNS FROM `npc_types` LIKE 'attack_count'|empty|
# 5172|1926_optional_stat_cap.sql
-5173|1944_spawn2.sql
-5174|1946_doors.sql
+5173|1944_spawn2.sql|SHOW COLUMNS FROM `spawn2` LIKE 'animation'|empty|
+5174|1946_doors.sql|SELECT * FROM db_version WHERE version >= 5166|empty|
# 5175|1960_optional_console_timeout_rule.sql
# 5176|1962_optional_guild_creation_window_rules.sql
# 5177|1963_optional_rule_live_like_focuses.sql
# 5178|1968_optional_enrage_rules.sql
# 5179|1972_optional_extradmg_item_cap.sql
-5180|1974_required_bot_spells_update.sql
-5181|1977_underwater.sql
+# 5180|1974_required_bot_spells_update.sql
+5181|1977_underwater.sql|SHOW COLUMNS FROM `npc_types` LIKE 'underwater'|empty|
# 5182|1998_optional_intoxication_and_looting_rules.sql
-5183|2004_charges_alt_currency.sql
+5183|2004_charges_alt_currency.sql|SHOW TABLES LIKE 'alternate_currency'|empty|
# 5184|2015_optional_specialization_training_rule.sql
# 5185|2016_optional_rule_bot_aa_expansion.sql
# 5186|2023_optional_mysqlcli.sql
# 5187|2024_optional_update_crystals.sql
-5188|2024_required_update.sql
-5189|2057_required_discovered_items.sql
+5188|2024_required_update.sql|SHOW TABLES LIKE 'char_create_combinations'|empty|
+5189|2057_required_discovered_items.sql|SHOW TABLES LIKE 'discovered_items'|empty|
# 5190|2058_optional_rule_discovered_items.sql
-5191|2062_required_version_changes.sql
-5192|2069_required_pets.sql
-5193|2079_player_speech.sql
-5194|2087_required_bots_hp_and_mana_and_spell_updates.sql
-5195|2098_required_zonepoint_version_changes.sql
-5196|2099_required_discovered_items_account_status.sql
-5197|2104_required_group_roles.sql
-5198|2107_required_bot_stances.sql
-5199|2129_required_lfguild.sql
-5200|2133_required_faction_loot_despawn.sql
-5201|2136_extended_targets.sql
-5202|2142_emotes.sql
+5191|2062_required_version_changes.sql|SELECT * FROM db_version WHERE version >= 5191|empty|
+5192|2069_required_pets.sql|SHOW TABLES LIKE 'pets_equipmentset'|empty|
+# 5193|2079_player_speech.sql
+# 5194|2087_required_bots_hp_and_mana_and_spell_updates.sql
+5195|2098_required_zonepoint_version_changes.sql|SELECT * FROM db_version WHERE version >= 5195|empty|
+5196|2099_required_discovered_items_account_status.sql|SELECT * FROM db_version WHERE version >= 5196|empty|
+5197|2104_required_group_roles.sql|SELECT * FROM db_version WHERE version >= 5197|empty|
+# 5198|2107_required_bot_stances.sql
+5199|2129_required_lfguild.sql|SHOW TABLES LIKE 'lfguild'|empty|
+5200|2133_required_faction_loot_despawn.sql|SELECT * FROM db_version WHERE version >= 5200|empty|
+5201|2136_extended_targets.sql|SELECT * FROM db_version WHERE version >= 5201|empty|
+5202|2142_emotes.sql|SELECT * FROM db_version WHERE version >= 5202|empty|
# 5203|2154_optional_rule_spell_procs_resists_falloff.sql
# 5204|2156_optional_charm_break_rule.sql
# 5205|2159_optional_defensiveproc_rules.sql
-5206|2164_require_bots_bottimers.sql
+# 5206|2164_require_bots_bottimers.sql
# 5207|2171_optional_SpecialAttackACBonus_rule.sql
# 5208|2176_optional_aa_expansion_SOF_fix.sql
# 5209|2176_optional_FrenzyBonus_rule.sql
-5210|2176_required_aa_updates.sql
-5211|2178_required_aa_updates.sql
+5210|2176_required_aa_updates.sql|SELECT * FROM db_version WHERE version >= 5210|empty|
+5211|2178_required_aa_updates.sql|SELECT * FROM db_version WHERE version >= 5211|empty|
# 5212|2183_optional_bot_xp_rule.sql
# 5213|2185_optional_NPCFlurryChacne_rule
# 5214|2185_optional_NPCFlurryChacne_rule.sql
# 5215|2185_optional_NPCFlurryChance_rule.sql
-5216|2185_required_aa_updates
-5217|2185_required_aa_updates.sql
+5216|2185_required_aa_updates|SELECT * FROM db_version WHERE version >= 5216|empty|
+5217|2185_required_aa_updates.sql|SELECT * FROM db_version WHERE version >= 5217|empty|
# 5218|2188_optional_miscspelleffect_rules
# 5219|2188_optional_miscspelleffect_rules.sql
-5220|2188_required_aa_updates
-5221|2188_required_aa_updates.sql
+# 5220|2188_required_aa_updates
+5221|2188_required_aa_updates.sql|SELECT * FROM db_version WHERE version >= 5221|empty|
# 5222|2189_optional_taunt_rules
# 5223|2189_optional_taunt_rules.sql
-5224|2195_required_sharedplatupdates.sql
+5224|2195_required_sharedplatupdates.sql|SELECT * FROM db_version WHERE version >= 5224|empty|
# 5225|2208_optional_aa_stacking_rule.sql
# 5226|2208_optional_EnableSoulAbrasionAA.sql
-5227|2208_required_aa_updates.sql
+5227|2208_required_aa_updates.sql|SELECT * FROM db_version WHERE version >= 5227|empty|
# 5228|2209_optional_additive_bonus_rule.sql
-5229|2213_loot_changes.sql
-5230|2214_faction_list_mod.sql
-5231|2215_required_aa_updates.sql
+5229|2213_loot_changes.sql|SELECT * FROM db_version WHERE version >= 5229|empty|
+5230|2214_faction_list_mod.sql|SHOW TABLES LIKE 'faction_list_mod'|empty|
+5231|2215_required_aa_updates.sql|SELECT * FROM db_version WHERE version >= 5231|empty|
# 5232|2243_optional_char_max_level_rule.sql
-5233|2260_probability.sql
-5234|2262_required_pet_discipline_update.sql
-5235|2264_required_aa_updates.sql
-5236|2268_QueryServ.sql
-5237|2268_required_updates.sql
+# 5233|2260_probability.sql
+5234|2262_required_pet_discipline_update.sql|SELECT * FROM db_version WHERE version >= 5234|empty|
+5235|2264_required_aa_updates.sql|SELECT * FROM db_version WHERE version >= 5235|empty|
+# 5236|2268_QueryServ.sql
+5237|2268_required_updates.sql|SELECT * FROM db_version WHERE version >= 5237|empty|
# 5238|2274_optional_rule_iplimitdisconnectall.sql
# 5239|2278_optional_rule_targetableswarmpet.sql
# 5240|2280_optional_rule_targetableswarmpet-rename.sql
-5241|2283_required_npc_changes.sql
-5242|2299_required_inspectmessage_fields.sql
+5241|2283_required_npc_changes.sql|SHOW COLUMNS FROM `npc_types` LIKE 'spellscale'|empty|
+5242|2299_required_inspectmessage_fields.sql|SELECT * FROM db_version WHERE version >= 5242|empty|
# 5243|2300_optional_loot_changes.sql
-5244|2304_QueryServ.sql
-5245|2340_required_maxbuffslotspet.sql
-5246|2361_QueryServ.sql
-5247|2361_required_qs_rule_values.sql
-5248|2370_required_aa_updates.sql
-5249|2376_required_aa_updates.sql
-# 5250|2380_optional_merc_data.sql
-# 5251|2380_optional_merc_merchant_npctypes_update.sql
-# 5252|2380_optional_merc_rules.sql
-5253|2383_required_group_ismerc.sql
+# 5244|2304_QueryServ.sql
+# 5245|2340_required_maxbuffslotspet.sql
+# 5246|2361_QueryServ.sql
+# 5247|2361_required_qs_rule_values.sql
+5248|2370_required_aa_updates.sql|SELECT * FROM db_version WHERE version >= 5248|empty|
+5249|2376_required_aa_updates.sql|SELECT * FROM db_version WHERE version >= 5249|empty|
+5250|2380_optional_merc_data.sql|SELECT * FROM db_version WHERE version >= 5250|empty|
+5251|2380_optional_merc_merchant_npctypes_update.sql|SELECT * FROM db_version WHERE version >= 5251|empty|
+5252|2380_optional_merc_rules.sql|SELECT * FROM db_version WHERE version >= 5252|empty|
+5253|2383_required_group_ismerc.sql|SELECT * FROM db_version WHERE version >= 5253|empty|
# 5254|2428_optional_levelbasedexpmods.sql
# 5255|2448_optional_stun_proc_aggro_rule.sql
-5256|2471_required_aa_updates.sql
-5257|2482_required_start_zones.sql
-5258|2504_required_aa_updates.sql
+5256|2471_required_aa_updates.sql|SELECT * FROM db_version WHERE version >= 5256|empty|
+5257|2482_required_start_zones.sql|SELECT * FROM db_version WHERE version >= 5257|empty|
+5258|2504_required_aa_updates.sql|SELECT * FROM db_version WHERE version >= 5258|empty|
8000|mercs.sql|SHOW TABLES LIKE 'merc_stats'|empty|
9000|2013_02_18_Merc_Rules_and_Tables.sql|SELECT * FROM `rule_values` WHERE `rule_name` LIKE '%Mercs:ResurrectRadius%'|empty|
9001|2013_02_25_Impr_HT_LT.sql|SHOW TABLES LIKE 'merc_inventory'|empty|
@@ -345,6 +345,9 @@
9089|2015_11_02_ai_idle_no_spell_recast_default_changes.sql|SELECT * FROM `rule_values` WHERE `rule_name` LIKE '%Spells:AI_IdleNoSpellMinRecast%' AND `rule_value` = '500'|not_empty|
9090|2015_12_01_spell_scribe_restriction_rule.sql|SELECT `rule_name` FROM `rule_values` WHERE `rule_name` LIKE 'Character:RestrictSpellScribing'|empty|
9091|2015_12_07_command_settings.sql|SHOW TABLES LIKE 'command_settings'|empty|
+9092|2015_12_17_eqtime.sql|SHOW TABLES LIKE 'eqtime'|empty|
+9093|2015_12_21_items_updates_evoitem.sql|SHOW COLUMNS FROM `items` LIKE 'evoitem'|empty|
+9094|2015_12_29_quest_zone_events.sql|SELECT * FROM perl_event_export_settings WHERE event_description = 'EVENT_SPAWN_ZONE'|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not
diff --git a/utils/sql/git/optional/2015_12_26_oow_aa_missing.sql b/utils/sql/git/optional/2015_12_26_oow_aa_missing.sql
new file mode 100644
index 000000000..50e3aade3
--- /dev/null
+++ b/utils/sql/git/optional/2015_12_26_oow_aa_missing.sql
@@ -0,0 +1,3 @@
+INSERT INTO `aa_ranks` (`id`, `upper_hotkey_sid`, `lower_hotkey_sid`, `title_sid`, `desc_sid`, `cost`, `level_req`, `spell`, `spell_type`, `recast_time`, `expansion`, `prev_id`, `next_id`) VALUES (1015, -1, -1, 1011, 1011, 0, 51, -1, 0, 0, 8, 1014, 1016);
+
+INSERT INTO `aa_rank_effects` (`rank_id`, `slot`, `effect_id`, `base1`, `base2`) VALUES (1015, 1, 262, 40, 7), (1015, 2, 262, 40, 8), (1015, 3, 262, 40, 9), (1015, 4, 262, 40, 10), (1015, 5, 262, 40, 11);
diff --git a/utils/sql/git/required/2015_12_17_eqtime.sql b/utils/sql/git/required/2015_12_17_eqtime.sql
new file mode 100644
index 000000000..c264edf27
--- /dev/null
+++ b/utils/sql/git/required/2015_12_17_eqtime.sql
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS `eqtime`;
+CREATE TABLE `eqtime` (
+ `minute` tinyint(4) not null default 0,
+ `hour` tinyint(4) not null default 0,
+ `day` tinyint(4) not null default 0,
+ `month` tinyint(4) not null default 0,
+ `year` int(4) not null default 0,
+ `realtime` int(11) not null default 0
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+
+INSERT INTO eqtime values (0,1,28,12,3766,1444035661);
\ No newline at end of file
diff --git a/utils/sql/git/required/2015_12_21_items_updates_evoitem.sql b/utils/sql/git/required/2015_12_21_items_updates_evoitem.sql
new file mode 100644
index 000000000..195df8a87
--- /dev/null
+++ b/utils/sql/git/required/2015_12_21_items_updates_evoitem.sql
@@ -0,0 +1,8 @@
+ALTER TABLE `items`
+ ADD COLUMN `evoitem` INT(11) NOT NULL DEFAULT '0' AFTER `purity`,
+ ADD COLUMN `evoid` INT(11) NOT NULL DEFAULT '0' AFTER `evoitem`,
+ ADD COLUMN `evomax` INT(11) NOT NULL DEFAULT '0' AFTER `evolvinglevel`,
+ CHANGE `UNK038` `skillmodmax` INT(11) NOT NULL DEFAULT '0',
+ CHANGE `UNK222` `heirloom` INT(11) NOT NULL DEFAULT '0',
+ CHANGE `UNK235` `placeable` INT(11) NOT NULL DEFAULT '0',
+ CHANGE `UNK242` `epicitem` INT(11) NOT NULL DEFAULT '0';
\ No newline at end of file
diff --git a/utils/sql/git/required/2015_12_29_quest_zone_events.sql b/utils/sql/git/required/2015_12_29_quest_zone_events.sql
new file mode 100644
index 000000000..783cb477e
--- /dev/null
+++ b/utils/sql/git/required/2015_12_29_quest_zone_events.sql
@@ -0,0 +1,4 @@
+INSERT INTO `perl_event_export_settings` (`event_id`, `event_description`, `export_qglobals`, `export_mob`, `export_zone`, `export_item`, `export_event`) VALUES (81, 'EVENT_SPAWN_ZONE', 0, 0, 0, 0, 1);
+INSERT INTO `perl_event_export_settings` (`event_id`, `event_description`, `export_qglobals`, `export_mob`, `export_zone`, `export_item`, `export_event`) VALUES (82, 'EVENT_DEATH_ZONE', 0, 0, 0, 0, 1);
+ALTER TABLE `rule_values`
+MODIFY COLUMN `notes` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL AFTER `rule_value`;
\ No newline at end of file
diff --git a/utils/sql/peq_aa_tables_post_rework.sql b/utils/sql/peq_aa_tables_post_rework.sql
index 53d465778..9d3b05eaf 100644
--- a/utils/sql/peq_aa_tables_post_rework.sql
+++ b/utils/sql/peq_aa_tables_post_rework.sql
@@ -2532,6 +2532,7 @@ INSERT INTO `aa_ranks` (`id`, `upper_hotkey_sid`, `lower_hotkey_sid`, `title_sid
(1012, -1, -1, 1011, 1011, 0, 51, -1, 0, 0, 8, 1011, 1013),
(1013, -1, -1, 1011, 1011, 0, 51, -1, 0, 0, 8, 1012, 1014),
(1014, -1, -1, 1011, 1011, 0, 51, -1, 0, 0, 8, 1013, 1015),
+ (1015, -1, -1, 1011, 1011, 0, 51, -1, 0, 0, 8, 1014, 1016),
(1016, -1, -1, 1011, 1011, 0, 51, -1, 0, 0, 8, 1015, -1),
(1017, 1017, 1017, 1017, 1017, 6, 59, 16531, 75, 15, 15, -1, 13726),
(1018, 1018, 1018, 1018, 1018, 2, 63, 16455, 69, 1, 15, -1, -1),
@@ -9526,6 +9527,11 @@ INSERT INTO `aa_rank_effects` (`rank_id`, `slot`, `effect_id`, `base1`, `base2`)
(1014, 3, 262, 32, 9),
(1014, 4, 262, 32, 10),
(1014, 5, 262, 32, 11),
+ (1015, 1, 262, 40, 7),
+ (1015, 2, 262, 40, 8),
+ (1015, 3, 262, 40, 9),
+ (1015, 4, 262, 40, 10),
+ (1015, 5, 262, 40, 11),
(1016, 1, 262, 50, 7),
(1016, 2, 262, 50, 8),
(1016, 3, 262, 50, 9),
diff --git a/world/net.cpp b/world/net.cpp
index d7669ce12..cd84c530a 100644
--- a/world/net.cpp
+++ b/world/net.cpp
@@ -345,8 +345,13 @@ int main(int argc, char** argv) {
database.ClearMerchantTemp();
}
Log.Out(Logs::General, Logs::World_Server, "Loading EQ time of day..");
- if (!zoneserver_list.worldclock.loadFile(Config->EQTimeFile.c_str()))
- Log.Out(Logs::General, Logs::World_Server, "Unable to load %s", Config->EQTimeFile.c_str());
+ TimeOfDay_Struct eqTime;
+ time_t realtime;
+ eqTime = database.LoadTime(realtime);
+ zoneserver_list.worldclock.SetCurrentEQTimeOfDay(eqTime, realtime);
+ Timer EQTimeTimer(600000);
+ EQTimeTimer.Start(600000);
+
Log.Out(Logs::General, Logs::World_Server, "Loading launcher list..");
launcher_list.LoadList();
@@ -470,6 +475,16 @@ int main(int argc, char** argv) {
database.PurgeExpiredInstances();
}
+ if (EQTimeTimer.Check())
+ {
+ TimeOfDay_Struct tod;
+ zoneserver_list.worldclock.GetCurrentEQTimeOfDay(time(0), &tod);
+ if (!database.SaveTime(tod.minute, tod.hour, tod.day, tod.month, tod.year))
+ Log.Out(Logs::General, Logs::World_Server, "Failed to save eqtime.");
+ else
+ Log.Out(Logs::Detail, Logs::World_Server, "EQTime successfully saved.");
+ }
+
//check for timeouts in other threads
timeout_manager.CheckTimeouts();
loginserverlist.Process();
@@ -519,8 +534,6 @@ int main(int argc, char** argv) {
void CatchSignal(int sig_num) {
Log.Out(Logs::General, Logs::World_Server,"Caught signal %d",sig_num);
- if(zoneserver_list.worldclock.saveFile(WorldConfig::get()->EQTimeFile.c_str())==false)
- Log.Out(Logs::General, Logs::World_Server,"Failed to save time file.");
RunLoops = false;
}
diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp
index f6447f3f4..580053a3b 100644
--- a/world/zoneserver.cpp
+++ b/world/zoneserver.cpp
@@ -989,8 +989,8 @@ bool ZoneServer::Process() {
Log.Out(Logs::Detail, Logs::World_Server,"Received SetWorldTime");
eqTimeOfDay* newtime = (eqTimeOfDay*) pack->pBuffer;
zoneserver_list.worldclock.SetCurrentEQTimeOfDay(newtime->start_eqtime, newtime->start_realtime);
- Log.Out(Logs::Detail, Logs::World_Server,"New time = %d-%d-%d %d:%d (%d)\n", newtime->start_eqtime.year, newtime->start_eqtime.month, (int)newtime->start_eqtime.day, (int)newtime->start_eqtime.hour, (int)newtime->start_eqtime.minute, (int)newtime->start_realtime);
- zoneserver_list.worldclock.saveFile(WorldConfig::get()->EQTimeFile.c_str());
+ Log.Out(Logs::Detail, Logs::World_Server, "New time = %d-%d-%d %d:%d (%d)\n", newtime->start_eqtime.year, newtime->start_eqtime.month, (int)newtime->start_eqtime.day, (int)newtime->start_eqtime.hour, (int)newtime->start_eqtime.minute, (int)newtime->start_realtime);
+ database.SaveTime((int)newtime->start_eqtime.minute, (int)newtime->start_eqtime.hour, (int)newtime->start_eqtime.day, newtime->start_eqtime.month, newtime->start_eqtime.year);
zoneserver_list.SendTimeSync();
break;
}
diff --git a/zone/aggro.cpp b/zone/aggro.cpp
index a1903c8ca..a2ee52357 100644
--- a/zone/aggro.cpp
+++ b/zone/aggro.cpp
@@ -431,11 +431,20 @@ void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) {
if (sender->GetPrimaryFaction() == 0 )
return; // well, if we dont have a faction set, we're gonna be indiff to everybody
+ if (sender->HasAssistAggro())
+ return;
+
for (auto it = npc_list.begin(); it != npc_list.end(); ++it) {
NPC *mob = it->second;
if (!mob)
continue;
+ if (mob->CheckAggro(attacker))
+ continue;
+
+ if (sender->NPCAssistCap() >= RuleI(Combat, NPCAssistCap))
+ break;
+
float r = mob->GetAssistRange();
r = r * r;
@@ -476,7 +485,8 @@ void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) {
attacker->GetName(), DistanceSquared(mob->GetPosition(),
sender->GetPosition()), fabs(sender->GetZ()+mob->GetZ()));
#endif
- mob->AddToHateList(attacker, 1, 0, false);
+ mob->AddToHateList(attacker, 25, 0, false);
+ sender->AddAssistCap();
}
}
}
diff --git a/zone/attack.cpp b/zone/attack.cpp
index 2752752aa..ad5be845a 100644
--- a/zone/attack.cpp
+++ b/zone/attack.cpp
@@ -2009,15 +2009,15 @@ void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, SkillUseTypes attack
}
}
-bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack_skill) {
- Log.Out(Logs::Detail, Logs::Combat, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", killerMob->GetName(), damage, spell, attack_skill);
+bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, SkillUseTypes attack_skill) {
+ Log.Out(Logs::Detail, Logs::Combat, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", killer_mob->GetName(), damage, spell, attack_skill);
Mob *oos = nullptr;
- if(killerMob) {
- oos = killerMob->GetOwnerOrSelf();
+ if(killer_mob) {
+ oos = killer_mob->GetOwnerOrSelf();
char buffer[48] = { 0 };
- snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill));
+ snprintf(buffer, 47, "%d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast(attack_skill));
if(parse->EventNPC(EVENT_DEATH, this, oos, buffer, 0) != 0)
{
if(GetHP() < 0) {
@@ -2026,15 +2026,15 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
return false;
}
- if(killerMob && killerMob->IsClient() && (spell != SPELL_UNKNOWN) && damage > 0) {
+ if(killer_mob && killer_mob->IsClient() && (spell != SPELL_UNKNOWN) && damage > 0) {
char val1[20]={0};
entity_list.MessageClose_StringID(this, false, 100, MT_NonMelee, HIT_NON_MELEE,
- killerMob->GetCleanName(), GetCleanName(), ConvertArray(damage, val1));
+ killer_mob->GetCleanName(), GetCleanName(), ConvertArray(damage, val1));
}
} else {
char buffer[48] = { 0 };
- snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill));
+ snprintf(buffer, 47, "%d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast(attack_skill));
if(parse->EventNPC(EVENT_DEATH, this, nullptr, buffer, 0) != 0)
{
if(GetHP() < 0) {
@@ -2072,21 +2072,21 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
EQApplicationPacket* app= new EQApplicationPacket(OP_Death,sizeof(Death_Struct));
Death_Struct* d = (Death_Struct*)app->pBuffer;
d->spawn_id = GetID();
- d->killer_id = killerMob ? killerMob->GetID() : 0;
+ d->killer_id = killer_mob ? killer_mob->GetID() : 0;
d->bindzoneid = 0;
d->spell_id = spell == SPELL_UNKNOWN ? 0xffffffff : spell;
d->attack_skill = SkillDamageTypes[attack_skill];
d->damage = damage;
app->priority = 6;
- entity_list.QueueClients(killerMob, app, false);
+ entity_list.QueueClients(killer_mob, app, false);
if(respawn2) {
respawn2->DeathReset(1);
}
- if (killerMob) {
+ if (killer_mob) {
if(GetClass() != LDON_TREASURE)
- hate_list.AddEntToHateList(killerMob, damage);
+ hate_list.AddEntToHateList(killer_mob, damage);
}
safe_delete(app);
@@ -2148,8 +2148,8 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
{
if(!IsLdonTreasure && MerchantType == 0) {
kr->SplitExp((finalxp), this);
- if(killerMob && (kr->IsRaidMember(killerMob->GetName()) || kr->IsRaidMember(killerMob->GetUltimateOwner()->GetName())))
- killerMob->TrySpellOnKill(killed_level,spell);
+ if(killer_mob && (kr->IsRaidMember(killer_mob->GetName()) || kr->IsRaidMember(killer_mob->GetUltimateOwner()->GetName())))
+ killer_mob->TrySpellOnKill(killed_level,spell);
}
/* Send the EVENT_KILLED_MERIT event for all raid members */
for (int i = 0; i < MAX_RAID_MEMBERS; i++) {
@@ -2193,8 +2193,8 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
{
if(!IsLdonTreasure && MerchantType == 0) {
kg->SplitExp((finalxp), this);
- if(killerMob && (kg->IsGroupMember(killerMob->GetName()) || kg->IsGroupMember(killerMob->GetUltimateOwner()->GetName())))
- killerMob->TrySpellOnKill(killed_level,spell);
+ if(killer_mob && (kg->IsGroupMember(killer_mob->GetName()) || kg->IsGroupMember(killer_mob->GetUltimateOwner()->GetName())))
+ killer_mob->TrySpellOnKill(killed_level,spell);
}
/* Send the EVENT_KILLED_MERIT event and update kill tasks
* for all group members */
@@ -2244,8 +2244,8 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
if(!GetOwner() || (GetOwner() && !GetOwner()->IsClient()))
{
give_exp_client->AddEXP((finalxp), conlevel);
- if(killerMob && (killerMob->GetID() == give_exp_client->GetID() || killerMob->GetUltimateOwner()->GetID() == give_exp_client->GetID()))
- killerMob->TrySpellOnKill(killed_level,spell);
+ if(killer_mob && (killer_mob->GetID() == give_exp_client->GetID() || killer_mob->GetUltimateOwner()->GetID() == give_exp_client->GetID()))
+ killer_mob->TrySpellOnKill(killed_level,spell);
}
}
}
@@ -2393,20 +2393,30 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
uint16 emoteid = oos->GetEmoteID();
if(emoteid != 0)
oos->CastToNPC()->DoNPCEmote(KILLEDNPC, emoteid);
- killerMob->TrySpellOnKill(killed_level, spell);
+ killer_mob->TrySpellOnKill(killed_level, spell);
}
}
WipeHateList();
p_depop = true;
- if(killerMob && killerMob->GetTarget() == this) //we can kill things without having them targeted
- killerMob->SetTarget(nullptr); //via AE effects and such..
+ if(killer_mob && killer_mob->GetTarget() == this) //we can kill things without having them targeted
+ killer_mob->SetTarget(nullptr); //via AE effects and such..
entity_list.UpdateFindableNPCState(this, true);
char buffer[48] = { 0 };
- snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill));
+ snprintf(buffer, 47, "%d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast(attack_skill));
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, buffer, 0);
+
+ /* Zone controller process EVENT_DEATH_ZONE (Death events) */
+ if (RuleB(Zone, UseZoneController)) {
+ if (entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID) && this->GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID){
+ char data_pass[100] = { 0 };
+ snprintf(data_pass, 99, "%d %d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast(attack_skill), this->GetNPCTypeID());
+ parse->EventNPC(EVENT_DEATH_ZONE, entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID)->CastToNPC(), nullptr, data_pass, 0);
+ }
+ }
+
return true;
}
@@ -2422,6 +2432,11 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
hate = 1;
}
+ if (iYellForHelp)
+ SetPrimaryAggro(true);
+ else
+ SetAssistAggro(true);
+
bool wasengaged = IsEngaged();
Mob* owner = other->GetOwner();
Mob* mypet = this->GetPet();
@@ -2688,16 +2703,38 @@ uint8 Mob::GetWeaponDamageBonus(const Item_Struct *weapon, bool offhand)
}
} else {
// 2h damage bonus
+ int damage_bonus = 1 + (level - 28) / 3;
if (delay <= 27)
- return 1 + ((level - 28) / 3);
- else if (delay < 40)
- return 1 + ((level - 28) / 3) + ((level - 30) / 5);
- else if (delay < 43)
- return 2 + ((level - 28) / 3) + ((level - 30) / 5) + ((delay - 40) / 3);
- else if (delay < 45)
- return 3 + ((level - 28) / 3) + ((level - 30) / 5) + ((delay - 40) / 3);
- else if (delay >= 45)
- return 4 + ((level - 28) / 3) + ((level - 30) / 5) + ((delay - 40) / 3);
+ return damage_bonus + 1;
+ // Client isn't reflecting what the dev quoted, this matches better
+ if (level > 29) {
+ int level_bonus = (level - 30) / 5 + 1;
+ if (level > 50) {
+ level_bonus++;
+ int level_bonus2 = level - 50;
+ if (level > 67)
+ level_bonus2 += 5;
+ else if (level > 59)
+ level_bonus2 += 4;
+ else if (level > 58)
+ level_bonus2 += 3;
+ else if (level > 56)
+ level_bonus2 += 2;
+ else if (level > 54)
+ level_bonus2++;
+ level_bonus += level_bonus2 * delay / 40;
+ }
+ damage_bonus += level_bonus;
+ }
+ if (delay >= 40) {
+ int delay_bonus = (delay - 40) / 3 + 1;
+ if (delay >= 45)
+ delay_bonus += 2;
+ else if (delay >= 43)
+ delay_bonus++;
+ damage_bonus += delay_bonus;
+ }
+ return damage_bonus;
}
}
diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp
index 3aa9cc4a1..f8edee26d 100644
--- a/zone/bonuses.cpp
+++ b/zone/bonuses.cpp
@@ -490,6 +490,7 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu
(item->SkillModValue < 0 && newbon->skillmod[item->SkillModType] > item->SkillModValue))
{
newbon->skillmod[item->SkillModType] = item->SkillModValue;
+ newbon->skillmodmax[item->SkillModType] = item->SkillModMax;
}
}
diff --git a/zone/client.h b/zone/client.h
index b5d3728e1..6db22c5b0 100644
--- a/zone/client.h
+++ b/zone/client.h
@@ -679,7 +679,7 @@ public:
void IncreaseSkill(int skill_id, int value = 1) { if (skill_id <= HIGHEST_SKILL) { m_pp.skills[skill_id] += value; } }
void IncreaseLanguageSkill(int skill_id, int value = 1);
- virtual uint16 GetSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return((itembonuses.skillmod[skill_id] > 0) ? m_pp.skills[skill_id] * (100 + itembonuses.skillmod[skill_id]) / 100 : m_pp.skills[skill_id]); } return 0; }
+ virtual uint16 GetSkill(SkillUseTypes skill_id) const {if (skill_id <= HIGHEST_SKILL) {return(itembonuses.skillmod[skill_id] > 0 ? (itembonuses.skillmodmax[skill_id] > 0 ? std::min(m_pp.skills[skill_id] + itembonuses.skillmodmax[skill_id], m_pp.skills[skill_id] * (100 + itembonuses.skillmod[skill_id]) / 100) : m_pp.skills[skill_id] * (100 + itembonuses.skillmod[skill_id]) / 100) : m_pp.skills[skill_id]);} return 0;}
uint32 GetRawSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return(m_pp.skills[skill_id]); } return 0; }
bool HasSkill(SkillUseTypes skill_id) const;
bool CanHaveSkill(SkillUseTypes skill_id) const;
diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp
index a766f0470..f20ffb5f4 100644
--- a/zone/client_packet.cpp
+++ b/zone/client_packet.cpp
@@ -2925,150 +2925,292 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
return;
}
- // Delegate to tradeskill object to perform combine
AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer;
bool deleteItems = false;
if (GetClientVersion() >= ClientVersion::RoF)
{
ItemInst *itemOneToPush = nullptr, *itemTwoToPush = nullptr;
- //Message(15, "%i %i %i %i %i %i", in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id);
+ //Log.Out(Logs::DebugLevel::Moderate, Logs::Debug, "cslot: %i aslot: %i cidx: %i aidx: %i act: %i dest: %i",
+ // in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id);
- // Adding augment
- if (in_augment->augment_action == 0)
+ ItemInst *tobe_auged = nullptr, *old_aug = nullptr, *new_aug = nullptr, *aug = nullptr, *solvent = nullptr;
+ Inventory& user_inv = GetInv();
+
+ uint16 item_slot = in_augment->container_slot;
+ uint16 solvent_slot = in_augment->augment_slot;
+ uint8 mat = Inventory::CalcMaterialFromSlot(item_slot); // for when player is augging a piece of equipment while they're wearing it
+
+ if (item_slot == INVALID_INDEX || solvent_slot == INVALID_INDEX)
{
- ItemInst *tobe_auged = nullptr, *auged_with = nullptr;
- int8 slot = -1;
- Inventory& user_inv = GetInv();
+ Message(13, "Error: Invalid Aug Index.");
+ return;
+ }
- uint16 slot_id = in_augment->container_slot;
- uint16 aug_slot_id = in_augment->augment_slot;
- if (slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX)
+ tobe_auged = user_inv.GetItem(item_slot);
+ solvent = user_inv.GetItem(solvent_slot);
+
+ if (!tobe_auged)
+ {
+ Message(13, "Error: Invalid item passed for augmenting.");
+ return;
+ }
+
+ if ((in_augment->augment_action == 1) || (in_augment->augment_action == 2))
+ {
+ // Check for valid distiller if safely removing / swapping an augmentation
+
+ if (!solvent)
{
- Message(13, "Error: Invalid Aug Index.");
+ Log.Out(Logs::General, Logs::Error, "Player tried to safely remove an augment without a distiller.");
+ Message(13, "Error: Missing an augmentation distiller for safely removing this augment.");
return;
}
-
- tobe_auged = user_inv.GetItem(slot_id);
- auged_with = user_inv.GetItem(MainCursor);
-
- if (tobe_auged && auged_with)
+ else if (solvent->GetItem()->ItemType == ItemUseTypes::ItemTypeAugmentationDistiller)
{
- if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) &&
- (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots)))
+ old_aug = tobe_auged->GetAugment(in_augment->augment_index);
+
+ if (!old_aug)
{
- tobe_auged->PutAugment(in_augment->augment_index, *auged_with);
- tobe_auged->UpdateOrnamentationInfo();
+ Log.Out(Logs::General, Logs::Error, "Player tried to safely remove a nonexistent augment.");
+ Message(13, "Error: No augment found in slot %i for safely removing.", in_augment->augment_index);
+ return;
+ }
+ else if (solvent->GetItem()->ID != old_aug->GetItem()->AugDistiller)
+ {
+ Log.Out(Logs::General, Logs::Error, "Player tried to safely remove an augment with the wrong distiller (item %u vs expected %u).", solvent->GetItem()->ID, old_aug->GetItem()->AugDistiller);
+ Message(13, "Error: Wrong augmentation distiller for safely removing this augment.");
+ return;
+ }
+ }
+ else if (solvent->GetItem()->ItemType != ItemUseTypes::ItemTypePerfectedAugmentationDistiller)
+ {
+ Log.Out(Logs::General, Logs::Error, "Player tried to safely remove an augment with a non-distiller item.");
+ Message(13, "Error: Invalid augmentation distiller for safely removing this augment.");
+ return;
+ }
+ }
- ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index);
- if (aug) {
- std::vector args;
- args.push_back(aug);
- parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
-
- args.assign(1, tobe_auged);
- parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args);
- }
- else
- {
- Message(13, "Error: Could not find augmentation at index %i. Aborting.", in_augment->augment_index);
- return;
- }
-
- itemOneToPush = tobe_auged->Clone();
- // Must push items after the items in inventory are deleted - necessary due to lore items...
- if (itemOneToPush)
- {
- DeleteItemInInventory(slot_id, 0, true);
- DeleteItemInInventory(MainCursor, 0, true);
-
- if (PutItemInInventory(slot_id, *itemOneToPush, true))
- {
- CalcBonuses();
- // Successfully added an augment to the item
- return;
- }
- else
- {
- Message(13, "Error: No available slot for end result. Please free up the augment slot.");
- }
- }
- else
- {
- Message(13, "Error in cloning item for augment. Aborted.");
- }
+ switch (in_augment->augment_action)
+ {
+ case 0: // Adding an augment
+ case 2: // Swapping augment
+ new_aug = user_inv.GetItem(MainCursor);
+ if (!new_aug) // Shouldn't get the OP code without the augment on the user's cursor, but maybe it's h4x.
+ {
+ Log.Out(Logs::General, Logs::Error, "AugmentItem OpCode with 'Insert' or 'Swap' action received, but no augment on client's cursor.");
+ Message(13, "Error: No augment found on cursor for inserting.");
+ return;
}
else
{
- Message(13, "Error: No available slot for augment in that item.");
+ if (((tobe_auged->IsAugmentSlotAvailable(new_aug->GetAugmentType(), in_augment->augment_index)) != -1) &&
+ (tobe_auged->AvailableWearSlot(new_aug->GetItem()->Slots)))
+ {
+ old_aug = tobe_auged->RemoveAugment(in_augment->augment_index);
+ if (old_aug)
+ {
+ // An old augment was removed in order to be replaced with the new one (augment_action 2)
+
+ CalcBonuses();
+
+ std::vector args;
+ args.push_back(old_aug);
+ parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
+
+ args.assign(1, tobe_auged);
+ args.push_back(false);
+ parse->EventItem(EVENT_AUGMENT_REMOVE, this, old_aug, nullptr, "", in_augment->augment_index, &args);
+ }
+
+ tobe_auged->PutAugment(in_augment->augment_index, *new_aug);
+ tobe_auged->UpdateOrnamentationInfo();
+
+ aug = tobe_auged->GetAugment(in_augment->augment_index);
+ if (aug)
+ {
+ std::vector args;
+ args.push_back(aug);
+ parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
+
+ args.assign(1, tobe_auged);
+ parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args);
+ }
+ else
+ {
+ Message(13, "Error: Could not properly insert augmentation into augment slot %i. Aborting.", in_augment->augment_index);
+ return;
+ }
+
+ itemOneToPush = tobe_auged->Clone();
+ if (old_aug)
+ {
+ itemTwoToPush = old_aug->Clone();
+ }
+
+ // Must push items after the items in inventory are deleted - necessary due to lore items...
+ if (itemOneToPush)
+ {
+ DeleteItemInInventory(item_slot, 0, true);
+ DeleteItemInInventory(MainCursor, new_aug->IsStackable() ? 1 : 0, true);
+
+ if (solvent)
+ {
+ // Consume the augment distiller
+ DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true);
+ }
+
+ if (itemTwoToPush)
+ {
+ // This is a swap. Return the old aug to the player's cursor.
+ if (!PutItemInInventory(MainCursor, *itemTwoToPush, true))
+ {
+ Log.Out(Logs::General, Logs::Error, "Problem returning old augment to player's cursor after augmentation swap.");
+ Message(15, "Error: Failed to retrieve old augment after augmentation swap!");
+ }
+ }
+
+ if (PutItemInInventory(item_slot, *itemOneToPush, true))
+ {
+ // Successfully added an augment to the item
+
+ CalcBonuses();
+
+ if (mat != _MaterialInvalid)
+ {
+ SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed.
+ }
+ }
+ else
+ {
+ Message(13, "Error: No available slot for end result. Please free up the augment slot.");
+ }
+ }
+ else
+ {
+ Message(13, "Error in cloning item for augment. Aborted.");
+ }
+ }
+ else
+ {
+ Message(13, "Error: No available slot for augment in that item.");
+ }
}
- }
- }
- else if (in_augment->augment_action == 1)
- {
- ItemInst *tobe_auged = nullptr, *auged_with = nullptr;
- int8 slot = -1;
- Inventory& user_inv = GetInv();
-
- uint16 slot_id = in_augment->container_slot;
- uint16 aug_slot_id = in_augment->augment_slot; //it's actually solvent slot
- if (slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX)
- {
- Message(13, "Error: Invalid Aug Index.");
- return;
- }
-
- tobe_auged = user_inv.GetItem(slot_id);
- auged_with = user_inv.GetItem(aug_slot_id);
-
- ItemInst *old_aug = nullptr;
- if (!auged_with)
- return;
- const uint32 id = auged_with->GetID();
- ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index);
- if (aug) {
- std::vector args;
- args.push_back(aug);
- parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
-
- args.assign(1, tobe_auged);
-
- args.push_back(false);
-
- parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args);
- }
- else
- {
- Message(13, "Error: Could not find augmentation at index %i. Aborting.");
- return;
- }
- old_aug = tobe_auged->RemoveAugment(in_augment->augment_index);
- tobe_auged->UpdateOrnamentationInfo();
-
- itemOneToPush = tobe_auged->Clone();
- if (old_aug)
- itemTwoToPush = old_aug->Clone();
- if (itemOneToPush && itemTwoToPush && auged_with)
- {
- DeleteItemInInventory(slot_id, 0, true);
- DeleteItemInInventory(aug_slot_id, auged_with->IsStackable() ? 1 : 0, true);
-
- if (!PutItemInInventory(slot_id, *itemOneToPush, true))
+ break;
+ case 1: // Removing augment safely (distiller)
+ aug = tobe_auged->GetAugment(in_augment->augment_index);
+ if (aug)
{
- Message(15, "Failed to remove augment properly!");
+ std::vector args;
+ args.push_back(aug);
+ parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
+
+ args.assign(1, tobe_auged);
+
+ args.push_back(false);
+
+ parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args);
}
-
- if (PutItemInInventory(MainCursor, *itemTwoToPush, true))
+ else
{
+ Message(13, "Error: Could not find augmentation to remove at index %i. Aborting.", in_augment->augment_index);
+ return;
+ }
+
+ old_aug = tobe_auged->RemoveAugment(in_augment->augment_index);
+ tobe_auged->UpdateOrnamentationInfo();
+
+ itemOneToPush = tobe_auged->Clone();
+ if (old_aug)
+ itemTwoToPush = old_aug->Clone();
+
+ if (itemOneToPush && itemTwoToPush)
+ {
+ // Consume the augment distiller
+ DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true);
+
+ // Remove the augmented item
+ DeleteItemInInventory(item_slot, 0, true);
+
+ // Replace it with the unaugmented item
+ if (!PutItemInInventory(item_slot, *itemOneToPush, true))
+ {
+ Log.Out(Logs::General, Logs::Error, "Problem returning equipment item to player's inventory after safe augment removal.");
+ Message(15, "Error: Failed to return item after de-augmentation!");
+ }
+
CalcBonuses();
- //Message(15, "Successfully removed an augmentation!");
+
+ if (mat != _MaterialInvalid)
+ {
+ SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed.
+ }
+
+ // Drop the removed augment on the player's cursor
+ if (!PutItemInInventory(MainCursor, *itemTwoToPush, true))
+ {
+ Log.Out(Logs::General, Logs::Error, "Problem returning augment to player's cursor after safe removal.");
+ Message(15, "Error: Failed to return augment after removal from item!");
+ return;
+ }
}
- }
+ break;
+ case 3: // Destroying augment (formerly done in birdbath/sealer with a solvent)
+
+ // RoF client does not require an augmentation solvent for destroying an augmentation in an item.
+ // Augments can be destroyed with a right click -> Destroy at any time.
+
+ aug = tobe_auged->GetAugment(in_augment->augment_index);
+ if (aug)
+ {
+ std::vector args;
+ args.push_back(aug);
+ parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
+
+ args.assign(1, tobe_auged);
+
+ args.push_back(true);
+
+ parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args);
+ }
+ else
+ {
+ Message(13, "Error: Could not find augmentation to remove at index %i. Aborting.");
+ return;
+ }
+
+ tobe_auged->DeleteAugment(in_augment->augment_index);
+ tobe_auged->UpdateOrnamentationInfo();
+
+ itemOneToPush = tobe_auged->Clone();
+ if (itemOneToPush)
+ {
+ DeleteItemInInventory(item_slot, 0, true);
+
+ if (!PutItemInInventory(item_slot, *itemOneToPush, true))
+ {
+ Log.Out(Logs::General, Logs::Error, "Problem returning equipment item to player's inventory after augment deletion.");
+ Message(15, "Error: Failed to return item after destroying augment!");
+ }
+ }
+
+ CalcBonuses();
+
+ if (mat != _MaterialInvalid)
+ {
+ SendWearChange(mat);
+ }
+ break;
+ default: // Unknown
+ Log.Out(Logs::General, Logs::Inventory, "Unrecognized augmentation action - cslot: %i aslot: %i cidx: %i aidx: %i act: %i dest: %i",
+ in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id);
+ break;
}
}
else
{
+ // Delegate to tradeskill object to perform combine
Object::HandleAugmentation(this, in_augment, m_tradeskill_object);
}
return;
diff --git a/zone/common.h b/zone/common.h
index a637e94bd..48749dbc3 100644
--- a/zone/common.h
+++ b/zone/common.h
@@ -278,6 +278,7 @@ struct StatBonuses {
float AggroRange; // when calculate just replace original value with this
float AssistRange;
int32 skillmod[HIGHEST_SKILL+1];
+ int32 skillmodmax[HIGHEST_SKILL+1];
int effective_casting_level;
int reflect_chance; // chance to reflect incoming spell
uint32 singingMod;
diff --git a/zone/embparser.cpp b/zone/embparser.cpp
index 617d17d2e..12501b089 100644
--- a/zone/embparser.cpp
+++ b/zone/embparser.cpp
@@ -114,7 +114,9 @@ const char *QuestEventSubroutines[_LargestEventID] = {
"EVENT_RESPAWN",
"EVENT_DEATH_COMPLETE",
"EVENT_UNHANDLED_OPCODE",
- "EVENT_TICK"
+ "EVENT_TICK",
+ "EVENT_SPAWN_ZONE",
+ "EVENT_DEATH_ZONE",
};
PerlembParser::PerlembParser() : perl(nullptr) {
@@ -1424,6 +1426,21 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID
ExportVar(package_name.c_str(), "slotid", extradata);
break;
}
+ case EVENT_SPAWN_ZONE: {
+ Seperator sep(data);
+ ExportVar(package_name.c_str(), "spawned_entity_id", sep.arg[0]);
+ ExportVar(package_name.c_str(), "spawned_npc_id", sep.arg[1]);
+ break;
+ }
+ case EVENT_DEATH_ZONE: {
+ Seperator sep(data);
+ ExportVar(package_name.c_str(), "killer_id", sep.arg[0]);
+ ExportVar(package_name.c_str(), "killer_damage", sep.arg[1]);
+ ExportVar(package_name.c_str(), "killer_spell", sep.arg[2]);
+ ExportVar(package_name.c_str(), "killer_skill", sep.arg[3]);
+ ExportVar(package_name.c_str(), "killed_npc_id", sep.arg[4]);
+ break;
+ }
default: {
break;
diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp
index 126e8169d..bf43e43a1 100644
--- a/zone/embparser_api.cpp
+++ b/zone/embparser_api.cpp
@@ -2919,6 +2919,29 @@ XS(XS__UpdateInstanceTimer) {
XSRETURN_EMPTY;
}
+XS(XS__GetInstanceTimer);
+XS(XS__GetInstanceTimer) {
+ dXSARGS;
+ if (items != 0)
+ Perl_croak(aTHX_ "Usage: GetInstanceTimer()");
+
+ uint32 timer = quest_manager.GetInstanceTimer();
+
+ XSRETURN_UV(timer);
+}
+
+XS(XS__GetInstanceTimerByID);
+XS(XS__GetInstanceTimerByID) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: GetInstanceTimerByID(instance_id)");
+
+ uint16 instance_id = (uint16)SvUV(ST(0));
+ uint32 timer = quest_manager.GetInstanceTimerByID(instance_id);
+
+ XSRETURN_UV(timer);
+}
+
XS(XS__GetInstanceID);
XS(XS__GetInstanceID) {
dXSARGS;
@@ -3614,6 +3637,19 @@ XS(XS__debug)
XSRETURN_EMPTY;
}
+XS(XS__UpdateZoneHeader);
+XS(XS__UpdateZoneHeader) {
+ dXSARGS;
+ if (items != 2)
+ Perl_croak(aTHX_ "Usage: UpdateZoneHeader(type, value)");
+
+ std::string type = (std::string)SvPV_nolen(ST(0));
+ std::string value = (std::string)SvPV_nolen(ST(1));
+ quest_manager.UpdateZoneHeader(type, value);
+
+ XSRETURN_EMPTY;
+}
+
/*
This is the callback perl will look for to setup the
@@ -3650,6 +3686,8 @@ EXTERN_C XS(boot_quest)
newXS(strcpy(buf, "CreateInstance"), XS__CreateInstance, file);
newXS(strcpy(buf, "DestroyInstance"), XS__DestroyInstance, file);
newXS(strcpy(buf, "UpdateInstanceTimer"), XS__UpdateInstanceTimer, file);
+ newXS(strcpy(buf, "GetInstanceTimer"), XS__GetInstanceTimer, file);
+ newXS(strcpy(buf, "GetInstanceTimerByID"), XS__GetInstanceTimerByID, file);
newXS(strcpy(buf, "FlagInstanceByGroupLeader"), XS__FlagInstanceByGroupLeader, file);
newXS(strcpy(buf, "FlagInstanceByRaidLeader"), XS__FlagInstanceByRaidLeader, file);
newXS(strcpy(buf, "FlyMode"), XS__FlyMode, file);
@@ -3841,6 +3879,7 @@ EXTERN_C XS(boot_quest)
newXS(strcpy(buf, "untraindiscs"), XS__untraindiscs, file);
newXS(strcpy(buf, "updatespawntimer"), XS__UpdateSpawnTimer, file);
newXS(strcpy(buf, "updatetaskactivity"), XS__updatetaskactivity, file);
+ newXS(strcpy(buf, "UpdateZoneHeader"), XS__UpdateZoneHeader, file);
newXS(strcpy(buf, "varlink"), XS__varlink, file);
newXS(strcpy(buf, "voicetell"), XS__voicetell, file);
newXS(strcpy(buf, "we"), XS__we, file);
diff --git a/zone/entity.cpp b/zone/entity.cpp
index 23848f73e..92fefb2ec 100644
--- a/zone/entity.cpp
+++ b/zone/entity.cpp
@@ -641,8 +641,18 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue)
{
npc->SetID(GetFreeID());
npc->SetMerchantProbability((uint8) zone->random.Int(0, 99));
+
parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0);
+ /* Zone controller process EVENT_SPAWN_ZONE */
+ if (RuleB(Zone, UseZoneController)) {
+ if (entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID) && npc->GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID){
+ char data_pass[100] = { 0 };
+ snprintf(data_pass, 99, "%d %d", npc->GetID(), npc->GetNPCTypeID());
+ parse->EventNPC(EVENT_SPAWN_ZONE, entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID)->CastToNPC(), nullptr, data_pass, 0);
+ }
+ }
+
uint16 emoteid = npc->GetEmoteID();
if (emoteid != 0)
npc->DoNPCEmote(ONSPAWN, emoteid);
diff --git a/zone/event_codes.h b/zone/event_codes.h
index 7850b0eb3..1b0e18505 100644
--- a/zone/event_codes.h
+++ b/zone/event_codes.h
@@ -83,7 +83,8 @@ typedef enum {
EVENT_DEATH_COMPLETE,
EVENT_UNHANDLED_OPCODE,
EVENT_TICK,
-
+ EVENT_SPAWN_ZONE,
+ EVENT_DEATH_ZONE,
_LargestEventID
} QuestEventID;
diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp
index bed0fd7bc..a42efd592 100644
--- a/zone/lua_general.cpp
+++ b/zone/lua_general.cpp
@@ -808,6 +808,14 @@ void lua_update_instance_timer(uint16 instance_id, uint32 new_duration) {
quest_manager.UpdateInstanceTimer(instance_id, new_duration);
}
+uint32 lua_get_instance_timer() {
+ return quest_manager.GetInstanceTimer();
+}
+
+uint32 lua_get_instance_timer_by_id(uint16 instance_id) {
+ return quest_manager.GetInstanceTimerByID(instance_id);
+}
+
int lua_get_instance_id(const char *zone, uint32 version) {
return quest_manager.GetInstanceID(zone, version);
}
@@ -1296,6 +1304,10 @@ void lua_debug(std::string message, int level) {
Log.Out(static_cast(level), Logs::QuestDebug, message);
}
+void lua_update_zone_header(std::string type, std::string value) {
+ quest_manager.UpdateZoneHeader(type, value);
+}
+
#define LuaCreateNPCParse(name, c_type, default_value) do { \
cur = table[#name]; \
if(luabind::type(cur) != LUA_TNIL) { \
@@ -1719,7 +1731,9 @@ luabind::scope lua_register_events() {
luabind::value("leave_area", static_cast(EVENT_LEAVE_AREA)),
luabind::value("death_complete", static_cast(EVENT_DEATH_COMPLETE)),
luabind::value("unhandled_opcode", static_cast(EVENT_UNHANDLED_OPCODE)),
- luabind::value("tick", static_cast(EVENT_TICK))
+ luabind::value("tick", static_cast(EVENT_TICK)),
+ luabind::value("spawn_zone", static_cast(EVENT_SPAWN_ZONE)),
+ luabind::value("death_zone", static_cast(EVENT_DEATH_ZONE))
];
}
diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp
index 3963ec64c..0d45c6c36 100644
--- a/zone/lua_mob.cpp
+++ b/zone/lua_mob.cpp
@@ -1881,6 +1881,106 @@ bool Lua_Mob::IsBlind() {
return self->IsBlind();
}
+uint8 Lua_Mob::SeeInvisible() {
+ Lua_Safe_Call_Int();
+ return self->SeeInvisible();
+}
+
+bool Lua_Mob::SeeInvisibleUndead() {
+ Lua_Safe_Call_Bool();
+ return self->SeeInvisibleUndead();
+}
+
+bool Lua_Mob::SeeHide() {
+ Lua_Safe_Call_Bool();
+ return self->SeeHide();
+}
+
+bool Lua_Mob::SeeImprovedHide() {
+ Lua_Safe_Call_Bool();
+ return self->SeeImprovedHide();
+}
+
+uint8 Lua_Mob::GetNimbusEffect1() {
+ Lua_Safe_Call_Int();
+ return self->GetNimbusEffect1();
+}
+
+uint8 Lua_Mob::GetNimbusEffect2() {
+ Lua_Safe_Call_Int();
+ return self->GetNimbusEffect2();
+}
+
+uint8 Lua_Mob::GetNimbusEffect3() {
+ Lua_Safe_Call_Int();
+ return self->GetNimbusEffect3();
+}
+
+bool Lua_Mob::IsTargetable() {
+ Lua_Safe_Call_Bool();
+ return self->IsTargetable();
+}
+
+bool Lua_Mob::HasShieldEquiped() {
+ Lua_Safe_Call_Bool();
+ return self->HasShieldEquiped();
+}
+
+bool Lua_Mob::HasTwoHandBluntEquiped() {
+ Lua_Safe_Call_Bool();
+ return self->HasTwoHandBluntEquiped();
+}
+
+bool Lua_Mob::HasTwoHanderEquipped() {
+ Lua_Safe_Call_Bool();
+ return self->HasTwoHanderEquipped();
+}
+
+uint32 Lua_Mob::GetHerosForgeModel(uint8 material_slot) {
+ Lua_Safe_Call_Int();
+ return self->GetHerosForgeModel(material_slot);
+}
+
+uint32 Lua_Mob::IsEliteMaterialItem(uint8 material_slot) {
+ Lua_Safe_Call_Int();
+ return self->IsEliteMaterialItem(material_slot);
+}
+
+float Lua_Mob::GetBaseSize() {
+ Lua_Safe_Call_Real();
+ return self->GetBaseSize();
+}
+
+bool Lua_Mob::HasOwner() {
+ Lua_Safe_Call_Bool();
+ return self->HasOwner();
+}
+
+bool Lua_Mob::IsPet() {
+ Lua_Safe_Call_Bool();
+ return self->IsPet();
+}
+
+bool Lua_Mob::HasPet() {
+ Lua_Safe_Call_Bool();
+ return self->HasPet();
+}
+
+bool Lua_Mob::IsSilenced() {
+ Lua_Safe_Call_Bool();
+ return self->IsSilenced();
+}
+
+bool Lua_Mob::IsAmnesiad() {
+ Lua_Safe_Call_Bool();
+ return self->IsAmnesiad();
+}
+
+int32 Lua_Mob::GetMeleeMitigation() {
+ Lua_Safe_Call_Int();
+ return self->GetMeleeMitigation();
+}
+
luabind::scope lua_register_mob() {
return luabind::class_("Mob")
.def(luabind::constructor<>())
@@ -2203,7 +2303,27 @@ luabind::scope lua_register_mob() {
.def("BuffFadeBySlot", (void(Lua_Mob::*)(int,bool))&Lua_Mob::BuffFadeBySlot)
.def("CanBuffStack", (int(Lua_Mob::*)(int,int))&Lua_Mob::CanBuffStack)
.def("CanBuffStack", (int(Lua_Mob::*)(int,int,bool))&Lua_Mob::CanBuffStack)
- .def("SetPseudoRoot", (void(Lua_Mob::*)(bool))&Lua_Mob::SetPseudoRoot);
+ .def("SetPseudoRoot", (void(Lua_Mob::*)(bool))&Lua_Mob::SetPseudoRoot)
+ .def("SeeInvisible", (uint8(Lua_Mob::*)(void))&Lua_Mob::SeeInvisible)
+ .def("SeeInvisibleUndead", (bool(Lua_Mob::*)(void))&Lua_Mob::SeeInvisibleUndead)
+ .def("SeeHide", (bool(Lua_Mob::*)(void))&Lua_Mob::SeeHide)
+ .def("SeeImprovedHide", (bool(Lua_Mob::*)(bool))&Lua_Mob::SeeImprovedHide)
+ .def("GetNimbusEffect1", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetNimbusEffect1)
+ .def("GetNimbusEffect2", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetNimbusEffect2)
+ .def("GetNimbusEffect3", (uint8(Lua_Mob::*)(void))&Lua_Mob::GetNimbusEffect3)
+ .def("IsTargetable", (bool(Lua_Mob::*)(void))&Lua_Mob::IsTargetable)
+ .def("HasShieldEquiped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasShieldEquiped)
+ .def("HasTwoHandBluntEquiped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasTwoHandBluntEquiped)
+ .def("HasTwoHanderEquipped", (bool(Lua_Mob::*)(void))&Lua_Mob::HasTwoHanderEquipped)
+ .def("GetHerosForgeModel", (int32(Lua_Mob::*)(uint8))&Lua_Mob::GetHerosForgeModel)
+ .def("IsEliteMaterialItem", (uint32(Lua_Mob::*)(uint8))&Lua_Mob::IsEliteMaterialItem)
+ .def("GetBaseSize", (double(Lua_Mob::*)(void))&Lua_Mob::GetBaseSize)
+ .def("HasOwner", (bool(Lua_Mob::*)(void))&Lua_Mob::HasOwner)
+ .def("IsPet", (bool(Lua_Mob::*)(void))&Lua_Mob::IsPet)
+ .def("HasPet", (bool(Lua_Mob::*)(void))&Lua_Mob::HasPet)
+ .def("IsSilenced", (bool(Lua_Mob::*)(void))&Lua_Mob::IsSilenced)
+ .def("IsAmnesiad", (bool(Lua_Mob::*)(void))&Lua_Mob::IsAmnesiad)
+ .def("GetMeleeMitigation", (int32(Lua_Mob::*)(void))&Lua_Mob::GetMeleeMitigation);
}
luabind::scope lua_register_special_abilities() {
diff --git a/zone/lua_mob.h b/zone/lua_mob.h
index 3b98b4af3..4009ae2fa 100644
--- a/zone/lua_mob.h
+++ b/zone/lua_mob.h
@@ -358,6 +358,26 @@ public:
int CanBuffStack(int spell_id, int caster_level);
int CanBuffStack(int spell_id, int caster_level, bool fail_if_overwrite);
void SetPseudoRoot(bool in);
+ uint8 SeeInvisible();
+ bool SeeInvisibleUndead();
+ bool SeeHide();
+ bool SeeImprovedHide();
+ uint8 GetNimbusEffect1();
+ uint8 GetNimbusEffect2();
+ uint8 GetNimbusEffect3();
+ bool IsTargetable();
+ bool HasShieldEquiped();
+ bool HasTwoHandBluntEquiped();
+ bool HasTwoHanderEquipped();
+ uint32 GetHerosForgeModel(uint8 material_slot);
+ uint32 IsEliteMaterialItem(uint8 material_slot);
+ float GetBaseSize();
+ bool HasOwner();
+ bool IsPet();
+ bool HasPet();
+ bool IsSilenced();
+ bool IsAmnesiad();
+ int32 GetMeleeMitigation();
};
#endif
diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp
index 462752705..aeb1e3228 100644
--- a/zone/lua_parser.cpp
+++ b/zone/lua_parser.cpp
@@ -117,7 +117,9 @@ const char *LuaEvents[_LargestEventID] = {
"event_respawn",
"event_death_complete",
"event_unhandled_opcode",
- "event_tick"
+ "event_tick",
+ "event_spawn_zone",
+ "event_death_zone"
};
extern Zone *zone;
diff --git a/zone/mob.cpp b/zone/mob.cpp
index eccf04a76..e28ccefcc 100644
--- a/zone/mob.cpp
+++ b/zone/mob.cpp
@@ -433,6 +433,9 @@ Mob::Mob(const char* in_name,
emoteid = 0;
endur_upkeep = false;
+ PrimaryAggro = false;
+ AssistAggro = false;
+ npc_assist_cap = 0;
}
Mob::~Mob()
@@ -2693,6 +2696,8 @@ bool Mob::RemoveFromHateList(Mob* mob)
{
AI_Event_NoLongerEngaged();
zone->DelAggroMob();
+ if (IsNPC() && !RuleB(Aggro, AllowTickPulling))
+ ResetAssistCap();
}
}
if(GetTarget() == mob)
@@ -5677,3 +5682,11 @@ void Mob::SetCurrentSpeed(int in){
}
}
}
+
+int32 Mob::GetMeleeMitigation() {
+ int32 mitigation = 0;
+ mitigation += spellbonuses.MeleeMitigationEffect;
+ mitigation += itembonuses.MeleeMitigationEffect;
+ mitigation += aabonuses.MeleeMitigationEffect;
+ return mitigation;
+}
\ No newline at end of file
diff --git a/zone/mob.h b/zone/mob.h
index 2ca027023..8fc7e7f08 100644
--- a/zone/mob.h
+++ b/zone/mob.h
@@ -504,6 +504,10 @@ public:
Mob* GetHateRandom() { return hate_list.GetRandomEntOnHateList();}
Mob* GetHateMost() { return hate_list.GetEntWithMostHateOnList();}
bool IsEngaged() { return(!hate_list.IsHateListEmpty()); }
+ bool HasPrimaryAggro() { return PrimaryAggro; }
+ bool HasAssistAggro() { return AssistAggro; }
+ void SetPrimaryAggro(bool value) { PrimaryAggro = value; if (value) AssistAggro = false; }
+ void SetAssistAggro(bool value) { AssistAggro = value; if (PrimaryAggro) AssistAggro = false; }
bool HateSummon();
void FaceTarget(Mob* MobToFace = 0);
void SetHeading(float iHeading) { if(m_Position.w != iHeading) { pLastChange = Timer::GetCurrentTime();
@@ -748,6 +752,7 @@ public:
inline bool GetInvul(void) { return invulnerable; }
inline void SetExtraHaste(int Haste) { ExtraHaste = Haste; }
virtual int GetHaste();
+ int32 GetMeleeMitigation();
uint8 GetWeaponDamageBonus(const Item_Struct* weapon, bool offhand = false);
uint16 GetDamageTable(SkillUseTypes skillinuse);
@@ -993,6 +998,11 @@ public:
void ApplyAABonuses(const AA::Rank &rank, StatBonuses* newbon);
bool CheckAATimer(int timer);
+ int NPCAssistCap() { return npc_assist_cap; }
+ void AddAssistCap() { ++npc_assist_cap; }
+ void DelAssistCap() { --npc_assist_cap; }
+ void ResetAssistCap() { npc_assist_cap = 0; }
+
protected:
void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, int special = 0);
static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
@@ -1293,6 +1303,11 @@ protected:
glm::vec4 m_CurrentWayPoint;
int cur_wp_pause;
+ bool PrimaryAggro;
+ bool AssistAggro;
+ int npc_assist_cap;
+ Timer assist_cap_timer; // clear assist cap so more nearby mobs can be called for help
+
int patrol;
glm::vec3 m_FearWalkTarget;
diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp
index 25ed72441..79c4493ea 100644
--- a/zone/mob_ai.cpp
+++ b/zone/mob_ai.cpp
@@ -1735,8 +1735,10 @@ void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) {
if (iYellForHelp) {
if(IsPet()) {
GetOwner()->AI_Event_Engaged(attacker, iYellForHelp);
- } else {
+ } else if (!HasAssistAggro() && NPCAssistCap() < RuleI(Combat, NPCAssistCap)) {
entity_list.AIYellForHelp(this, attacker);
+ if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled())
+ assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer));
}
}
@@ -1787,6 +1789,8 @@ void Mob::AI_Event_NoLongerEngaged() {
if(IsNPC())
{
+ SetPrimaryAggro(false);
+ SetAssistAggro(false);
if(CastToNPC()->GetCombatEvent() && GetHP() > 0)
{
if(entity_list.GetNPCByID(this->GetID()))
diff --git a/zone/npc.cpp b/zone/npc.cpp
index ebdd8a79d..8f09d3f40 100644
--- a/zone/npc.cpp
+++ b/zone/npc.cpp
@@ -712,8 +712,18 @@ bool NPC::Process()
}
//Handle assists...
- if(assist_timer.Check() && IsEngaged() && !Charmed()) {
+ if (assist_cap_timer.Check()) {
+ if (NPCAssistCap() > 0)
+ DelAssistCap();
+ else
+ assist_cap_timer.Disable();
+ }
+
+ if (assist_timer.Check() && IsEngaged() && !Charmed() && !HasAssistAggro() &&
+ NPCAssistCap() < RuleI(Combat, NPCAssistCap)) {
entity_list.AIYellForHelp(this, GetTarget());
+ if (NPCAssistCap() > 0 && !assist_cap_timer.Enabled())
+ assist_cap_timer.Start(RuleI(Combat, NPCAssistCapTimer));
}
if(qGlobals)
@@ -833,6 +843,53 @@ bool NPC::DatabaseCastAccepted(int spell_id) {
return false;
}
+bool NPC::SpawnZoneController(){
+
+ if (!RuleB(Zone, UseZoneController))
+ return false;
+
+ NPCType* npc_type = new NPCType;
+ memset(npc_type, 0, sizeof(NPCType));
+
+ strncpy(npc_type->name, "zone_controller", 60);
+ npc_type->cur_hp = 2000000000;
+ npc_type->max_hp = 2000000000;
+ npc_type->hp_regen = 100000000;
+ npc_type->race = 240;
+ npc_type->gender = 2;
+ npc_type->class_ = 1;
+ npc_type->deity = 1;
+ npc_type->level = 200;
+ npc_type->npc_id = ZONE_CONTROLLER_NPC_ID;
+ npc_type->loottable_id = 0;
+ npc_type->texture = 3;
+ npc_type->runspeed = 0;
+ npc_type->d_melee_texture1 = 0;
+ npc_type->d_melee_texture2 = 0;
+ npc_type->merchanttype = 0;
+ npc_type->bodytype = 11;
+
+ npc_type->prim_melee_type = 28;
+ npc_type->sec_melee_type = 28;
+
+ npc_type->findable = 0;
+ npc_type->trackable = 0;
+
+ strcpy(npc_type->special_abilities, "12,1^13,1^14,1^15,1^16,1^17,1^19,1^22,1^24,1^25,1^28,1^31,1^35,1^39,1^42,1");
+
+ glm::vec4 point;
+ point.x = 5000;
+ point.y = 5000;
+ point.z = -5000;
+
+ NPC* npc = new NPC(npc_type, nullptr, point, FlyMode3);
+ npc->GiveNPCTypeData(npc_type);
+
+ entity_list.AddNPC(npc);
+
+ return true;
+}
+
NPC* NPC::SpawnNPC(const char* spawncommand, const glm::vec4& position, Client* client) {
if(spawncommand == 0 || spawncommand[0] == 0) {
return 0;
diff --git a/zone/npc.h b/zone/npc.h
index 32afd0afd..327918c19 100644
--- a/zone/npc.h
+++ b/zone/npc.h
@@ -96,6 +96,7 @@ class NPC : public Mob
{
public:
static NPC* SpawnNPC(const char* spawncommand, const glm::vec4& position, Client* client = nullptr);
+ static bool SpawnZoneController();
static int8 GetAILevel(bool iForceReRead = false);
NPC(const NPCType* data, Spawn2* respawn, const glm::vec4& position, int iflymode, bool IsCorpse = false);
diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp
index adbdd0301..40077c2c5 100644
--- a/zone/perl_mob.cpp
+++ b/zone/perl_mob.cpp
@@ -8490,6 +8490,524 @@ XS(XS_Mob_IsBlind) {
XSRETURN(1);
}
+XS(XS_Mob_SeeInvisible);
+XS(XS_Mob_SeeInvisible) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::SeeInvisible(THIS)");
+ {
+ Mob* THIS;
+ uint8 RETVAL;
+ dXSTARG;
+
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->SeeInvisible();
+ XSprePUSH;
+ PUSHu((UV)RETVAL);
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_SeeInvisibleUndead);
+XS(XS_Mob_SeeInvisibleUndead) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::SeeInvisibleUndead(THIS)");
+ {
+ Mob* THIS;
+ bool RETVAL;
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->SeeInvisibleUndead();
+ ST(0) = boolSV(RETVAL);
+ sv_2mortal(ST(0));
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_SeeHide);
+XS(XS_Mob_SeeHide) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::SeeHide(THIS)");
+ {
+ Mob* THIS;
+ bool RETVAL;
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->SeeHide();
+ ST(0) = boolSV(RETVAL);
+ sv_2mortal(ST(0));
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_SeeImprovedHide);
+XS(XS_Mob_SeeImprovedHide) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::SeeImprovedHide(THIS)");
+ {
+ Mob* THIS;
+ bool RETVAL;
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->SeeImprovedHide();
+ ST(0) = boolSV(RETVAL);
+ sv_2mortal(ST(0));
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_GetNimbusEffect1);
+XS(XS_Mob_GetNimbusEffect1) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::GetNimbusEffect1(THIS)");
+ {
+ Mob* THIS;
+ uint8 RETVAL;
+ dXSTARG;
+
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->GetNimbusEffect1();
+ XSprePUSH;
+ PUSHu((UV)RETVAL);
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_GetNimbusEffect2);
+XS(XS_Mob_GetNimbusEffect2) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::GetNimbusEffect2(THIS)");
+ {
+ Mob* THIS;
+ uint8 RETVAL;
+ dXSTARG;
+
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->GetNimbusEffect2();
+ XSprePUSH;
+ PUSHu((UV)RETVAL);
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_GetNimbusEffect3);
+XS(XS_Mob_GetNimbusEffect3) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::GetNimbusEffect3(THIS)");
+ {
+ Mob* THIS;
+ uint8 RETVAL;
+ dXSTARG;
+
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->GetNimbusEffect3();
+ XSprePUSH;
+ PUSHu((UV)RETVAL);
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_IsTargetable);
+XS(XS_Mob_IsTargetable) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::IsTargetable(THIS)");
+ {
+ Mob* THIS;
+ bool RETVAL;
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->IsTargetable();
+ ST(0) = boolSV(RETVAL);
+ sv_2mortal(ST(0));
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_HasShieldEquiped);
+XS(XS_Mob_HasShieldEquiped) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::HasShieldEquiped(THIS)");
+ {
+ Mob* THIS;
+ bool RETVAL;
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->HasShieldEquiped();
+ ST(0) = boolSV(RETVAL);
+ sv_2mortal(ST(0));
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_HasTwoHandBluntEquiped);
+XS(XS_Mob_HasTwoHandBluntEquiped) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::HasTwoHandBluntEquiped(THIS)");
+ {
+ Mob* THIS;
+ bool RETVAL;
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->HasTwoHandBluntEquiped();
+ ST(0) = boolSV(RETVAL);
+ sv_2mortal(ST(0));
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_HasTwoHanderEquipped);
+XS(XS_Mob_HasTwoHanderEquipped) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::HasTwoHanderEquipped(THIS)");
+ {
+ Mob* THIS;
+ bool RETVAL;
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->HasTwoHanderEquipped();
+ ST(0) = boolSV(RETVAL);
+ sv_2mortal(ST(0));
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_GetHerosForgeModel);
+XS(XS_Mob_GetHerosForgeModel) {
+ dXSARGS;
+ if (items != 2)
+ Perl_croak(aTHX_ "Usage: Mob::GetHerosForgeModel(THIS, material_slot)");
+ {
+ Mob* THIS;
+ int32 RETVAL;
+ uint8 material_slot = (uint8)SvUV(ST(1));
+ dXSTARG;
+
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->GetHerosForgeModel(material_slot);
+ XSprePUSH;
+ PUSHi((IV)RETVAL);
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_IsEliteMaterialItem);
+XS(XS_Mob_IsEliteMaterialItem) {
+ dXSARGS;
+ if (items != 2)
+ Perl_croak(aTHX_ "Usage: Mob::IsEliteMaterialItem(THIS, material_slot)");
+ {
+ Mob* THIS;
+ uint32 RETVAL;
+ uint8 material_slot = (uint8)SvUV(ST(1));
+ dXSTARG;
+
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->IsEliteMaterialItem(material_slot);
+ XSprePUSH;
+ PUSHu((UV)RETVAL);
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_GetBaseSize);
+XS(XS_Mob_GetBaseSize) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::GetBaseSize(THIS)");
+ {
+ Mob* THIS;
+ float RETVAL;
+ dXSTARG;
+
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->GetBaseSize();
+ XSprePUSH;
+ PUSHn((double)RETVAL);
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_HasOwner);
+XS(XS_Mob_HasOwner) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::HasOwner(THIS)");
+ {
+ Mob* THIS;
+ bool RETVAL;
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->HasOwner();
+ ST(0) = boolSV(RETVAL);
+ sv_2mortal(ST(0));
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_IsPet);
+XS(XS_Mob_IsPet) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::IsPet(THIS)");
+ {
+ Mob* THIS;
+ bool RETVAL;
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->IsPet();
+ ST(0) = boolSV(RETVAL);
+ sv_2mortal(ST(0));
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_HasPet);
+XS(XS_Mob_HasPet) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::HasPet(THIS)");
+ {
+ Mob* THIS;
+ bool RETVAL;
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->HasPet();
+ ST(0) = boolSV(RETVAL);
+ sv_2mortal(ST(0));
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_IsSilenced);
+XS(XS_Mob_IsSilenced) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::IsSilenced(THIS)");
+ {
+ Mob* THIS;
+ bool RETVAL;
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->IsSilenced();
+ ST(0) = boolSV(RETVAL);
+ sv_2mortal(ST(0));
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_IsAmnesiad);
+XS(XS_Mob_IsAmnesiad) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::IsAmnesiad(THIS)");
+ {
+ Mob* THIS;
+ bool RETVAL;
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->IsAmnesiad();
+ ST(0) = boolSV(RETVAL);
+ sv_2mortal(ST(0));
+ }
+ XSRETURN(1);
+}
+
+XS(XS_Mob_GetMeleeMitigation);
+XS(XS_Mob_GetMeleeMitigation) {
+ dXSARGS;
+ if (items != 1)
+ Perl_croak(aTHX_ "Usage: Mob::GetMeleeMitigation(THIS)");
+ {
+ Mob* THIS;
+ int32 RETVAL;
+ dXSTARG;
+
+ if (sv_derived_from(ST(0), "Mob")) {
+ IV tmp = SvIV((SV*)SvRV(ST(0)));
+ THIS = INT2PTR(Mob*, tmp);
+ }
+ else
+ Perl_croak(aTHX_ "THIS is not of type Mob");
+
+ if (THIS == nullptr)
+ Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
+
+ RETVAL = THIS->GetMeleeMitigation();
+ XSprePUSH;
+ PUSHi((IV)RETVAL);
+ }
+ XSRETURN(1);
+}
+
#ifdef __cplusplus
extern "C"
#endif
@@ -8803,6 +9321,26 @@ XS(boot_Mob)
newXSproto(strcpy(buf, "CanClassEquipItem"), XS_Mob_CanClassEquipItem, file, "$$");
newXSproto(strcpy(buf, "IsFeared"), XS_Mob_IsFeared, file, "$");
newXSproto(strcpy(buf, "IsBlind"), XS_Mob_IsBlind, file, "$");
+ newXSproto(strcpy(buf, "SeeInvisible"), XS_Mob_SeeInvisible, file, "$");
+ newXSproto(strcpy(buf, "SeeInvisibleUndead"), XS_Mob_SeeInvisibleUndead, file, "$");
+ newXSproto(strcpy(buf, "SeeHide"), XS_Mob_SeeHide, file, "$");
+ newXSproto(strcpy(buf, "SeeImprovedHide"), XS_Mob_SeeImprovedHide, file, "$");
+ newXSproto(strcpy(buf, "GetNimbusEffect1"), XS_Mob_GetNimbusEffect1, file, "$");
+ newXSproto(strcpy(buf, "GetNimbusEffect2"), XS_Mob_GetNimbusEffect2, file, "$");
+ newXSproto(strcpy(buf, "GetNimbusEffect3"), XS_Mob_GetNimbusEffect3, file, "$");
+ newXSproto(strcpy(buf, "IsTargetable"), XS_Mob_IsTargetable, file, "$");
+ newXSproto(strcpy(buf, "HasShieldEquiped"), XS_Mob_HasShieldEquiped, file, "$");
+ newXSproto(strcpy(buf, "HasTwoHandBluntEquiped"), XS_Mob_HasTwoHandBluntEquiped, file, "$");
+ newXSproto(strcpy(buf, "HasTwoHanderEquipped"), XS_Mob_HasTwoHanderEquipped, file, "$");
+ newXSproto(strcpy(buf, "GetHerosForgeModel"), XS_Mob_GetHerosForgeModel, file, "$$");
+ newXSproto(strcpy(buf, "IsEliteMaterialItem"), XS_Mob_IsEliteMaterialItem, file, "$$");
+ newXSproto(strcpy(buf, "GetBaseSize"), XS_Mob_GetBaseSize, file, "$");
+ newXSproto(strcpy(buf, "HasOwner"), XS_Mob_HasOwner, file, "$");
+ newXSproto(strcpy(buf, "IsPet"), XS_Mob_IsPet, file, "$");
+ newXSproto(strcpy(buf, "HasPet"), XS_Mob_HasPet, file, "$");
+ newXSproto(strcpy(buf, "IsSilenced"), XS_Mob_IsSilenced, file, "$");
+ newXSproto(strcpy(buf, "IsAmnesiad"), XS_Mob_IsAmnesiad, file, "$");
+ newXSproto(strcpy(buf, "GetMeleeMitigation"), XS_Mob_GetMeleeMitigation, file, "$");
XSRETURN_YES;
}
diff --git a/zone/quest_parser_collection.cpp b/zone/quest_parser_collection.cpp
index 1f4f99c0e..4d2a2960a 100644
--- a/zone/quest_parser_collection.cpp
+++ b/zone/quest_parser_collection.cpp
@@ -484,10 +484,17 @@ QuestInterface *QuestParserCollection::GetQIByNPCQuest(uint32 npcid, std::string
//second look for /quests/zone/npcname.ext (precedence)
const NPCType *npc_type = database.LoadNPCTypesData(npcid);
- if(!npc_type) {
+ if (!npc_type && npcid != ZONE_CONTROLLER_NPC_ID) {
return nullptr;
}
- std::string npc_name = npc_type->name;
+
+ std::string npc_name;
+ if (npcid == ZONE_CONTROLLER_NPC_ID){
+ npc_name = "zone_controller";
+ }
+ else{
+ npc_name = npc_type->name;
+ }
int sz = static_cast(npc_name.length());
for(int i = 0; i < sz; ++i) {
if(npc_name[i] == '`') {
diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp
index cb70f93d3..86b8cb7d9 100644
--- a/zone/questmgr.cpp
+++ b/zone/questmgr.cpp
@@ -2598,6 +2598,29 @@ void QuestManager::UpdateInstanceTimer(uint16 instance_id, uint32 new_duration)
}
}
+uint32 QuestManager::GetInstanceTimer() {
+ if (zone && zone->GetInstanceID() > 0 && zone->GetInstanceTimer()) {
+ uint32 ttime = zone->GetInstanceTimer()->GetRemainingTime();
+ return ttime;
+ }
+ return 0;
+}
+
+uint32 QuestManager::GetInstanceTimerByID(uint16 instance_id) {
+ if (instance_id == 0)
+ return 0;
+
+ std::string query = StringFormat("SELECT ((start_time + duration) - UNIX_TIMESTAMP()) AS `remaining` FROM `instance_list` WHERE `id` = %lu", (unsigned long)instance_id);
+ auto results = database.QueryDatabase(query);
+
+ if (results.Success()) {
+ auto row = results.begin();
+ uint32 timer = atoi(row[0]);
+ return timer;
+ }
+ return 0;
+}
+
uint16 QuestManager::GetInstanceID(const char *zone, int16 version)
{
QuestManagerCurrentQuestVars();
@@ -3070,3 +3093,75 @@ std::string QuestManager::GetEncounter() const {
return "";
}
+
+void QuestManager::UpdateZoneHeader(std::string type, std::string value) {
+ if (strcasecmp(type.c_str(), "ztype") == 0)
+ zone->newzone_data.ztype = atoi(value.c_str());
+ else if (strcasecmp(type.c_str(), "fog_red") == 0) {
+ for (int i = 0; i < 4; i++) {
+ zone->newzone_data.fog_red[i] = atoi(value.c_str());
+ }
+ } else if (strcasecmp(type.c_str(), "fog_green") == 0) {
+ for (int i = 0; i < 4; i++) {
+ zone->newzone_data.fog_green[i] = atoi(value.c_str());
+ }
+ } else if (strcasecmp(type.c_str(), "fog_blue") == 0) {
+ for (int i = 0; i < 4; i++) {
+ zone->newzone_data.fog_blue[i] = atoi(value.c_str());
+ }
+ } else if (strcasecmp(type.c_str(), "fog_minclip") == 0) {
+ for (int i = 0; i < 4; i++) {
+ zone->newzone_data.fog_minclip[i] = atof(value.c_str());
+ }
+ } else if (strcasecmp(type.c_str(), "fog_maxclip") == 0) {
+ for (int i = 0; i < 4; i++) {
+ zone->newzone_data.fog_maxclip[i] = atof(value.c_str());
+ }
+ }
+ else if (strcasecmp(type.c_str(), "gravity") == 0)
+ zone->newzone_data.gravity = atof(value.c_str());
+ else if (strcasecmp(type.c_str(), "time_type") == 0)
+ zone->newzone_data.time_type = atoi(value.c_str());
+ else if (strcasecmp(type.c_str(), "rain_chance") == 0) {
+ for (int i = 0; i < 4; i++) {
+ zone->newzone_data.rain_chance[i] = atoi(value.c_str());
+ }
+ } else if (strcasecmp(type.c_str(), "rain_duration") == 0) {
+ for (int i = 0; i < 4; i++) {
+ zone->newzone_data.rain_duration[i] = atoi(value.c_str());
+ }
+ } else if (strcasecmp(type.c_str(), "snow_chance") == 0) {
+ for (int i = 0; i < 4; i++) {
+ zone->newzone_data.snow_chance[i] = atoi(value.c_str());
+ }
+ } else if (strcasecmp(type.c_str(), "snow_duration") == 0) {
+ for (int i = 0; i < 4; i++) {
+ zone->newzone_data.snow_duration[i] = atoi(value.c_str());
+ }
+ }
+ else if (strcasecmp(type.c_str(), "sky") == 0)
+ zone->newzone_data.sky = atoi(value.c_str());
+ else if (strcasecmp(type.c_str(), "safe_x") == 0)
+ zone->newzone_data.safe_x = atof(value.c_str());
+ else if (strcasecmp(type.c_str(), "safe_y") == 0)
+ zone->newzone_data.safe_y = atof(value.c_str());
+ else if (strcasecmp(type.c_str(), "safe_z") == 0)
+ zone->newzone_data.safe_z = atof(value.c_str());
+ else if (strcasecmp(type.c_str(), "max_z") == 0)
+ zone->newzone_data.max_z = atof(value.c_str());
+ else if (strcasecmp(type.c_str(), "underworld") == 0)
+ zone->newzone_data.underworld = atof(value.c_str());
+ else if (strcasecmp(type.c_str(), "minclip") == 0)
+ zone->newzone_data.minclip = atof(value.c_str());
+ else if (strcasecmp(type.c_str(), "maxclip") == 0)
+ zone->newzone_data.maxclip = atof(value.c_str());
+ else if (strcasecmp(type.c_str(), "fog_density") == 0)
+ zone->newzone_data.fog_density = atof(value.c_str());
+ else if (strcasecmp(type.c_str(), "suspendbuffs") == 0)
+ zone->newzone_data.SuspendBuffs = atoi(value.c_str());
+
+ EQApplicationPacket* outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct));
+ memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size);
+ entity_list.QueueClients(0, outapp);
+ safe_delete(outapp);
+}
\ No newline at end of file
diff --git a/zone/questmgr.h b/zone/questmgr.h
index cd1c4a303..a4cc2a38b 100644
--- a/zone/questmgr.h
+++ b/zone/questmgr.h
@@ -218,6 +218,9 @@ public:
uint32 MerchantCountItem(uint32 NPCid, uint32 itemid);
uint16 CreateInstance(const char *zone, int16 version, uint32 duration);
void UpdateInstanceTimer(uint16 instance_id, uint32 new_duration);
+ void UpdateZoneHeader(std::string type, std::string value);
+ uint32 GetInstanceTimer();
+ uint32 GetInstanceTimerByID(uint16 instance_id = 0);
void DestroyInstance(uint16 instance_id);
uint16 GetInstanceID(const char *zone, int16 version);
void AssignToInstance(uint16 instance_id);
diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp
index f2461ed4b..7cb3b1f3c 100644
--- a/zone/spawn2.cpp
+++ b/zone/spawn2.cpp
@@ -549,6 +549,8 @@ bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList &spa
spawn2_list.Insert(new_spawn);
}
+ NPC::SpawnZoneController();
+
return true;
}
diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp
index 903c7c34c..2efed1e93 100644
--- a/zone/tradeskills.cpp
+++ b/zone/tradeskills.cpp
@@ -166,25 +166,34 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme
else
{
ItemInst *old_aug = nullptr;
- const uint32 id = auged_with->GetID();
+ bool isSolvent = auged_with->GetItem()->ItemType == ItemUseTypes::ItemTypeAugmentationSolvent;
+ if (!isSolvent && auged_with->GetItem()->ItemType != ItemUseTypes::ItemTypeAugmentationDistiller)
+ {
+ Log.Out(Logs::General, Logs::Error, "Player tried to remove an augment without a solvent or distiller.");
+ user->Message(13, "Error: Missing an augmentation solvent or distiller for removing this augment.");
+
+ return;
+ }
+
ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_slot);
- if(aug) {
+ if (aug) {
+ if (!isSolvent && auged_with->GetItem()->ID != aug->GetItem()->AugDistiller)
+ {
+ Log.Out(Logs::General, Logs::Error, "Player tried to safely remove an augment with the wrong distiller (item %u vs expected %u).", auged_with->GetItem()->ID, aug->GetItem()->AugDistiller);
+ user->Message(13, "Error: Wrong augmentation distiller for safely removing this augment.");
+ return;
+ }
std::vector args;
args.push_back(aug);
parse->EventItem(EVENT_UNAUGMENT_ITEM, user, tobe_auged, nullptr, "", slot, &args);
args.assign(1, tobe_auged);
- bool destroyed = false;
- if(id == 40408 || id == 40409 || id == 40410) {
- destroyed = true;
- }
-
- args.push_back(&destroyed);
+ args.push_back(&isSolvent);
parse->EventItem(EVENT_AUGMENT_REMOVE, user, aug, nullptr, "", slot, &args);
}
- if(id == 40408 || id == 40409 || id == 40410)
+ if (isSolvent)
tobe_auged->DeleteAugment(in_augment->augment_slot);
else
old_aug = tobe_auged->RemoveAugment(in_augment->augment_slot);