mirror of
https://github.com/EQEmu/Server.git
synced 2026-02-24 20:52:26 +00:00
Merge branch 'master' into loot
This commit is contained in:
commit
54fae508c5
@ -1,5 +1,12 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 10/01/2014 ==
|
||||
Kayen: Exported to PERL $client->SendColoredText(color, msg)
|
||||
demonstar55: Exported SendColoredText to lua
|
||||
|
||||
== 09/30/2014 ==
|
||||
Uleat: Implemented click-casting from bag slots for clients that natively support it (RoF)
|
||||
|
||||
== 09/28/2014 ==
|
||||
demonstar55: Add support for post June 18, 2014 Hundred Hands Effect spells (they changed the formula and stuff)
|
||||
set Spells:Jun182014HundredHandsRevamp to true if you're using a spell file from June 18, 2014+
|
||||
|
||||
@ -17,7 +17,8 @@ static const uint32 BIT_RoFAndLater = 0xFFFFFFE0;
|
||||
static const uint32 BIT_RoF2AndLater = 0xFFFFFFC0;
|
||||
static const uint32 BIT_AllClients = 0xFFFFFFFF;
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
EQClientUnknown = 0,
|
||||
EQClient62, // Build: 'Aug 4 2005 15:40:59'
|
||||
EQClientTitanium, // Build: 'Oct 31 2005 10:33:37'
|
||||
@ -26,17 +27,50 @@ typedef enum {
|
||||
EQClientUnderfoot, // Build: 'Jun 8 2010 16:44:32'
|
||||
EQClientRoF, // Build: 'Dec 10 2012 17:35:44'
|
||||
EQClientRoF2, // Build: 'May 10 2013 23:30:08'
|
||||
|
||||
|
||||
_EQClientCount, // place new clients before this point (preferably, in release/attribute order)
|
||||
|
||||
|
||||
// Values below are not implemented, as yet...
|
||||
|
||||
|
||||
EmuNPC = _EQClientCount,
|
||||
EmuMerc,
|
||||
EmuBot,
|
||||
EmuPet,
|
||||
|
||||
|
||||
_EmuClientCount // array size for EQLimits
|
||||
} EQClientVersion;
|
||||
|
||||
static const char* EQClientVersionName(EQClientVersion version)
|
||||
{
|
||||
switch (version)
|
||||
{
|
||||
case EQClientUnknown:
|
||||
return "EQClientUnknown";
|
||||
case EQClient62:
|
||||
return "EQClient62";
|
||||
case EQClientTitanium:
|
||||
return "EQClientTitanium";
|
||||
case EQClientSoF:
|
||||
return "EQClientSoF";
|
||||
case EQClientSoD:
|
||||
return "EQClientSoD";
|
||||
case EQClientUnderfoot:
|
||||
return "EQClientUnderfoot";
|
||||
case EQClientRoF:
|
||||
return "EQClientRoF";
|
||||
case EQClientRoF2:
|
||||
return "EQClientRoF2";
|
||||
case EmuNPC:
|
||||
return "EmuNPC";
|
||||
case EmuMerc:
|
||||
return "EmuMerc";
|
||||
case EmuBot:
|
||||
return "EmuBot";
|
||||
case EmuPet:
|
||||
return "EmuPet";
|
||||
default:
|
||||
return "ERROR: Invalid EQClientVersion";
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* CLIENTVERSIONS_H */
|
||||
|
||||
@ -254,6 +254,7 @@ N(OP_ItemLinkText),
|
||||
N(OP_ItemName),
|
||||
N(OP_ItemPacket),
|
||||
N(OP_ItemPreview),
|
||||
N(OP_ItemRecastDelay),
|
||||
N(OP_ItemVerifyReply),
|
||||
N(OP_ItemVerifyRequest),
|
||||
N(OP_ItemViewUnknown),
|
||||
|
||||
@ -1024,6 +1024,26 @@ bool EQLimits::AllowsEmptyBagInBag(uint32 version) {
|
||||
//return local[ValidateMobVersion(version)];
|
||||
}
|
||||
|
||||
bool EQLimits::AllowsClickCastFromBag(uint32 version) {
|
||||
static const bool local[_EmuClientCount] = {
|
||||
/*Unknown*/ false,
|
||||
/*62*/ Client62::limits::ALLOWS_CLICK_CAST_FROM_BAG,
|
||||
/*Titanium*/ Titanium::limits::ALLOWS_CLICK_CAST_FROM_BAG,
|
||||
/*SoF*/ SoF::limits::ALLOWS_CLICK_CAST_FROM_BAG,
|
||||
/*SoD*/ SoD::limits::ALLOWS_CLICK_CAST_FROM_BAG,
|
||||
/*Underfoot*/ Underfoot::limits::ALLOWS_CLICK_CAST_FROM_BAG,
|
||||
/*RoF*/ RoF::limits::ALLOWS_CLICK_CAST_FROM_BAG,
|
||||
/*RoF2*/ false,
|
||||
|
||||
/*NPC*/ false,
|
||||
/*Merc*/ false,
|
||||
/*Bot*/ false,
|
||||
/*Pet*/ false
|
||||
};
|
||||
|
||||
return local[ValidateMobVersion(version)];
|
||||
}
|
||||
|
||||
// items
|
||||
uint16 EQLimits::ItemCommonSize(uint32 version) {
|
||||
static const uint16 local[_EmuClientCount] = {
|
||||
|
||||
@ -184,6 +184,7 @@ public:
|
||||
static uint64 CursorBitmask(uint32 version);
|
||||
|
||||
static bool AllowsEmptyBagInBag(uint32 version);
|
||||
static bool AllowsClickCastFromBag(uint32 version);
|
||||
|
||||
// items
|
||||
static uint16 ItemCommonSize(uint32 version);
|
||||
|
||||
@ -4264,6 +4264,13 @@ struct ItemVerifyReply_Struct {
|
||||
/*012*/
|
||||
};
|
||||
|
||||
struct ItemRecastDelay_Struct {
|
||||
/*000*/ uint32 recast_delay; // in seconds
|
||||
/*004*/ uint32 recast_type;
|
||||
/*008*/ uint32 unknown008;
|
||||
/*012*/
|
||||
};
|
||||
|
||||
/**
|
||||
* Shroud yourself. For yourself shrouding, this has your spawnId, spawnStruct,
|
||||
* bits of your charProfileStruct (no checksum, then charProfile up till
|
||||
|
||||
@ -911,6 +911,30 @@ bool Inventory::CanItemFitInContainer(const Item_Struct *ItemToTry, const Item_S
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Inventory::SupportsClickCasting(int16 slot_id)
|
||||
{
|
||||
// there are a few non-potion items that identify as ItemTypePotion..so, we still need to ubiquitously include the equipment range
|
||||
if ((uint16)slot_id <= EmuConstants::GENERAL_END || slot_id == MainPowerSource)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (slot_id >= EmuConstants::GENERAL_BAGS_BEGIN && slot_id <= EmuConstants::GENERAL_BAGS_END)
|
||||
{
|
||||
if (EQLimits::AllowsClickCastFromBag(m_version))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Inventory::SupportsPotionBeltCasting(int16 slot_id)
|
||||
{
|
||||
if ((uint16)slot_id <= EmuConstants::GENERAL_END || slot_id == MainPowerSource || (slot_id >= EmuConstants::GENERAL_BAGS_BEGIN && slot_id <= EmuConstants::GENERAL_BAGS_END))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test whether a given slot can support a container item
|
||||
bool Inventory::SupportsContainers(int16 slot_id)
|
||||
{
|
||||
|
||||
@ -121,8 +121,22 @@ public:
|
||||
// Public Methods
|
||||
///////////////////////////////
|
||||
|
||||
Inventory() { m_version = EQClientUnknown; m_versionset = false; }
|
||||
~Inventory();
|
||||
|
||||
// Inventory v2 creep
|
||||
bool SetInventoryVersion(EQClientVersion version) {
|
||||
if (!m_versionset) {
|
||||
m_version = version;
|
||||
return (m_versionset = true);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
EQClientVersion GetInventoryVersion() { return m_version; }
|
||||
|
||||
static void CleanDirty();
|
||||
static void MarkDirty(ItemInst *inst);
|
||||
|
||||
@ -132,7 +146,7 @@ public:
|
||||
|
||||
inline iter_queue cursor_begin() { return m_cursor.begin(); }
|
||||
inline iter_queue cursor_end() { return m_cursor.end(); }
|
||||
inline bool CursorEmpty() { return (m_cursor.size() == 0); }
|
||||
inline bool CursorEmpty() { return (m_cursor.size() == 0); }
|
||||
|
||||
// Retrieve a read-only item from inventory
|
||||
inline const ItemInst* operator[](int16 slot_id) const { return GetItem(slot_id); }
|
||||
@ -183,6 +197,10 @@ public:
|
||||
|
||||
static bool CanItemFitInContainer(const Item_Struct *ItemToTry, const Item_Struct *Container);
|
||||
|
||||
// Test for valid inventory casting slot
|
||||
bool SupportsClickCasting(int16 slot_id);
|
||||
bool SupportsPotionBeltCasting(int16 slot_id);
|
||||
|
||||
// Test whether a given slot can support a container item
|
||||
static bool SupportsContainers(int16 slot_id);
|
||||
|
||||
@ -229,7 +247,12 @@ protected:
|
||||
std::map<int16, ItemInst*> m_bank; // Items in character bank
|
||||
std::map<int16, ItemInst*> m_shbank; // Items in character shared bank
|
||||
std::map<int16, ItemInst*> m_trade; // Items in a trade session
|
||||
ItemInstQueue m_cursor; // Items on cursor: FIFO
|
||||
ItemInstQueue m_cursor; // Items on cursor: FIFO
|
||||
|
||||
private:
|
||||
// Active inventory version
|
||||
EQClientVersion m_version;
|
||||
bool m_versionset;
|
||||
};
|
||||
|
||||
class SharedDatabase;
|
||||
|
||||
@ -306,6 +306,7 @@ OUTz(OP_FinishWindow2);
|
||||
//OUTv(OP_AdventureMerchantResponse, strlen(msg)+2);
|
||||
OUTv(OP_ItemPacket, ItemPacket_Struct);
|
||||
OUTv(OP_ColoredText, ColoredText_Struct);
|
||||
OUTv(OP_ItemRecastDelay, ItemRecastDelay_Struct);
|
||||
OUTv(OP_FormattedMessage, FormattedMessage_Struct);
|
||||
OUTv(OP_GuildMemberList, uint32); //variable length, but nasty
|
||||
OUTv(OP_InterruptCast, InterruptCast_Struct);
|
||||
|
||||
@ -180,6 +180,7 @@ namespace Client62 {
|
||||
|
||||
namespace limits {
|
||||
static const bool ALLOWS_EMPTY_BAG_IN_BAG = false;
|
||||
static const bool ALLOWS_CLICK_CAST_FROM_BAG = false;
|
||||
static const bool COIN_HAS_WEIGHT = true;
|
||||
}
|
||||
|
||||
|
||||
@ -184,6 +184,7 @@ namespace RoF {
|
||||
|
||||
namespace limits {
|
||||
static const bool ALLOWS_EMPTY_BAG_IN_BAG = true;
|
||||
static const bool ALLOWS_CLICK_CAST_FROM_BAG = true;
|
||||
static const bool COIN_HAS_WEIGHT = false;
|
||||
}
|
||||
|
||||
|
||||
@ -181,6 +181,7 @@ namespace SoD {
|
||||
|
||||
namespace limits {
|
||||
static const bool ALLOWS_EMPTY_BAG_IN_BAG = false;
|
||||
static const bool ALLOWS_CLICK_CAST_FROM_BAG = false;
|
||||
static const bool COIN_HAS_WEIGHT = false;
|
||||
}
|
||||
|
||||
|
||||
@ -181,6 +181,7 @@ namespace SoF {
|
||||
|
||||
namespace limits {
|
||||
static const bool ALLOWS_EMPTY_BAG_IN_BAG = false;
|
||||
static const bool ALLOWS_CLICK_CAST_FROM_BAG = false;
|
||||
static const bool COIN_HAS_WEIGHT = true;
|
||||
}
|
||||
|
||||
|
||||
@ -180,6 +180,7 @@ namespace Titanium {
|
||||
|
||||
namespace limits {
|
||||
static const bool ALLOWS_EMPTY_BAG_IN_BAG = false;
|
||||
static const bool ALLOWS_CLICK_CAST_FROM_BAG = false;
|
||||
static const bool COIN_HAS_WEIGHT = true;
|
||||
}
|
||||
|
||||
|
||||
@ -181,6 +181,7 @@ namespace Underfoot {
|
||||
|
||||
namespace limits {
|
||||
static const bool ALLOWS_EMPTY_BAG_IN_BAG = false;
|
||||
static const bool ALLOWS_CLICK_CAST_FROM_BAG = false;
|
||||
static const bool COIN_HAS_WEIGHT = false;
|
||||
}
|
||||
|
||||
|
||||
@ -523,9 +523,9 @@ void Database::ExpireMail() {
|
||||
time(nullptr) - RuleI(Mail, ExpireTrash));
|
||||
results = QueryDatabase(query);
|
||||
if(results.Success())
|
||||
_log(UCS__ERROR, "Error expiring trash messages, %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
else
|
||||
_log(UCS__INIT, "Expired %i trash messages.", results.RowsAffected());
|
||||
else
|
||||
_log(UCS__ERROR, "Error expiring trash messages, %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -653,3 +653,5 @@ OP_RAWOutOfSession=0x0000
|
||||
# we need to document the differences between these packets to make identifying them easier
|
||||
OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs
|
||||
OP_InitialHPUpdate=0x0000
|
||||
|
||||
OP_ItemRecastDelay=0x57ed
|
||||
|
||||
@ -660,3 +660,5 @@ OP_RAWOutOfSession=0x0000 #
|
||||
# we need to document the differences between these packets to make identifying them easier
|
||||
OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs
|
||||
OP_InitialHPUpdate=0x0000 #
|
||||
|
||||
OP_ItemRecastDelay=0x15c4
|
||||
|
||||
@ -654,3 +654,5 @@ OP_RAWOutOfSession=0x0000 #
|
||||
# we need to document the differences between these packets to make identifying them easier
|
||||
OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs
|
||||
OP_InitialHPUpdate=0x0000 #
|
||||
|
||||
OP_ItemRecastDelay=0x82d7
|
||||
|
||||
2079
zone/bot.cpp
2079
zone/bot.cpp
File diff suppressed because it is too large
Load Diff
@ -1307,7 +1307,13 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
||||
conn_state = ReceivedZoneEntry;
|
||||
|
||||
ClientVersion = Connection()->ClientVersion();
|
||||
ClientVersionBit = 1 << (ClientVersion - 1);
|
||||
if (ClientVersion != EQClientUnknown)
|
||||
ClientVersionBit = 1 << (ClientVersion - 1);
|
||||
else
|
||||
ClientVersionBit = 0;
|
||||
|
||||
bool siv = m_inv.SetInventoryVersion(ClientVersion);
|
||||
LogFile->write(EQEMuLog::Debug, "%s inventory version to %s(%i)", (siv ? "Succeeded in setting" : "Failed to set"), EQClientVersionName(ClientVersion), ClientVersion);
|
||||
|
||||
/* Antighost code
|
||||
tmp var is so the search doesnt find this object
|
||||
@ -3999,15 +4005,16 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
|
||||
else if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // ITEM or POTION cast
|
||||
{
|
||||
//discipline, using the item spell slot
|
||||
if (castspell->inventoryslot == 0xFFFFFFFF) {
|
||||
if (castspell->inventoryslot == INVALID_INDEX) {
|
||||
if (!UseDiscipline(castspell->spell_id, castspell->target_id)) {
|
||||
LogFile->write(EQEMuLog::Debug, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id);
|
||||
InterruptSpell(castspell->spell_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if ((castspell->inventoryslot <= EmuConstants::GENERAL_END) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // sanity check
|
||||
else if (m_inv.SupportsClickCasting(castspell->inventoryslot) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // sanity check
|
||||
{
|
||||
// packet field types will be reviewed as packet transistions occur -U
|
||||
const ItemInst* inst = m_inv[castspell->inventoryslot]; //slot values are int16, need to check packet on this field
|
||||
//bool cancast = true;
|
||||
if (inst && inst->IsType(ItemClassCommon))
|
||||
@ -8555,7 +8562,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
|
||||
|
||||
LogFile->write(EQEMuLog::Debug, "OP ItemVerifyRequest: spell=%i, target=%i, inv=%i", spell_id, target_id, slot_id);
|
||||
|
||||
if ((slot_id < MainCursor) || (slot_id == MainPowerSource) || (slot_id > 250 && slot_id < 331 && ((item->ItemType == ItemTypePotion) || item->PotionBelt))) // sanity check
|
||||
if (m_inv.SupportsClickCasting(slot_id) || ((item->ItemType == ItemTypePotion || item->PotionBelt) && m_inv.SupportsPotionBeltCasting(slot_id))) // sanity check
|
||||
{
|
||||
ItemInst* p_inst = (ItemInst*)inst;
|
||||
|
||||
@ -10686,7 +10693,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
|
||||
Client *i = entity_list.GetClientByName(ri->player_name);
|
||||
if (i){
|
||||
if (IsRaidGrouped()){
|
||||
i->Message_StringID(0, 5060); //group failed, must invite members not in raid...
|
||||
i->Message_StringID(0, ALREADY_IN_RAID, GetName()); //group failed, must invite members not in raid...
|
||||
return;
|
||||
}
|
||||
Raid *r = entity_list.GetRaidByClient(i);
|
||||
|
||||
@ -1239,6 +1239,11 @@ void Lua_Client::SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in
|
||||
self->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg);
|
||||
}
|
||||
|
||||
void Lua_Client::SendColoredText(uint32 type, std::string msg) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->SendColoredText(type, msg);
|
||||
}
|
||||
|
||||
void Lua_Client::PlayMP3(std::string file)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
@ -1492,6 +1497,7 @@ luabind::scope lua_register_client() {
|
||||
.def("SetThirst", (void(Lua_Client::*)(int))&Lua_Client::SetThirst)
|
||||
.def("SetConsumption", (void(Lua_Client::*)(int, int))&Lua_Client::SetConsumption)
|
||||
.def("SendMarqueeMessage", (void(Lua_Client::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_Client::SendMarqueeMessage)
|
||||
.def("SendColoredText", (void(Lua_Client::*)(uint32, std::string))&Lua_Client::SendColoredText)
|
||||
.def("PlayMP3", (void(Lua_Client::*)(std::string))&Lua_Client::PlayMP3);
|
||||
}
|
||||
|
||||
|
||||
@ -275,6 +275,7 @@ public:
|
||||
void SetThirst(int in_thirst);
|
||||
void SetConsumption(int in_hunger, int in_thirst);
|
||||
void SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg);
|
||||
void SendColoredText(uint32 type, std::string msg);
|
||||
void PlayMP3(std::string file);
|
||||
};
|
||||
|
||||
|
||||
554
zone/npc.cpp
554
zone/npc.cpp
@ -965,240 +965,352 @@ NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z,
|
||||
}
|
||||
}
|
||||
|
||||
uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) {
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
uint32 tmp = 0;
|
||||
uint32 tmp2 = 0;
|
||||
uint32 ZoneDatabase::CreateNewNPCCommand(const char* zone, uint32 zone_version,Client *client, NPC* spawn, uint32 extra) {
|
||||
|
||||
uint32 npc_type_id = 0;
|
||||
|
||||
if (extra && client && client->GetZoneID())
|
||||
{
|
||||
// Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000)
|
||||
int starting_npc_id = client->GetZoneID() * 1000;
|
||||
|
||||
std::string query = StringFormat("SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i",
|
||||
starting_npc_id, starting_npc_id + 1000);
|
||||
auto results = QueryDatabase(query);
|
||||
if (results.Success()) {
|
||||
if (results.RowCount() != 0)
|
||||
{
|
||||
auto row = results.begin();
|
||||
npc_type_id = atoi(row[0]) + 1;
|
||||
// Prevent the npc_type id from exceeding the range for this zone
|
||||
if (npc_type_id >= (starting_npc_id + 1000))
|
||||
npc_type_id = 0;
|
||||
}
|
||||
else // No npc_type IDs set in this range yet
|
||||
npc_type_id = starting_npc_id;
|
||||
}
|
||||
}
|
||||
|
||||
char tmpstr[64];
|
||||
EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr)));
|
||||
std::string query;
|
||||
if (npc_type_id)
|
||||
{
|
||||
query = StringFormat("INSERT INTO npc_types (id, name, level, race, class, hp, gender, "
|
||||
"texture, helmtexture, size, loottable_id, merchant_id, face, "
|
||||
"runspeed, prim_melee_type, sec_melee_type) "
|
||||
"VALUES(%i, \"%s\" , %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)",
|
||||
npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(),
|
||||
spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(),
|
||||
spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(),
|
||||
spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
npc_type_id = results.LastInsertedID();
|
||||
}
|
||||
else
|
||||
{
|
||||
query = StringFormat("INSERT INTO npc_types (name, level, race, class, hp, gender, "
|
||||
"texture, helmtexture, size, loottable_id, merchant_id, face, "
|
||||
"runspeed, prim_melee_type, sec_melee_type) "
|
||||
"VALUES(\"%s\", %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)",
|
||||
tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(),
|
||||
spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(),
|
||||
spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(),
|
||||
spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
npc_type_id = results.LastInsertedID();
|
||||
}
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
query = StringFormat("INSERT INTO spawngroup (id, name) VALUES(%i, '%s-%s')", 0, zone, spawn->GetName());
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
uint32 spawngroupid = results.LastInsertedID();
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) "
|
||||
"VALUES('%s', %u, %f, %f, %f, %i, %f, %i)",
|
||||
zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200,
|
||||
spawn->GetHeading(), spawngroupid);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) VALUES(%i, %i, %i)",
|
||||
spawngroupid, npc_type_id, 100);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 ZoneDatabase::AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime) {
|
||||
uint32 last_insert_id = 0;
|
||||
|
||||
std::string query = StringFormat("INSERT INTO spawngroup (name) VALUES('%s%s%i')",
|
||||
zone, spawn->GetName(), Timer::GetCurrentTime());
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return 0;
|
||||
}
|
||||
last_insert_id = results.LastInsertedID();
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
uint32 respawntime = 0;
|
||||
uint32 spawnid = 0;
|
||||
if (respawnTime)
|
||||
respawntime = respawnTime;
|
||||
else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0)
|
||||
respawntime = spawn->respawn2->RespawnTimer();
|
||||
else
|
||||
respawntime = 1200;
|
||||
|
||||
query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) "
|
||||
"VALUES('%s', %u, %f, %f, %f, %i, %f, %i)",
|
||||
zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime,
|
||||
spawn->GetHeading(), last_insert_id);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return 0;
|
||||
}
|
||||
spawnid = results.LastInsertedID();
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) VALUES(%i, %i, %i)",
|
||||
last_insert_id, spawn->GetNPCTypeID(), 100);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
return spawnid;
|
||||
}
|
||||
|
||||
uint32 ZoneDatabase::UpdateNPCTypeAppearance(Client *client, NPC* spawn) {
|
||||
|
||||
std::string query = StringFormat("UPDATE npc_types SET name = \"%s\", level = %i, race = %i, class = %i, "
|
||||
"hp = %i, gender = %i, texture = %i, helmtexture = %i, size = %i, "
|
||||
"loottable_id = %i, merchant_id = %i, face = %i, WHERE id = %i",
|
||||
spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(),
|
||||
spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(),
|
||||
spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(),
|
||||
spawn->MerchantType, spawn->GetNPCTypeID());
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success() && client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
return results.Success() == true? 1: 0;
|
||||
}
|
||||
|
||||
uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn) {
|
||||
uint32 id = 0;
|
||||
uint32 spawngroupID = 0;
|
||||
|
||||
std::string query = StringFormat("SELECT id, spawngroupID FROM spawn2 WHERE "
|
||||
"zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2());
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return 0;
|
||||
|
||||
if (results.RowCount() == 0)
|
||||
return 0;
|
||||
|
||||
auto row = results.begin();
|
||||
if (row[0])
|
||||
id = atoi(row[0]);
|
||||
|
||||
if (row[1])
|
||||
spawngroupID = atoi(row[1]);
|
||||
|
||||
query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", id);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return 0;
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
query = StringFormat("DELETE FROM spawngroup WHERE id = '%i'", spawngroupID);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return 0;
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
query = StringFormat("DELETE FROM spawnentry WHERE spawngroupID = '%i'", spawngroupID);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return 0;
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32 ZoneDatabase::DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn) {
|
||||
|
||||
uint32 id = 0;
|
||||
uint32 spawngroupID = 0;
|
||||
|
||||
std::string query = StringFormat("SELECT id, spawngroupID FROM spawn2 WHERE zone = '%s' "
|
||||
"AND version = %u AND spawngroupID = %i",
|
||||
zone, zone_version, spawn->GetSp2());
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return 0;
|
||||
|
||||
if (results.RowCount() == 0)
|
||||
return 0;
|
||||
|
||||
auto row = results.begin();
|
||||
|
||||
if (row[0])
|
||||
id = atoi(row[0]);
|
||||
|
||||
if (row[1])
|
||||
spawngroupID = atoi(row[1]);
|
||||
|
||||
query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", id);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return 0;
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
query = StringFormat("DELETE FROM spawngroup WHERE id = '%i'", spawngroupID);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return 0;
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
query = StringFormat("DELETE FROM spawnentry WHERE spawngroupID = '%i'", spawngroupID);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return 0;
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
query = StringFormat("DELETE FROM npc_types WHERE id = '%i'", spawn->GetNPCTypeID());
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return 0;
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32 ZoneDatabase::AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) {
|
||||
|
||||
uint32 last_insert_id = 0;
|
||||
std::string query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) "
|
||||
"VALUES('%s', %u, %f, %f, %f, %i, %f, %i)",
|
||||
zone, zone_version, client->GetX(), client->GetY(), client->GetZ(),
|
||||
120, client->GetHeading(), spawnGroupID);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return 0;
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32 ZoneDatabase::AddNPCTypes(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) {
|
||||
|
||||
uint32 npc_type_id;
|
||||
char numberlessName[64];
|
||||
|
||||
EntityList::RemoveNumbers(strn0cpy(numberlessName, spawn->GetName(), sizeof(numberlessName)));
|
||||
std::string query = StringFormat("INSERT INTO npc_types (name, level, race, class, hp, gender, "
|
||||
"texture, helmtexture, size, loottable_id, merchant_id, face, "
|
||||
"runspeed, prim_melee_type, sec_melee_type) "
|
||||
"VALUES(\"%s\", %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)",
|
||||
numberlessName, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(),
|
||||
spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(),
|
||||
spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(),
|
||||
spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return 0;
|
||||
npc_type_id = results.LastInsertedID();
|
||||
|
||||
if(client)
|
||||
client->LogSQL(query.c_str());
|
||||
|
||||
if(client)
|
||||
client->Message(0, "%s npc_type ID %i created successfully!", numberlessName, npc_type_id);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) {
|
||||
|
||||
switch (command) {
|
||||
case 0: { // Create a new NPC and add all spawn related data
|
||||
uint32 npc_type_id = 0;
|
||||
uint32 spawngroupid;
|
||||
if (extra && c && c->GetZoneID())
|
||||
{
|
||||
// Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000)
|
||||
int starting_npc_id = c->GetZoneID() * 1000;
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i", starting_npc_id, (starting_npc_id + 1000)), errbuf, &result)) {
|
||||
row = mysql_fetch_row(result);
|
||||
if(row)
|
||||
{
|
||||
if (row[0])
|
||||
{
|
||||
npc_type_id = atoi(row[0]) + 1;
|
||||
// Prevent the npc_type id from exceeding the range for this zone
|
||||
if (npc_type_id >= (starting_npc_id + 1000))
|
||||
{
|
||||
npc_type_id = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// row[0] is nullptr - No npc_type IDs set in this range yet
|
||||
npc_type_id = starting_npc_id;
|
||||
}
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
mysql_free_result(result);
|
||||
}
|
||||
}
|
||||
char tmpstr[64];
|
||||
EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr)));
|
||||
if (npc_type_id)
|
||||
{
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (id, name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(%i,\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
snprintf(tmpstr, sizeof(tmpstr), "%s-%s", zone, spawn->GetName());
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (id, name) values(%i, '%s')", tmp, tmpstr), errbuf, 0, 0, &spawngroupid)) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200, spawn->GetHeading(), spawngroupid), errbuf, 0, 0, &tmp)) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", spawngroupid, npc_type_id, 100), errbuf, 0)) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
return true;
|
||||
break;
|
||||
return CreateNewNPCCommand(zone, zone_version, c, spawn, extra);
|
||||
}
|
||||
case 1:{ // Add new spawn group and spawn point for an existing NPC Type ID
|
||||
tmp2 = spawn->GetNPCTypeID();
|
||||
char tmpstr[64];
|
||||
snprintf(tmpstr, sizeof(tmpstr), "%s%s%i", zone, spawn->GetName(),Timer::GetCurrentTime());
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (name) values('%s')", tmpstr), errbuf, 0, 0, &last_insert_id)) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
|
||||
uint32 respawntime = 0;
|
||||
uint32 spawnid = 0;
|
||||
if (extra)
|
||||
respawntime = extra;
|
||||
else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0)
|
||||
respawntime = spawn->respawn2->RespawnTimer();
|
||||
else
|
||||
respawntime = 1200;
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime, spawn->GetHeading(), last_insert_id), errbuf, 0, 0, &spawnid)) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", last_insert_id, tmp2, 100), errbuf, 0)) {
|
||||
LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf);
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
return spawnid;
|
||||
break;
|
||||
return AddNewNPCSpawnGroupCommand(zone, zone_version, c, spawn, extra);
|
||||
}
|
||||
case 2: { // Update npc_type appearance and other data on targeted spawn
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET name=\"%s\", level=%i, race=%i, class=%i, hp=%i, gender=%i, texture=%i, helmtexture=%i, size=%i, loottable_id=%i, merchant_id=%i, face=%i, WHERE id=%i", spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, spawn->GetNPCTypeID()), errbuf, 0)) {
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
safe_delete_array(query);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
return UpdateNPCTypeAppearance(c, spawn);
|
||||
}
|
||||
case 3: { // delete spawn from spawning, but leave in npc_types table
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()), errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
return 0;
|
||||
}
|
||||
safe_delete_array(query);
|
||||
|
||||
row = mysql_fetch_row(result);
|
||||
if (row == nullptr) return false;
|
||||
if (row[0]) tmp = atoi(row[0]);
|
||||
if (row[1]) tmp2 = atoi(row[1]);
|
||||
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) {
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) {
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) {
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
return true;
|
||||
|
||||
|
||||
break;
|
||||
return DeleteSpawnLeaveInNPCTypeTable(zone, c, spawn);
|
||||
}
|
||||
case 4: { //delete spawn from DB (including npc_type)
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND version=%u AND spawngroupID=%i", zone, zone_version, spawn->GetSp2()), errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
return(0);
|
||||
}
|
||||
safe_delete_array(query);
|
||||
|
||||
row = mysql_fetch_row(result);
|
||||
if (row == nullptr) return false;
|
||||
if (row[0]) tmp = atoi(row[0]);
|
||||
if (row[1]) tmp2 = atoi(row[1]);
|
||||
mysql_free_result(result);
|
||||
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) {
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) {
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) {
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM npc_types WHERE id='%i'", spawn->GetNPCTypeID()), errbuf,0)) {
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
return true;
|
||||
break;
|
||||
return DeleteSpawnRemoveFromNPCTypeTable(zone, zone_version, c, spawn);
|
||||
}
|
||||
case 5: { // add a spawn from spawngroup
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, c->GetX(), c->GetY(), c->GetZ(), 120, c->GetHeading(), extra), errbuf, 0, 0, &tmp)) {
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
return AddSpawnFromSpawnGroup(zone, zone_version, c, spawn, extra);
|
||||
}
|
||||
case 6: { // add npc_type
|
||||
uint32 npc_type_id;
|
||||
char tmpstr[64];
|
||||
EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr)));
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) {
|
||||
safe_delete(query);
|
||||
return false;
|
||||
}
|
||||
if(c) c->LogSQL(query);
|
||||
safe_delete_array(query);
|
||||
if(c) c->Message(0, "%s npc_type ID %i created successfully!", tmpstr, npc_type_id);
|
||||
return true;
|
||||
break;
|
||||
return AddNPCTypes(zone, zone_version, c, spawn, extra);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -1743,7 +1855,7 @@ void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
||||
else
|
||||
ns->spawn.IsMercenary = 0;
|
||||
}
|
||||
|
||||
|
||||
//Not recommended if using above (However, this will work better on older clients).
|
||||
if (RuleB(Pets, UnTargetableSwarmPet)) {
|
||||
if(GetOwnerID() || GetSwarmOwner()) {
|
||||
|
||||
@ -6020,6 +6020,32 @@ XS(XS_Client_SendMarqueeMessage)
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS_Client_SendColoredText); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Client_SendColoredText)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 3)
|
||||
Perl_croak(aTHX_ "Usage: Client::SendColoredText(color, message)");
|
||||
{
|
||||
Client * THIS;
|
||||
uint32 color = (uint32)SvUV(ST(1));
|
||||
std::string msg = (std::string)SvPV_nolen(ST(2));
|
||||
dXSTARG;
|
||||
|
||||
if (sv_derived_from(ST(0), "Client")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
THIS = INT2PTR(Client *,tmp);
|
||||
}
|
||||
else
|
||||
Perl_croak(aTHX_ "THIS is not of type Client");
|
||||
if(THIS == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
THIS->SendColoredText(color, msg);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
@ -6259,6 +6285,7 @@ XS(boot_Client)
|
||||
newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$");
|
||||
newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$");
|
||||
newXSproto(strcpy(buf, "SendMarqueeMessage"), XS_Client_SendMarqueeMessage, file, "$$$$$$$");
|
||||
newXSproto(strcpy(buf, "SendColoredText"), XS_Client_SendColoredText, file, "$$$");
|
||||
XSRETURN_YES;
|
||||
}
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#include "string_ids.h"
|
||||
#include "../common/misc_functions.h"
|
||||
#include "../common/rulesys.h"
|
||||
#include "../common/string_util.h"
|
||||
|
||||
|
||||
int Mob::GetKickDamage() {
|
||||
@ -346,15 +347,23 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
|
||||
ReuseTime = MonkSpecialAttack(GetTarget(), ca_atk->m_skill) - 1 - skill_reduction;
|
||||
|
||||
//Live AA - Technique of Master Wu
|
||||
uint16 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack;
|
||||
if (bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > MakeRandomInt(0, 99))) {
|
||||
|
||||
int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick };
|
||||
MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]);
|
||||
|
||||
// always 1/4 of the double attack chance, 25% at rank 5 (100/4)
|
||||
if ((bDoubleSpecialAttack / 4) > MakeRandomInt(0, 99))
|
||||
MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]);
|
||||
int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack;
|
||||
if (wuchance) {
|
||||
if (wuchance >= 100 || wuchance > MakeRandomInt(0, 99)) {
|
||||
int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick };
|
||||
int extra = 1;
|
||||
// always 1/4 of the double attack chance, 25% at rank 5 (100/4)
|
||||
if (wuchance / 4 > MakeRandomInt(0, 99))
|
||||
extra++;
|
||||
// They didn't add a string ID for this.
|
||||
std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra);
|
||||
// live uses 400 here -- not sure if it's the best for all clients though
|
||||
SendColoredText(400, msg);
|
||||
while (extra) {
|
||||
MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]);
|
||||
extra--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ReuseTime < 100) {
|
||||
@ -1743,18 +1752,21 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
|
||||
return;
|
||||
|
||||
//Live AA - Technique of Master Wu
|
||||
uint16 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack;
|
||||
if( bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > MakeRandomInt(0,100))) {
|
||||
|
||||
int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick };
|
||||
MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0,4)]);
|
||||
|
||||
int TripleChance = 25;
|
||||
if (bDoubleSpecialAttack > 100)
|
||||
TripleChance += TripleChance*(100-bDoubleSpecialAttack)/100;
|
||||
|
||||
if(TripleChance > MakeRandomInt(0,100)) {
|
||||
MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0,4)]);
|
||||
int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack;
|
||||
if (wuchance) {
|
||||
if (wuchance >= 100 || wuchance > MakeRandomInt(0, 99)) {
|
||||
int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick };
|
||||
int extra = 1;
|
||||
if (wuchance / 4 > MakeRandomInt(0, 99))
|
||||
extra++;
|
||||
// They didn't add a string ID for this.
|
||||
std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra);
|
||||
// live uses 400 here -- not sure if it's the best for all clients though
|
||||
SendColoredText(400, msg);
|
||||
while (extra) {
|
||||
MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0, 4)]);
|
||||
extra--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3205,55 +3205,55 @@ snare has both of them negative, yet their range should work the same:
|
||||
case 124: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += caster_level - 50;
|
||||
result += updownsign * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 125: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 2 * (caster_level - 50);
|
||||
result += updownsign * 2 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 126: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 3 * (caster_level - 50);
|
||||
result += updownsign * 3 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 127: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 4 * (caster_level - 50);
|
||||
result += updownsign * 4 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 128: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 5 * (caster_level - 50);
|
||||
result += updownsign * 5 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 129: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 10 * (caster_level - 50);
|
||||
result += updownsign * 10 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 130: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 15 * (caster_level - 50);
|
||||
result += updownsign * 15 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 131: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 20 * (caster_level - 50);
|
||||
result += updownsign * 20 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 132: // check sign
|
||||
result = ubase;
|
||||
if (caster_level > 50)
|
||||
result += 25 * (caster_level - 50);
|
||||
result += updownsign * 25 * (caster_level - 50);
|
||||
break;
|
||||
|
||||
case 137: // used in berserker AA desperation
|
||||
|
||||
@ -484,6 +484,19 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
return false;
|
||||
}
|
||||
|
||||
// This needs a bit more work for saving timer to PP for zoning etc
|
||||
if (IsClient() && item_slot != INVALID_INDEX && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT))) {
|
||||
ItemInst *itm = CastToClient()->GetInv().GetItem(item_slot);
|
||||
if (itm && itm->GetItem()->RecastDelay) {
|
||||
outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct));
|
||||
ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer;
|
||||
ird->recast_delay = itm->GetItem()->RecastDelay;
|
||||
ird->recast_type = itm->GetItem()->RecastType;
|
||||
CastToClient()->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
@ -275,6 +275,7 @@
|
||||
#define TOLD_NOT_ONLINE 5046 //%1 is not online at this time.
|
||||
#define PETITION_NO_DELETE 5053 //You do not have a petition in the queue.
|
||||
#define PETITION_DELETED 5054 //Your petition was successfully deleted.
|
||||
#define ALREADY_IN_RAID 5060 //%1 is already in a raid.
|
||||
#define GAIN_RAIDEXP 5085 //You gained raid experience!
|
||||
#define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there.
|
||||
#define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure.
|
||||
|
||||
@ -1615,36 +1615,36 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat
|
||||
|
||||
}
|
||||
|
||||
void Client::SendBazaarWelcome(){
|
||||
|
||||
void Client::SendBazaarWelcome()
|
||||
{
|
||||
const std::string query = "SELECT COUNT(DISTINCT char_id), count(char_id) FROM trader";
|
||||
auto results = database.QueryDatabase(query);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (results.Success() && results.RowCount() == 1){
|
||||
auto row = results.begin();
|
||||
auto row = results.begin();
|
||||
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct));
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct));
|
||||
|
||||
memset(outapp->pBuffer,0,outapp->size);
|
||||
memset(outapp->pBuffer,0,outapp->size);
|
||||
|
||||
BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer;
|
||||
BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer;
|
||||
|
||||
bws->Beginning.Action = BazaarWelcome;
|
||||
bws->Beginning.Action = BazaarWelcome;
|
||||
|
||||
bws->Traders = atoi(row[0]);
|
||||
bws->Items = atoi(row[1]);
|
||||
bws->Traders = atoi(row[0]);
|
||||
bws->Items = atoi(row[1]);
|
||||
|
||||
QueuePacket(outapp);
|
||||
QueuePacket(outapp);
|
||||
|
||||
safe_delete(outapp);
|
||||
}
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
const std::string buyerCountQuery = "SELECT COUNT(DISTINCT charid) FROM buyer";
|
||||
results = database.QueryDatabase(query);
|
||||
const std::string buyerCountQuery = "SELECT COUNT(DISTINCT charid) FROM buyer";
|
||||
results = database.QueryDatabase(buyerCountQuery);
|
||||
if (!results.Success() || results.RowCount() != 1)
|
||||
return;
|
||||
return;
|
||||
|
||||
auto row = results.begin();
|
||||
Message(10, "There are %i Buyers waiting to purchase your loot. Type /barter to search for them, "
|
||||
auto row = results.begin();
|
||||
Message(10, "There are %i Buyers waiting to purchase your loot. Type /barter to search for them, "
|
||||
"or use /buyer to set up your own Buy Lines.", atoi(row[0]));
|
||||
}
|
||||
|
||||
|
||||
@ -376,6 +376,13 @@ public:
|
||||
/* NPCs */
|
||||
const NPCType* GetNPCType(uint32 id);
|
||||
uint32 NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete
|
||||
uint32 CreateNewNPCCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra);
|
||||
uint32 AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime);
|
||||
uint32 DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn);
|
||||
uint32 DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn);
|
||||
uint32 AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID);
|
||||
uint32 AddNPCTypes(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID);
|
||||
uint32 UpdateNPCTypeAppearance(Client *client, NPC* spawn);
|
||||
bool SetSpecialAttkFlag(uint8 id, const char* flag);
|
||||
bool GetPetEntry(const char *pet_type, PetRecord *into);
|
||||
bool GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user