Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Paul Coene 2016-01-02 09:17:09 -05:00
commit a56f17a9e5
56 changed files with 2274 additions and 766 deletions

View File

@ -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.

View File

@ -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

View File

@ -1183,22 +1183,17 @@ 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) < 3)
if (!name || strlen(name) < 4)
{
return false;
}
}
else
{
// the minimum 4 is enforced by the client too
if(!name || strlen(name) < 4 || strlen(name) > 15)
// Given name length is enforced by the client too
if (!surname && strlen(name) > 15)
{
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();
}

View File

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

View File

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

View File

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

View File

@ -79,7 +79,6 @@ class EQEmuConfig : public XMLParser
// From <files/>
std::string SpellsFile;
std::string OpCodesFile;
std::string EQTimeFile;
// From <directories/>
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";

View File

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

View File

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

View File

@ -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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4103,7 +4103,7 @@ struct ItemBodyStruct
uint32 Races;
uint32 Deity;
int32 SkillModValue;
uint32 unknown5;
uint32 SkillModMax;
uint32 SkillModType;
uint32 BaneDmgRace;
uint32 BaneDmgBody;

View File

@ -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())

View File

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

View File

@ -1,6 +1,10 @@
#include <iostream>
#include <cstring>
#if defined(_MSC_VER) && _MSC_VER >= 1800
#include <algorithm>
#endif
#include "classes.h"
#include "eq_packet_structs.h"
#include "eqemu_exception.h"
@ -802,31 +806,32 @@ bool SharedDatabase::LoadItems(const std::string &prefix) {
return true;
}
void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_item_id) {
EQEmu::FixedMemoryHashSet<Item_Struct> hash(reinterpret_cast<uint8*>(data), size, items, max_item_id);
void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_item_id)
{
EQEmu::FixedMemoryHashSet<Item_Struct> hash(reinterpret_cast<uint8 *>(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;
}
}
@ -843,13 +848,13 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
return;
}
for(auto row = results.begin(); row != results.end(); ++row) {
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]);
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]);
@ -860,7 +865,7 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
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.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]);
@ -878,12 +883,12 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
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.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]);
@ -927,7 +932,7 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
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]);
@ -953,23 +958,23 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
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.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.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.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]);
@ -982,20 +987,20 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
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.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.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.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]);
@ -1017,10 +1022,13 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
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.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]);
@ -1043,20 +1051,19 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_
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]);
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) {
} catch (std::exception &ex) {
Log.Out(Logs::General, Logs::Error, "Database::LoadItems: %s", ex.what());
break;
}
}
}
const Item_Struct* SharedDatabase::GetItem(uint32 id) {

View File

@ -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

View File

@ -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 <<EO_MENU;
11) [Windows Server Build] :: Download Latest and Stable Server Build (Overwrites existing .exe's, includes .dll's)
12) [Windows Server .dll's] :: Download Pre-Requisite Server .dll's
13) [Windows Server Loginserver Setup] :: Download and install Windows Loginserver
14) [Remove Duplicate Rule Values] :: Looks for redundant rule_values entries and removes them
19) [EQEmu DB Drop Bots Schema] :: Remove Bots schema and return database to normal state
20) [Update the updater] Force update this script (Redownload)
0) Exit
@ -521,6 +556,33 @@ sub opcodes_fetch{
print "\nDone...\n\n";
}
sub remove_duplicate_rule_values{
$ruleset_id = trim(get_mysql_result("SELECT `ruleset_id` FROM `rule_sets` WHERE `name` = 'default'"));
print "Default Ruleset ID: " . $ruleset_id . "\n";
$total_removed = 0;
#::: Store Default values...
$mysql_result = get_mysql_result("SELECT * FROM `rule_values` WHERE `ruleset_id` = " . $ruleset_id);
my @lines = split("\n", $mysql_result);
foreach my $val (@lines){
my @values = split("\t", $val);
$rule_set_values{$values[1]}[0] = $values[2];
}
#::: Compare default values against other rulesets to check for duplicates...
$mysql_result = get_mysql_result("SELECT * FROM `rule_values` WHERE `ruleset_id` != " . $ruleset_id);
my @lines = split("\n", $mysql_result);
foreach my $val (@lines){
my @values = split("\t", $val);
if($values[2] == $rule_set_values{$values[1]}[0]){
print "DUPLICATE : " . $values[1] . " (Ruleset (" . $values[0] . ")) matches default value of : " . $values[2] . ", removing...\n";
get_mysql_result("DELETE FROM `rule_values` WHERE `ruleset_id` = " . $values[0] . " AND `rule_name` = '" . $values[1] . "'");
$total_removed++;
}
}
print "Total duplicate rules removed... " . $total_removed . "\n";
}
sub copy_file{
$l_source_file = $_[0];
$l_dest_file = $_[1];
@ -1316,8 +1378,14 @@ sub run_database_check{
@total_updates = ();
#::: This is where we set checkpoints for where a database might be so we don't check so far back in the manifest...
$revision_check = 1000;
if(get_mysql_result("SHOW TABLES LIKE 'character_data'") ne ""){
$revision_check = 9000;
}
#::: Iterate through Manifest backwards from binary version down to local version...
for($i = $bin_db_ver; $i > 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]);

View File

@ -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(<F>) {
s/\r//g;
if(/<database>/i) { $indb = 1; }
next unless($indb == 1);
if(/<\/database>/i) { $indb = 0; last; }
if(/<host>(.*)<\/host>/i) { $host = $1; }
elsif(/<username>(.*)<\/username>/i) { $user = $1; }
elsif(/<password>(.*)<\/password>/i) { $pass = $1; }
elsif(/<db>(.*)<\/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 = <F>;
close(F);
#::: Chomp this array...
my @newitem_file_lines;
chomp($item_file_lines[0]);
@fields = split("(?<!\\\\)\\|", $item_file_lines[0]);
my $sth = $dbh->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("(?<!\\\\)\\|", $item_file_lines[$_]);
#::: Build our individual prepared statement (?, ?) values in the insert_ph
#::: ?, ? placeholders will be resolved via @field_values in the execute
$query_insert_ph .= " (";
foreach (@f) {
push (@field_values, trim($_));
$query_insert_ph .= "?, ";
}
$query_insert_ph = substr($query_insert_ph, 0, -2);
$query_insert_ph .= "), ";
#::: Let's chunk our updates so we can break up the amount of individual queries
if($query_count > 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;
}

View File

@ -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(<F>) {
s/\r//g;
if(/<database>/i) {
$indb = 1;
}
next unless($indb == 1);
if(/<\/database>/i) {
$indb = 0;
last;
}
if(/<host>(.*)<\/host>/i) {
$host = $1;
} elsif(/<username>(.*)<\/username>/i) {
$user = $1;
} elsif(/<password>(.*)<\/password>/i) {
$pass = $1;
} elsif(/<db>(.*)<\/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";
$_=<STDIN>;
chomp();
s/'/\\'/g;
@fields=split("(?<!\\\\)\\|", $_);
%conversions = (
"itemtype" => "itemuse"
);
$insert="replace into items (".join(",",@fields).",source,updated) values ('";
$insert=~s/UNK130/potionbeltslots/;
$insert=~s/UNK133/stackable/;
#select(STDOUT); $|=1;
while(<STDIN>) {
chomp();
s/'/\\'/g;
@f=split("(?<!\\\\)\\|", $_);
$insert2=join("','",@f);
$#f--;
grep(s/\\\|/\\\\\|/g,@f);
grep(s/"/\\\\"/g,@f);
$statement=sprintf("%s%s','13THFLOOR',now())",$insert,$insert2,join('|',@f));
$dbh->do($statement);
printf("Processing: %d %s \r",$f[4],$f[1]);
++$count;
}
printf("Processed: %d items(s) \n",$count);

View File

@ -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

View File

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

View File

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

View File

@ -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';

View File

@ -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`;

View File

@ -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),

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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();
}
}
}

View File

@ -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<int>(attack_skill));
snprintf(buffer, 47, "%d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast<int>(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<int>(attack_skill));
snprintf(buffer, 47, "%d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast<int>(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<int>(attack_skill));
snprintf(buffer, 47, "%d %d %d %d", killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast<int>(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<int>(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;
}
}

View File

@ -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;
}
}

View File

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

View File

@ -2925,43 +2925,111 @@ 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, *auged_with = nullptr;
int8 slot = -1;
ItemInst *tobe_auged = nullptr, *old_aug = nullptr, *new_aug = nullptr, *aug = nullptr, *solvent = nullptr;
Inventory& user_inv = GetInv();
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)
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)
{
Message(13, "Error: Invalid Aug Index.");
return;
}
tobe_auged = user_inv.GetItem(slot_id);
auged_with = user_inv.GetItem(MainCursor);
tobe_auged = user_inv.GetItem(item_slot);
solvent = user_inv.GetItem(solvent_slot);
if (tobe_auged && auged_with)
if (!tobe_auged)
{
if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) &&
(tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots)))
Message(13, "Error: Invalid item passed for augmenting.");
return;
}
if ((in_augment->augment_action == 1) || (in_augment->augment_action == 2))
{
tobe_auged->PutAugment(in_augment->augment_index, *auged_with);
// Check for valid distiller if safely removing / swapping an augmentation
if (!solvent)
{
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;
}
else if (solvent->GetItem()->ItemType == ItemUseTypes::ItemTypeAugmentationDistiller)
{
old_aug = tobe_auged->GetAugment(in_augment->augment_index);
if (!old_aug)
{
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;
}
}
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
{
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<EQEmu::Any> 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();
ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index);
if (aug) {
aug = tobe_auged->GetAugment(in_augment->augment_index);
if (aug)
{
std::vector<EQEmu::Any> args;
args.push_back(aug);
parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
@ -2971,22 +3039,48 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
}
else
{
Message(13, "Error: Could not find augmentation at index %i. Aborting.", in_augment->augment_index);
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(slot_id, 0, true);
DeleteItemInInventory(MainCursor, 0, true);
DeleteItemInInventory(item_slot, 0, true);
DeleteItemInInventory(MainCursor, new_aug->IsStackable() ? 1 : 0, true);
if (PutItemInInventory(slot_id, *itemOneToPush, 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))
{
CalcBonuses();
// Successfully added an augment to the item
return;
CalcBonuses();
if (mat != _MaterialInvalid)
{
SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed.
}
}
else
{
@ -2997,37 +3091,17 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
{
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)
break;
case 1: // Removing augment safely (distiller)
aug = tobe_auged->GetAugment(in_augment->augment_index);
if (aug)
{
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<EQEmu::Any> args;
args.push_back(aug);
parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
@ -3040,35 +3114,103 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
}
else
{
Message(13, "Error: Could not find augmentation at index %i. Aborting.");
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 && auged_with)
{
DeleteItemInInventory(slot_id, 0, true);
DeleteItemInInventory(aug_slot_id, auged_with->IsStackable() ? 1 : 0, true);
if (!PutItemInInventory(slot_id, *itemOneToPush, true))
if (itemOneToPush && itemTwoToPush)
{
Message(15, "Failed to remove augment properly!");
// 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!");
}
if (PutItemInInventory(MainCursor, *itemTwoToPush, true))
{
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<EQEmu::Any> 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;

View File

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

View File

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

View File

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

View File

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

View File

@ -83,7 +83,8 @@ typedef enum {
EVENT_DEATH_COMPLETE,
EVENT_UNHANDLED_OPCODE,
EVENT_TICK,
EVENT_SPAWN_ZONE,
EVENT_DEATH_ZONE,
_LargestEventID
} QuestEventID;

View File

@ -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<Logs::DebugLevel>(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<int>(EVENT_LEAVE_AREA)),
luabind::value("death_complete", static_cast<int>(EVENT_DEATH_COMPLETE)),
luabind::value("unhandled_opcode", static_cast<int>(EVENT_UNHANDLED_OPCODE)),
luabind::value("tick", static_cast<int>(EVENT_TICK))
luabind::value("tick", static_cast<int>(EVENT_TICK)),
luabind::value("spawn_zone", static_cast<int>(EVENT_SPAWN_ZONE)),
luabind::value("death_zone", static_cast<int>(EVENT_DEATH_ZONE))
];
}

View File

@ -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_<Lua_Mob, Lua_Entity>("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() {

View File

@ -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

View File

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

View File

@ -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;
}

View File

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

View File

@ -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()))

View File

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

View File

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

View File

@ -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;
}

View File

@ -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<int>(npc_name.length());
for(int i = 0; i < sz; ++i) {
if(npc_name[i] == '`') {

View File

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

View File

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

View File

@ -549,6 +549,8 @@ bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList<Spawn2*> &spa
spawn2_list.Insert(new_spawn);
}
NPC::SpawnZoneController();
return true;
}

View File

@ -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<EQEmu::Any> 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);