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);