diff --git a/changelog.txt b/changelog.txt index 6e37c6513..a7a1127dd 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,28 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/09/2015 == +Trevius: (RoF+) Setting Alt flag on characters in the Guild Management Window is now saved and functional for filtering. +Trevius: (RoF+) Guild Invites between RoF+ and previous Clients is now functional. + +== 02/08/2015 == +Kayen: Implemented npc specialability (44) COUNTER_AVOID_DAMAGE which when applied to the ATTACKING NPC will make their attacks more difficult to be avoided by riposte/dodge/parry/block. +Parama0: Negative modifer value that affects ALL avoid damage types dodge/parry/riposte/block) chance on defender. Ie (44,50 = 50 pct reduction to ALL) +Parama1: Negative modifer value that affects RIPOSTE chance on defender. Ie (44,1,0,50 = 50 pct reduction to riposte chance) +Parama2: Negative modifer value that affects PARRY chance on defender. Ie (44,1,0,0,50 = 50 pct reduction to parry chance) +Parama3: Negative modifer value that affects BLOCK chance on defender. Ie (44,1,0,0,0,50 = 50 pct reduction to block chance) +Parama4: Negative modifer value that affects DODGE chance on defender. e (44,1,0,0,0,0,50 = 50 pct reduction to dodge chance) +Example of usage: Player has Improved Dodge V (+50 pct dodge chance), you want to negate this bonus you would set 44,1,0,0,0,0,50 on your NPC. + +== 02/07/2015 == +Akkadius: Reduced #repop time dramatically by taking down hundreds of individual SELECT/DELETE/INSERT queries in routines and bringing it down to very few + See: https://www.youtube.com/watch?v=9kSFbyTBuAk + +== 02/06/2015 == +Uleat: Updated returns for Inventory and ItemInst const iterators. (const == const) +Uleat: Replaced 'iter_inst' and 'iter_contents' typedefs with their stl definitions +Uleat: Removed 'limbo' from the 'HasItem' series of checks - including lore checks. The client excludes this range and it causes issues when performing item searches - dupe lore checks were added to account for this. +Uleat: Updated command #iteminfo to show light source information and a few other things + == 02/05/2015 == Trevius: Fixed Environmental Damage for RoF2. diff --git a/client_files/import/main.cpp b/client_files/import/main.cpp index a8477f77a..3a2f4154f 100644 --- a/client_files/import/main.cpp +++ b/client_files/import/main.cpp @@ -76,6 +76,24 @@ int GetSpellColumns(SharedDatabase *db) { return results.RowCount(); } +bool IsStringField(int i) { + switch(i) + { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + return true; + break; + default: + return false; + } +} + void ImportSpells(SharedDatabase *db) { Log.Out(Logs::General, Logs::Status, "Importing Spells..."); FILE *f = fopen("import/spells_us.txt", "r"); @@ -113,7 +131,12 @@ void ImportSpells(SharedDatabase *db) { sql += "'"; } - sql += split[i]; + if(split[i].compare("") == 0 && !IsStringField(i)) { + sql += "0"; + } + else { + sql += split[i]; + } sql += "'"; } @@ -128,7 +151,12 @@ void ImportSpells(SharedDatabase *db) { sql += "'"; } - sql += split[i]; + if(split[i].compare("") == 0 && !IsStringField(i)) { + sql += "0"; + } else { + sql += split[i]; + } + sql += "'"; } diff --git a/common/item.cpp b/common/item.cpp index 82da305f1..0e2f0f574 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -506,6 +506,7 @@ int16 Inventory::HasItem(uint32 item_id, uint8 quantity, uint8 where) return slot_id; } + // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItem(m_cursor, item_id, quantity); @@ -552,6 +553,7 @@ int16 Inventory::HasItemByUse(uint8 use, uint8 quantity, uint8 where) return slot_id; } + // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItemByUse(m_cursor, use, quantity); @@ -597,6 +599,7 @@ int16 Inventory::HasItemByLoreGroup(uint32 loregroup, uint8 where) return slot_id; } + // Behavioral change - Limbo is no longer checked due to improper handling of return value if (where & invWhereCursor) { // Check cursor queue slot_id = _HasItemByLoreGroup(m_cursor, loregroup); @@ -1060,7 +1063,7 @@ int Inventory::GetSlotByItemInstCollection(const std::map &col } if (t_inst && !t_inst->IsType(ItemClassContainer)) { - for (auto b_iter = t_inst->_begin(); b_iter != t_inst->_end(); ++b_iter) { + for (auto b_iter = t_inst->_cbegin(); b_iter != t_inst->_cend(); ++b_iter) { if (b_iter->second == inst) { return Inventory::CalcSlotId(iter->first, b_iter->first); } @@ -1071,13 +1074,10 @@ int Inventory::GetSlotByItemInstCollection(const std::map &col return -1; } -void Inventory::dumpItemCollection(const std::map &collection) { - iter_inst it; - iter_contents itb; - ItemInst* inst = nullptr; - - for (it = collection.begin(); it != collection.end(); ++it) { - inst = it->second; +void Inventory::dumpItemCollection(const std::map &collection) +{ + for (auto it = collection.cbegin(); it != collection.cend(); ++it) { + auto inst = it->second; if (!inst || !inst->GetItem()) continue; @@ -1088,14 +1088,13 @@ void Inventory::dumpItemCollection(const std::map &collection) } } -void Inventory::dumpBagContents(ItemInst *inst, iter_inst *it) { - iter_contents itb; - +void Inventory::dumpBagContents(ItemInst *inst, std::map::const_iterator *it) +{ if (!inst || !inst->IsType(ItemClassContainer)) return; // Go through bag, if bag - for (itb = inst->_begin(); itb != inst->_end(); ++itb) { + for (auto itb = inst->_cbegin(); itb != inst->_cend(); ++itb) { ItemInst* baginst = itb->second; if (!baginst || !baginst->GetItem()) continue; @@ -1110,7 +1109,7 @@ void Inventory::dumpBagContents(ItemInst *inst, iter_inst *it) { // Internal Method: Retrieves item within an inventory bucket ItemInst* Inventory::_GetItem(const std::map& bucket, int16 slot_id) const { - iter_inst it = bucket.find(slot_id); + auto it = bucket.find(slot_id); if (it != bucket.end()) { return it->second; } @@ -1123,6 +1122,8 @@ ItemInst* Inventory::_GetItem(const std::map& bucket, int16 sl // Assumes item has already been allocated int16 Inventory::_PutItem(int16 slot_id, ItemInst* inst) { + // What happens here when we _PutItem(MainCursor)? Bad things..really bad things... + // // If putting a nullptr into slot, we need to remove slot without memory delete if (inst == nullptr) { //Why do we not delete the poped item here???? @@ -1204,7 +1205,7 @@ int16 Inventory::_HasItem(std::map& bucket, uint32 item_id, ui if (!inst->IsType(ItemClassContainer)) { continue; } - for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) { + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { auto bag_inst = bag_iter->second; if (bag_inst == nullptr) { continue; } @@ -1235,7 +1236,7 @@ int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity) uint8 quantity_found = 0; - for (auto iter = iqueue.begin(); iter != iqueue.end(); ++iter) { + for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { auto inst = *iter; if (inst == nullptr) { continue; } @@ -1252,7 +1253,7 @@ int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity) if (!inst->IsType(ItemClassContainer)) { continue; } - for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) { + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { auto bag_inst = bag_iter->second; if (bag_inst == nullptr) { continue; } @@ -1267,6 +1268,9 @@ int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity) return legacy::SLOT_AUGMENT; } } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; } return INVALID_INDEX; @@ -1289,7 +1293,7 @@ int16 Inventory::_HasItemByUse(std::map& bucket, uint8 use, ui if (!inst->IsType(ItemClassContainer)) { continue; } - for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) { + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { auto bag_inst = bag_iter->second; if (bag_inst == nullptr) { continue; } @@ -1309,7 +1313,7 @@ int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity) { uint8 quantity_found = 0; - for (auto iter = iqueue.begin(); iter != iqueue.end(); ++iter) { + for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { auto inst = *iter; if (inst == nullptr) { continue; } @@ -1321,7 +1325,7 @@ int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity) if (!inst->IsType(ItemClassContainer)) { continue; } - for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) { + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { auto bag_inst = bag_iter->second; if (bag_inst == nullptr) { continue; } @@ -1331,6 +1335,9 @@ int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity) return Inventory::CalcSlotId(MainCursor, bag_iter->first); } } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; } return INVALID_INDEX; @@ -1355,7 +1362,7 @@ int16 Inventory::_HasItemByLoreGroup(std::map& bucket, uint32 if (!inst->IsType(ItemClassContainer)) { continue; } - for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) { + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { auto bag_inst = bag_iter->second; if (bag_inst == nullptr) { continue; } @@ -1378,7 +1385,7 @@ int16 Inventory::_HasItemByLoreGroup(std::map& bucket, uint32 // Internal Method: Checks an inventory queue type bucket for a particular item int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup) { - for (auto iter = iqueue.begin(); iter != iqueue.end(); ++iter) { + for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { auto inst = *iter; if (inst == nullptr) { continue; } @@ -1395,7 +1402,7 @@ int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup) if (!inst->IsType(ItemClassContainer)) { continue; } - for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) { + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { auto bag_inst = bag_iter->second; if (bag_inst == nullptr) { continue; } @@ -1410,6 +1417,9 @@ int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup) return legacy::SLOT_AUGMENT; } } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; } return INVALID_INDEX; @@ -1505,8 +1515,7 @@ ItemInst::ItemInst(const ItemInst& copy) m_attuned=copy.m_attuned; m_merchantcount=copy.m_merchantcount; // Copy container contents - iter_contents it; - for (it=copy.m_contents.begin(); it!=copy.m_contents.end(); ++it) { + for (auto it = copy.m_contents.begin(); it != copy.m_contents.end(); ++it) { ItemInst* inst_old = it->second; ItemInst* inst_new = nullptr; @@ -1676,7 +1685,7 @@ bool ItemInst::IsAugmentSlotAvailable(int32 augtype, uint8 slot) const // Retrieve item inside container ItemInst* ItemInst::GetItem(uint8 index) const { - iter_contents it = m_contents.find(index); + auto it = m_contents.find(index); if (it != m_contents.end()) { return it->second; } @@ -1739,7 +1748,7 @@ void ItemInst::ClearByFlags(byFlagSetting is_nodrop, byFlagSetting is_norent) // TODO: This needs work... // Destroy container contents - iter_contents cur, end, del; + std::map::const_iterator cur, end, del; cur = m_contents.begin(); end = m_contents.end(); for (; cur != end;) { diff --git a/common/item.h b/common/item.h index fd30b5ea0..906e00313 100644 --- a/common/item.h +++ b/common/item.h @@ -33,9 +33,6 @@ class EvolveInfo; // Stores information about an evolving item family #include #include -// Helper typedefs -typedef std::map::const_iterator iter_inst; -typedef std::map::const_iterator iter_contents; namespace ItemField { @@ -86,8 +83,8 @@ public: // Public Methods ///////////////////////// - inline std::list::const_iterator begin() { return m_list.begin(); } - inline std::list::const_iterator end() { return m_list.end(); } + inline std::list::const_iterator cbegin() { return m_list.cbegin(); } + inline std::list::const_iterator cend() { return m_list.cend(); } inline int size() { return static_cast(m_list.size()); } // TODO: change to size_t inline bool empty() { return m_list.empty(); } @@ -140,8 +137,8 @@ public: ItemInst* GetItem(int16 slot_id) const; ItemInst* GetItem(int16 slot_id, uint8 bagidx) const; - inline std::list::const_iterator cursor_begin() { return m_cursor.begin(); } - inline std::list::const_iterator cursor_end() { return m_cursor.end(); } + inline std::list::const_iterator cursor_cbegin() { return m_cursor.cbegin(); } + inline std::list::const_iterator cursor_cend() { return m_cursor.cend(); } inline int CursorSize() { return m_cursor.size(); } inline bool CursorEmpty() { return m_cursor.empty(); } @@ -227,7 +224,7 @@ protected: int GetSlotByItemInstCollection(const std::map &collection, ItemInst *inst); void dumpItemCollection(const std::map &collection); - void dumpBagContents(ItemInst *inst, iter_inst *it); + void dumpBagContents(ItemInst *inst, std::map::const_iterator *it); // Retrieves item within an inventory bucket ItemInst* _GetItem(const std::map& bucket, int16 slot_id) const; @@ -425,8 +422,8 @@ protected: ////////////////////////// // Protected Members ////////////////////////// - iter_contents _begin() { return m_contents.begin(); } - iter_contents _end() { return m_contents.end(); } + std::map::const_iterator _cbegin() { return m_contents.cbegin(); } + std::map::const_iterator _cend() { return m_contents.cend(); } friend class Inventory; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 6947ded15..d0189e56a 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -5053,7 +5053,7 @@ namespace RoF2 //sprintf(hdr.unknown000, "06e0002Y1W00"); - snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%012d", item->ID); + snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%016d", item->ID); hdr.stacksize = stackable ? charges : 1; hdr.unknown004 = 0; diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 9ef524f55..95f5c31e6 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -4353,7 +4353,7 @@ struct RoF2SlotStruct struct ItemSerializationHeader { -/*000*/ char unknown000[13]; // New for HoT. Looks like a string. +/*000*/ char unknown000[17]; // New for HoT. Looks like a string. /*017*/ uint32 stacksize; /*021*/ uint32 unknown004; /*025*/ uint8 slot_type; // 0 = normal, 1 = bank, 2 = shared bank, 9 = merchant, 20 = ? diff --git a/common/ruletypes.h b/common/ruletypes.h index ff88115e2..12a8a0737 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -300,7 +300,7 @@ RULE_INT ( Spells, MaxCastTimeReduction, 50) //Max percent your spell cast time RULE_INT ( Spells, RootBreakFromSpells, 55) //Chance for root to break when cast on. RULE_INT ( Spells, DeathSaveCharismaMod, 3) //Determines how much charisma effects chance of death save firing. RULE_INT ( Spells, DivineInterventionHeal, 8000) //Divine intervention heal amount. -RULE_BOOL ( Spells, AdditiveBonusValues, false) //Allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. (ie Add together all Cleave Effects) +RULE_INT ( Spells, AdditiveBonusWornType, 0) //Calc worn bonuses to add together (instead of taking highest) if set to THIS worn type. (2=Will covert live items automatically) RULE_BOOL ( Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest functions will ignore entries where field 12 is CHA. What's the best way to do this? RULE_BOOL ( Spells, BuffLevelRestrictions, true) //Buffs will not land on low level toons like live RULE_INT ( Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick. diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 8a150a86c..6328f38eb 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1183,7 +1183,7 @@ void SharedDatabase::GetFactionListInfo(uint32 &list_count, uint32 &max_lists) { auto row = results.begin(); list_count = static_cast(atoul(row[0])); - max_lists = static_cast(atoul(row[1])); + max_lists = static_cast(atoul(row[1] ? row[1] : "0")); } const NPCFactionList* SharedDatabase::GetNPCFactionEntry(uint32 id) { @@ -1263,9 +1263,6 @@ bool SharedDatabase::LoadNPCFactionLists() { uint32 list_count = 0; uint32 max_lists = 0; GetFactionListInfo(list_count, max_lists); - if(list_count == 0) { - EQ_EXCEPT("SharedDatabase", "Database returned no result"); - } uint32 size = static_cast(EQEmu::FixedMemoryHashSet::estimated_size( list_count, max_lists)); @@ -1883,7 +1880,7 @@ void SharedDatabase::GetLootTableInfo(uint32 &loot_table_count, uint32 &max_loot auto row = results.begin(); loot_table_count = static_cast(atoul(row[0])); - max_loot_table = static_cast(atoul(row[1])); + max_loot_table = static_cast(atoul(row[1] ? row[1] : "0")); loot_table_entries = static_cast(atoul(row[2])); } @@ -1905,7 +1902,7 @@ void SharedDatabase::GetLootDropInfo(uint32 &loot_drop_count, uint32 &max_loot_d auto row =results.begin(); loot_drop_count = static_cast(atoul(row[0])); - max_loot_drop = static_cast(atoul(row[1])); + max_loot_drop = static_cast(atoul(row[1] ? row[1] : "0")); loot_drop_entries = static_cast(atoul(row[2])); } diff --git a/shared_memory/npc_faction.cpp b/shared_memory/npc_faction.cpp index 9c76605a7..cde79ab77 100644 --- a/shared_memory/npc_faction.cpp +++ b/shared_memory/npc_faction.cpp @@ -32,9 +32,6 @@ void LoadFactions(SharedDatabase *database) { uint32 lists = 0; uint32 max_list = 0; database->GetFactionListInfo(lists, max_list); - if(lists == 0) { - EQ_EXCEPT("Shared Memory", "Unable to get any factions from the database."); - } uint32 size = static_cast(EQEmu::FixedMemoryHashSet::estimated_size(lists, max_list)); EQEmu::MemoryMappedFile mmf("shared/faction", size); diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 05a3a4a76..d144b040e 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -129,7 +129,7 @@ OP_GuildInviteAccept=0x78a5 OP_GuildDemote=0x3100 OP_GuildPromote=0x2945 OP_GuildPublicNote=0x3c2c -OP_GuildManageBanker=0x096d # Was 0x0737 +OP_GuildManageBanker=0x389c # Was 0x096d OP_GuildBank=0x2ab0 # Was 0x10c3 OP_SetGuildRank=0x3599 OP_GuildUpdateURLAndChannel=0x7851 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 7159dcce5..189edc8b3 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -128,7 +128,7 @@ OP_GuildInviteAccept=0x7053 OP_GuildDemote=0x2d4e OP_GuildPromote=0x6a98 OP_GuildPublicNote=0x5053 -OP_GuildManageBanker=0x748f +OP_GuildManageBanker=0x3f35 OP_GuildBank=0x5134 OP_SetGuildRank=0x0b9c OP_GuildUpdateURLAndChannel=0x2958 @@ -353,21 +353,22 @@ OP_Marquee=0x502e OP_ItemRecastDelay=0x15a9 #OP_OpenInventory=0x0000 # Likely does not exist in RoF -U -OP_DzQuit=0x205f -OP_DzListTimers=0x0398 -OP_DzAddPlayer=0x59ca -OP_DzRemovePlayer=0x4701 -OP_DzSwapPlayer=0x1abc -OP_DzMakeLeader=0x405b -OP_DzPlayerList=0x543d -OP_DzJoinExpeditionConfirm=0x14c6 -OP_DzJoinExpeditionReply=0x7f4b -OP_DzExpeditionInfo=0x4f7e -OP_DzExpeditionList=0x9119 -OP_DzMemberStatus=0xb2e3 -OP_DzLeaderStatus=0x32f0 +# Expeditions +OP_DzAddPlayer=0x4701 +OP_DzRemovePlayer=0x1abc +OP_DzSwapPlayer=0x405b +OP_DzMakeLeader=0x543d +OP_DzPlayerList=0x14c6 +OP_DzJoinExpeditionConfirm=0x7f4b +OP_DzJoinExpeditionReply=0x1950 +OP_DzListTimers=0x7b68 +OP_DzExpeditionInfo=0x9119 +OP_DzExpeditionList=0x205f +OP_DzQuit=0xb2e3 +OP_DzMemberStatus=0x32f0 +OP_DzLeaderStatus=0x3de9 +OP_DzMemberList=0x5ae4 OP_DzExpeditionEndsWarning=0x383c -OP_DzMemberList=0x3de9 OP_DzCompass=0x3e0e OP_DzChooseZone=0x0b7d diff --git a/utils/sql/git/optional/2015_2_6_AdditiveBonusWornType.sql b/utils/sql/git/optional/2015_2_6_AdditiveBonusWornType.sql new file mode 100644 index 000000000..aab88bd9f --- /dev/null +++ b/utils/sql/git/optional/2015_2_6_AdditiveBonusWornType.sql @@ -0,0 +1,4 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AdditiveBonusWornType', '0', 'Calcs worn bonuses to add together (instead of taking highest) if item set to THIS worn type. Will stack with regular worn bonuses. (2=Will cause all live items to use this behavior)'); + +-- This is no longer used - Set the above value equal to 2 to achieve the same effect. +DELETE FROM `rule_values` WHERE rule_name LIKE "Spells:AdditiveBonusValues"; diff --git a/zone/attack.cpp b/zone/attack.cpp index bfd4a5cc2..b98fa1c3e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -362,13 +362,40 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) //garunteed hit bool ghit = false; - if((attacker->spellbonuses.MeleeSkillCheck + attacker->itembonuses.MeleeSkillCheck) > 500) + if((attacker->aabonuses.MeleeSkillCheck + attacker->spellbonuses.MeleeSkillCheck + attacker->itembonuses.MeleeSkillCheck) > 500) ghit = true; + bool InFront = false; + + if (attacker->InFrontMob(this, attacker->GetX(), attacker->GetY())) + InFront = true; + + /* + This special ability adds a negative modifer to the defenders riposte/block/parry/chance + therefore reducing the defenders chance to successfully avoid the melee attack. At present + time this is the only way to fine tune counter these mods on players. This may + ultimately end up being more useful as fields in npc_types. + */ + + int counter_all = 0; + int counter_riposte = 0; + int counter_block = 0; + int counter_parry = 0; + int counter_dodge = 0; + + if (attacker->GetSpecialAbility(COUNTER_AVOID_DAMAGE)){ + + counter_all = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 0); + counter_riposte = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE,1); + counter_block = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 2); + counter_parry = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 3); + counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4); + } + ////////////////////////////////////////////////////////// // make enrage same as riposte ///////////////////////////////////////////////////////// - if (IsEnraged() && other->InFrontMob(this, other->GetX(), other->GetY())) { + if (IsEnraged() && InFront) { damage = -3; Log.Out(Logs::Detail, Logs::Combat, "I am enraged, riposting frontal attack."); } @@ -377,9 +404,10 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) // riposte ///////////////////////////////////////////////////////// float riposte_chance = 0.0f; - if (CanRiposte && damage > 0 && CanThisClassRiposte() && other->InFrontMob(this, other->GetX(), other->GetY())) + if (CanRiposte && damage > 0 && CanThisClassRiposte() && InFront) { - riposte_chance = (100.0f + (float)defender->aabonuses.RiposteChance + (float)defender->spellbonuses.RiposteChance + (float)defender->itembonuses.RiposteChance) / 100.0f; + riposte_chance = (100.0f + static_cast(aabonuses.RiposteChance + spellbonuses.RiposteChance + + itembonuses.RiposteChance - counter_riposte - counter_all)) / 100.0f; skill = GetSkill(SkillRiposte); if (IsClient()) { CastToClient()->CheckIncreaseSkill(SkillRiposte, other, -10); @@ -398,28 +426,19 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) /////////////////////////////////////////////////////// bool bBlockFromRear = false; - bool bShieldBlockFromRear = false; - if (this->IsClient()) { - int aaChance = 0; + // a successful roll on this does not mean a successful block is forthcoming. only that a chance to block + // from a direction other than the rear is granted. - // a successful roll on this does not mean a successful block is forthcoming. only that a chance to block - // from a direction other than the rear is granted. + int BlockBehindChance = aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind; - //Live AA - HightenedAwareness - int BlockBehindChance = aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind; - - if (BlockBehindChance && zone->random.Roll(BlockBehindChance)) { - bBlockFromRear = true; - - if (spellbonuses.BlockBehind || itembonuses.BlockBehind) - bShieldBlockFromRear = true; //This bonus should allow a chance to Shield Block from behind. - } - } + if (BlockBehindChance && zone->random.Roll(BlockBehindChance)) + bBlockFromRear = true; float block_chance = 0.0f; - if (damage > 0 && CanThisClassBlock() && (other->InFrontMob(this, other->GetX(), other->GetY()) || bBlockFromRear)) { - block_chance = (100.0f + (float)spellbonuses.IncreaseBlockChance + (float)itembonuses.IncreaseBlockChance) / 100.0f; + if (damage > 0 && CanThisClassBlock() && (InFront || bBlockFromRear)) { + block_chance = (100.0f + static_cast(aabonuses.IncreaseBlockChance + spellbonuses.IncreaseBlockChance + + itembonuses.IncreaseBlockChance - counter_block - counter_all)) / 100.0f; skill = CastToClient()->GetSkill(SkillBlock); if (IsClient()) { CastToClient()->CheckIncreaseSkill(SkillBlock, other, -10); @@ -435,32 +454,20 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) RollTable[1] = RollTable[0]; } - if(damage > 0 && HasShieldEquiped() && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) - && (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { - - float bonusShieldBlock = 0.0f; - bonusShieldBlock = static_cast(aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock); - RollTable[1] += bonusShieldBlock; - } - - if(IsClient() && damage > 0 && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) - && (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { - if(CastToClient()->m_inv.GetItem(MainPrimary)) { - float bonusStaffBlock = 0.0f; - if (CastToClient()->m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HBlunt){ - bonusStaffBlock = static_cast(aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock); - RollTable[1] += bonusStaffBlock; - } - } - } + //Try Shield Block OR TwoHandBluntBlockCheck + if(damage > 0 && HasShieldEquiped() && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) && (InFront || bBlockFromRear)) + RollTable[1] += static_cast(aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock - counter_block - counter_all); + else if(damage > 0 && HasTwoHandBluntEquiped() && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) && (InFront || bBlockFromRear)) + RollTable[1] += static_cast(aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock - counter_block - counter_all); + ////////////////////////////////////////////////////// // parry ////////////////////////////////////////////////////// float parry_chance = 0.0f; - if (damage > 0 && CanThisClassParry() && other->InFrontMob(this, other->GetX(), other->GetY())) - { - parry_chance = (100.0f + (float)defender->spellbonuses.ParryChance + (float)defender->itembonuses.ParryChance) / 100.0f; + if (damage > 0 && CanThisClassParry() && InFront){ + parry_chance = (100.0f + static_cast(aabonuses.ParryChance + itembonuses.ParryChance + + itembonuses.ParryChance - counter_parry - counter_all)) / 100.0f; skill = CastToClient()->GetSkill(SkillParry); if (IsClient()) { CastToClient()->CheckIncreaseSkill(SkillParry, other, -10); @@ -481,9 +488,11 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) // dodge //////////////////////////////////////////////////////// float dodge_chance = 0.0f; - if (damage > 0 && CanThisClassDodge() && other->InFrontMob(this, other->GetX(), other->GetY())) - { - dodge_chance = (100.0f + (float)defender->spellbonuses.DodgeChance + (float)defender->itembonuses.DodgeChance) / 100.0f; + if (damage > 0 && CanThisClassDodge() && InFront){ + + dodge_chance = (100.0f + static_cast(aabonuses.DodgeChance + spellbonuses.DodgeChance + + itembonuses.DodgeChance - counter_dodge - counter_all)) / 100.0f; + skill = CastToClient()->GetSkill(SkillDodge); if (IsClient()) { CastToClient()->CheckIncreaseSkill(SkillDodge, other, -10); @@ -3521,6 +3530,10 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons Log.Out(Logs::Detail, Logs::Combat, "Melee Damage reduced to %d", damage); damage = ReduceAllDamage(damage); TryTriggerThreshHold(damage, SE_TriggerMeleeThreshold, attacker); + + if (skill_used) + CheckNumHitsRemaining(NumHit::IncomingHitSuccess); + } else { int32 origdmg = damage; damage = AffectMagicalDamage(damage, spell_id, iBuffTic, attacker); @@ -3536,9 +3549,6 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons TryTriggerThreshHold(damage, SE_TriggerSpellThreshold, attacker); } - if (skill_used) - CheckNumHitsRemaining(NumHit::IncomingHitSuccess); - if(IsClient() && CastToClient()->sneaking){ CastToClient()->sneaking = false; SendAppearancePacket(AT_Sneak, 0); diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 4aa13519b..a76860ce4 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -139,7 +139,8 @@ void Client::CalcItemBonuses(StatBonuses* newbon) { // Clear item faction mods ClearItemFactionBonuses(); - ShieldEquiped(false); + SetShieldEquiped(false); + SetTwoHandBluntEquiped(false); unsigned int i; //should not include 21 (SLOT_AMMO) @@ -149,9 +150,12 @@ void Client::CalcItemBonuses(StatBonuses* newbon) { continue; AddItemBonuses(inst, newbon); - //Check if item is secondary slot is a 'shield'. Required for multiple spelll effects. - if (i == MainSecondary && (m_inv.GetItem(MainSecondary)->GetItem()->ItemType == ItemTypeShield)) - ShieldEquiped(true); + //These are given special flags due to how often they are checked for various spell effects. + const Item_Struct *item = inst->GetItem(); + if (i == MainSecondary && (item && item->ItemType == ItemTypeShield)) + SetShieldEquiped(true); + else if (i == MainPrimary && (item && item->ItemType == ItemType2HBlunt)) + SetTwoHandBluntEquiped(true); } //Power Source Slot @@ -169,6 +173,17 @@ void Client::CalcItemBonuses(StatBonuses* newbon) { continue; AddItemBonuses(inst, newbon, false, true); } + + //Optional ability to have worn effects calculate as an addititive bonus instead of highest value + if (RuleI(Spells, AdditiveBonusWornType) && RuleI(Spells, AdditiveBonusWornType) != ET_WornEffect){ + for (i = MainCharm; i < MainAmmo; i++) { + const ItemInst* inst = m_inv[i]; + if(inst == 0) + continue; + AdditiveWornBonuses(inst, newbon); + } + } + // Caps if(newbon->HPRegen > CalcHPRegenCap()) newbon->HPRegen = CalcHPRegenCap(); @@ -410,12 +425,12 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu else newbon->DSMitigation += item->DSMitigation; } - if (item->Worn.Effect>0 && (item->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, true, true); + if (item->Worn.Effect > 0 && item->Worn.Type == ET_WornEffect) {// latent effects + ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type); } if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects - ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0, true, false); + ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0); } switch(item->BardType) @@ -537,6 +552,45 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu } +void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug) { + + /* + Powerful Non-live like option allows developers to add worn effects on items that + can stack with other worn effects of the same spell effect type, instead of only taking the highest value. + Ie Cleave I = 40 pct cleave - So if you equip 3 cleave I items you will have a 120 pct cleave bonus. + To enable use RuleI(Spells, AdditiveBonusWornType) + Setting value = 2 Will force all live items to automatically be calculated additivily + Setting value to anything else will indicate the item 'worntype' that if set to the same, will cause the bonuses to use this calculation + which will also stack with regular (worntype 2) effects. [Ie set rule = 3 and item worntype = 3] + */ + + if(!inst || !inst->IsType(ItemClassCommon)) + return; + + if(inst->GetAugmentType()==0 && isAug == true) + return; + + const Item_Struct *item = inst->GetItem(); + + if(!inst->IsEquipable(GetBaseRace(),GetClass())) + return; + + if(GetLevel() < item->ReqLevel) + return; + + if (item->Worn.Effect > 0 && item->Worn.Type == RuleI(Spells, AdditiveBonusWornType)) + ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);// Non-live like - Addititive latent effects + + + if (!isAug) + { + int i; + for (i = 0; i < EmuConstants::ITEM_COMMON_SIZE; i++) { + AdditiveWornBonuses(inst->GetAugment(i),newbon,true); + } + } +} + void Client::CalcEdibleBonuses(StatBonuses* newbon) { uint32 i; @@ -1393,7 +1447,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) int buff_count = GetMaxTotalSlots(); for(i = 0; i < buff_count; i++) { if(buffs[i].spellid != SPELL_UNKNOWN){ - ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, false, false, buffs[i].ticsremaining,i); + ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining,i); if (buffs[i].numhits > 0) Numhits(true); @@ -1416,10 +1470,11 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells. } -void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, bool item_bonus, bool IsWornEffect, uint32 ticsremaining, int buffslot, +void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, uint8 WornType, uint32 ticsremaining, int buffslot, bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max) { int i, effect_value, base2, max, effectid; + bool AdditiveWornBonus = false; Mob *caster = nullptr; if(!IsAISpellEffect && !IsValidSpell(spell_id)) @@ -1439,15 +1494,19 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne uint8 focus = IsFocusEffect(spell_id, i); if (focus) { - if (!IsWornEffect) - new_bonus->FocusEffects[focus] = static_cast(spells[spell_id].effectid[i]); + if (WornType){ + if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) + new_bonus->FocusEffectsWorn[focus] += spells[spell_id].base[i]; + } - else if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) - new_bonus->FocusEffectsWorn[focus] += spells[spell_id].base[i]; + else + new_bonus->FocusEffects[focus] = static_cast(spells[spell_id].effectid[i]); continue; } + if (WornType && (RuleI(Spells, AdditiveBonusWornType) == WornType)) + AdditiveWornBonus = true; effectid = spells[spell_id].effectid[i]; effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining); @@ -1813,7 +1872,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_CriticalHitChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) { + if (AdditiveWornBonus) { if(base2 == -1) new_bonus->CriticalHitChance[HIGHEST_SKILL+1] += effect_value; else @@ -1839,7 +1898,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_CrippBlowChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->CrippBlowChance += effect_value; else if((effect_value < 0) && (new_bonus->CrippBlowChance > effect_value)) @@ -1853,7 +1912,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_AvoidMeleeChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->AvoidMeleeChanceEffect += effect_value; else if((effect_value < 0) && (new_bonus->AvoidMeleeChanceEffect > effect_value)) @@ -1866,7 +1925,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_RiposteChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->RiposteChance += effect_value; else if((effect_value < 0) && (new_bonus->RiposteChance > effect_value)) @@ -1879,7 +1938,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_DodgeChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->DodgeChance += effect_value; else if((effect_value < 0) && (new_bonus->DodgeChance > effect_value)) @@ -1892,7 +1951,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_ParryChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->ParryChance += effect_value; else if((effect_value < 0) && (new_bonus->ParryChance > effect_value)) @@ -1905,7 +1964,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_DualWieldChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->DualWieldChance += effect_value; else if((effect_value < 0) && (new_bonus->DualWieldChance > effect_value)) @@ -1919,7 +1978,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_DoubleAttackChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->DoubleAttackChance += effect_value; else if((effect_value < 0) && (new_bonus->DoubleAttackChance > effect_value)) @@ -1933,7 +1992,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_TripleAttackChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->TripleAttackChance += effect_value; else if((effect_value < 0) && (new_bonus->TripleAttackChance > effect_value)) @@ -1946,7 +2005,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_MeleeLifetap: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->MeleeLifetap += spells[spell_id].base[i]; else if((effect_value < 0) && (new_bonus->MeleeLifetap > effect_value)) @@ -1995,7 +2054,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_HundredHands: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->HundredHands += effect_value; if (effect_value > 0 && effect_value > new_bonus->HundredHands) @@ -2017,7 +2076,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_HitChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus){ + if (AdditiveWornBonus){ if(base2 == -1) new_bonus->HitChanceEffect[HIGHEST_SKILL+1] += effect_value; else @@ -2084,7 +2143,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_ProcChance: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + if (AdditiveWornBonus) new_bonus->ProcChanceSPA += effect_value; else if((effect_value < 0) && (new_bonus->ProcChanceSPA > effect_value)) @@ -2122,7 +2181,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_DivineSave: { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) { + if (AdditiveWornBonus) { new_bonus->DivineSaveChance[0] += effect_value; new_bonus->DivineSaveChance[1] = 0; } @@ -2131,7 +2190,6 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne { new_bonus->DivineSaveChance[0] = effect_value; new_bonus->DivineSaveChance[1] = base2; - //SetDeathSaveChance(true); } break; } @@ -3051,12 +3109,12 @@ void NPC::CalcItemBonuses(StatBonuses *newbon) newbon->ProcChance += cur->CombatEffects; } if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon); + ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon, 0, cur->Worn.Type); } if (RuleB(Spells, NPC_UseFocusFromItems)){ if (cur->Focus.Effect>0 && (cur->Focus.Type == ET_Focus)){ // focus effects - ApplySpellsBonuses(cur->Focus.Effect, cur->Focus.Level, newbon, 0, true); + ApplySpellsBonuses(cur->Focus.Effect, cur->Focus.Level, newbon); } } diff --git a/zone/bot.cpp b/zone/bot.cpp index c39696576..4b42fac37 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10959,7 +10959,7 @@ void Bot::CalcItemBonuses() } } if ((itemtmp->Worn.Effect != 0) && (itemtmp->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses); + ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses,0,itemtmp->Worn.Type); } } } @@ -11043,7 +11043,7 @@ void Bot::CalcItemBonuses() } } if ((itemtmp->Worn.Effect != 0) && (itemtmp->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses); + ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses,0,itemtmp->Worn.Type); } } } diff --git a/zone/client.cpp b/zone/client.cpp index 5cc5ae309..b5575a307 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -141,6 +141,10 @@ Client::Client(EQStreamInterface* ieqs) merc_timer(RuleI(Mercs, UpkeepIntervalMS)), ItemTickTimer(10000), ItemQuestTimer(500), + anon_toggle_timer(250), + afk_toggle_timer(250), + helm_toggle_timer(250), + light_update_timer(600), m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f), m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f), @@ -409,6 +413,69 @@ Client::~Client() { UninitializeBuffSlots(); } +void Client::SendZoneInPackets() +{ + ////////////////////////////////////////////////////// + // Spawn Appearance Packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; + sa->type = AT_SpawnID; // Is 0x10 used to set the player id? + sa->parameter = GetID(); // Four bytes for this parameter... + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + // Inform the world about the client + outapp = new EQApplicationPacket(); + + CreateSpawnPacket(outapp); + outapp->priority = 6; + if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + if (GetPVP()) //force a PVP update until we fix the spawn struct + SendAppearancePacket(AT_PVP, GetPVP(), true, false); + + //Send AA Exp packet: + if (GetLevel() >= 51) + SendAAStats(); + + // Send exp packets + outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); + ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; + uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); + uint32 tmpxp2 = GetEXPForLevel(GetLevel()); + + // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) + if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { + float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); + eu->exp = (uint32)(330.0f * tmpxp); + outapp->priority = 6; + QueuePacket(outapp); + } + safe_delete(outapp); + + SendAATimers(); + + outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); + ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; + strcpy(zonesendname->name, m_pp.name); + strcpy(zonesendname->name2, m_pp.name); + zonesendname->unknown0 = 0x0A; + QueuePacket(outapp); + safe_delete(outapp); + + if (IsInAGuild()) { + SendGuildMembers(); + SendGuildURL(); + SendGuildChannel(); + SendGuildLFGuildStatus(); + } + SendLFGuildStatus(); + + //No idea why live sends this if even were not in a guild + SendGuildMOTD(); +} + void Client::SendLogoutPackets() { EQApplicationPacket* outapp = new EQApplicationPacket(OP_CancelTrade, sizeof(CancelTrade_Struct)); @@ -2471,11 +2538,13 @@ void Client::LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 bool Client::BindWound(Mob* bindmob, bool start, bool fail){ EQApplicationPacket* outapp = 0; - if(!fail) { + if(!fail) + { outapp = new EQApplicationPacket(OP_Bind_Wound, sizeof(BindWound_Struct)); BindWound_Struct* bind_out = (BindWound_Struct*) outapp->pBuffer; // Start bind - if(!bindwound_timer.Enabled()) { + if(!bindwound_timer.Enabled()) + { //make sure we actually have a bandage... and consume it. int16 bslot = m_inv.HasItemByUse(ItemTypeBandage, 1, invWhereWorn|invWherePersonal); if (bslot == INVALID_INDEX) { @@ -2521,7 +2590,9 @@ bool Client::BindWound(Mob* bindmob, bool start, bool fail){ ; // Binding self } } - } else { + } + else if (bindwound_timer.Check()) // Did the timer finish? + { // finish bind // disable complete timer bindwound_timer.Disable(); diff --git a/zone/client.h b/zone/client.h index 9248fb742..53d3c0428 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1257,6 +1257,7 @@ protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); void AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false, bool isTribute = false); + void AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false); int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat); void CalcEdibleBonuses(StatBonuses* newbon); void CalcAABonuses(StatBonuses* newbon); @@ -1414,6 +1415,7 @@ private: bool CanBeInZone(); void SendLogoutPackets(); + void SendZoneInPackets(); bool AddPacket(const EQApplicationPacket *, bool); bool AddPacket(EQApplicationPacket**, bool); bool SendAllPackets(); @@ -1464,6 +1466,10 @@ private: Timer TrackingTimer; Timer RespawnFromHoverTimer; Timer merc_timer; + Timer anon_toggle_timer; + Timer afk_toggle_timer; + Timer helm_toggle_timer; + Timer light_update_timer; glm::vec3 m_Proximity; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 95475f1ca..db7f65781 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1089,76 +1089,15 @@ void Client::Handle_Connect_OP_SendAATable(const EQApplicationPacket *app) void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) { - ////////////////////////////////////////////////////// - // Spawn Appearance Packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; - sa->type = AT_SpawnID; // Is 0x10 used to set the player id? - sa->parameter = GetID(); // Four bytes for this parameter... - outapp->priority = 6; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendExpZonein, 0); QueuePacket(outapp); safe_delete(outapp); - // Inform the world about the client - outapp = new EQApplicationPacket(); - - CreateSpawnPacket(outapp); - outapp->priority = 6; - if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if (GetPVP()) //force a PVP update until we fix the spawn struct - SendAppearancePacket(AT_PVP, GetPVP(), true, false); - - //Send AA Exp packet: - if (GetLevel() >= 51) - SendAAStats(); - - // Send exp packets - outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); - ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) - if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); + // SoF+ Gets Zone-In packets after sending OP_WorldObjectsSent + if (GetClientVersion() < ClientVersion::SoF) + { + SendZoneInPackets(); } - safe_delete(outapp); - - SendAATimers(); - - outapp = new EQApplicationPacket(OP_SendExpZonein, 0); - QueuePacket(outapp); - safe_delete(outapp); - - outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name, m_pp.name); - strcpy(zonesendname->name2, m_pp.name); - zonesendname->unknown0 = 0x0A; - QueuePacket(outapp); - safe_delete(outapp); - - /* this is actually the guild MOTD - outapp = new EQApplicationPacket(OP_ZoneInSendName2, sizeof(ZoneInSendName_Struct2)); - ZoneInSendName_Struct2* zonesendname2=(ZoneInSendName_Struct2*)outapp->pBuffer; - strcpy(zonesendname2->name,m_pp.name); - QueuePacket(outapp); - safe_delete(outapp);*/ - - if (IsInAGuild()) { - SendGuildMembers(); - SendGuildURL(); - SendGuildChannel(); - SendGuildLFGuildStatus(); - } - SendLFGuildStatus(); - - //No idea why live sends this if even were not in a guild - SendGuildMOTD(); return; } @@ -1216,72 +1155,13 @@ void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app) void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) { - //This is a copy of SendExpZonein created for SoF+ due to packet order change - - ////////////////////////////////////////////////////// - // Spawn Appearance Packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; - sa->type = AT_SpawnID; // Is 0x10 used to set the player id? - sa->parameter = GetID(); // Four bytes for this parameter... - outapp->priority = 6; + // New for SoF+ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); QueuePacket(outapp); safe_delete(outapp); - // Inform the world about the client - outapp = new EQApplicationPacket(); - - CreateSpawnPacket(outapp); - outapp->priority = 6; - if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if (GetPVP()) //force a PVP update until we fix the spawn struct - SendAppearancePacket(AT_PVP, GetPVP(), true, false); - - //Send AA Exp packet: - if (GetLevel() >= 51) - SendAAStats(); - - // Send exp packets - outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); - ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); - uint32 tmpxp2 = GetEXPForLevel(GetLevel()); - - // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) - if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); - eu->exp = (uint32)(330.0f * tmpxp); - outapp->priority = 6; - QueuePacket(outapp); - } - safe_delete(outapp); - - SendAATimers(); - - // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein - outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); - QueuePacket(outapp); - safe_delete(outapp); - - outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name, m_pp.name); - strcpy(zonesendname->name2, m_pp.name); - zonesendname->unknown0 = 0x0A; - QueuePacket(outapp); - safe_delete(outapp); - - if (IsInAGuild()) { - SendGuildMembers(); - SendGuildURL(); - SendGuildChannel(); - SendGuildLFGuildStatus(); - } - SendLFGuildStatus(); - - //No idea why live sends this if even were not in a guild - SendGuildMOTD(); + // Packet order changed for SoF+, so below is sent here instead of OP_SendExpLogin + SendZoneInPackets(); if (RuleB(Mercs, AllowMercs)) { @@ -1838,9 +1718,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (loaditems) { /* Dont load if a length error occurs */ BulkSendInventoryItems(); /* Send stuff on the cursor which isnt sent in bulk */ - for (auto iter = m_inv.cursor_begin(); iter != m_inv.cursor_end(); ++iter) { + for (auto iter = m_inv.cursor_cbegin(); iter != m_inv.cursor_cend(); ++iter) { /* First item cursor is sent in bulk inventory packet */ - if (iter == m_inv.cursor_begin()) + if (iter == m_inv.cursor_cbegin()) continue; const ItemInst *inst = *iter; SendItemPacket(MainCursor, inst, ItemPacketSummonItem); @@ -7360,6 +7240,16 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) if (gc->guildeqid == 0) gc->guildeqid = GuildID(); + // Convert Membership Level between RoF and previous clients. + if (client->GetClientVersion() < ClientVersion::RoF && GetClientVersion() >= ClientVersion::RoF) + { + gc->officer = 0; + } + if (client->GetClientVersion() >= ClientVersion::RoF && GetClientVersion() < ClientVersion::RoF) + { + gc->officer = 8; + } + Log.Out(Logs::Detail, Logs::Guilds, "Sending OP_GuildInvite for invite to %s, length %d", client->GetName(), app->size); client->SetPendingGuildInvitation(true); client->QueuePacket(app); @@ -7394,6 +7284,8 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) GuildInviteAccept_Struct* gj = (GuildInviteAccept_Struct*)app->pBuffer; + uint32 guildrank = gj->response; + if (GetClientVersion() >= ClientVersion::RoF) { if (gj->response > 9) @@ -7422,9 +7314,25 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) Log.Out(Logs::Detail, Logs::Guilds, "Guild Invite Accept: guild %d, response %d, inviter %s, person %s", gj->guildeqid, gj->response, gj->inviter, gj->newmember); + //ok, the invite is also used for changing rank as well. + Mob* inviter = entity_list.GetMob(gj->inviter); + + if (inviter && inviter->IsClient()) + { + Client* client = inviter->CastToClient(); + // Convert Membership Level between RoF and previous clients. + if (client->GetClientVersion() < ClientVersion::RoF && GetClientVersion() >= ClientVersion::RoF) + { + guildrank = 0; + } + if (client->GetClientVersion() >= ClientVersion::RoF && GetClientVersion() < ClientVersion::RoF) + { + guildrank = 8; + } + } //we dont really care a lot about what this packet means, as long as //it has been authorized with the guild manager - if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response)) { + if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, guildrank)) { worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has sent an invalid response to your invite!", GetName()); Message(13, "Invalid invite response packet!"); return; @@ -7452,7 +7360,7 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) //change guild and rank - uint32 guildrank = gj->response; + guildrank = gj->response; if (GetClientVersion() >= ClientVersion::RoF) { @@ -12145,6 +12053,10 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) mp->quantity = prevcharges; } + // Item's stackable, but the quantity they want to buy exceeds the max stackable quantity. + if (item->Stackable && mp->quantity > item->StackSize) + mp->quantity = item->StackSize; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct)); Merchant_Sell_Struct* mpo = (Merchant_Sell_Struct*)outapp->pBuffer; mpo->quantity = mp->quantity; @@ -12171,6 +12083,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) mpo->price = SinglePrice; else mpo->price = SinglePrice * mp->quantity; + if (mpo->price < 0) { safe_delete(outapp); @@ -12640,6 +12553,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) else if (sa->type == AT_Anim) { if (IsAIControlled()) return; + if (sa->parameter == ANIM_STAND) { SetAppearance(eaStanding); playeraction = 0; @@ -12673,15 +12587,6 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) SetFeigned(false); } - // This is from old code - // I have no clue what it's for - /* - else if (sa->parameter == 0x05) { - // Illusion - std::cout << "Illusion packet recv'd:" << std::endl; - DumpPacket(app); - } - */ else { std::cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << std::endl; return; @@ -12690,6 +12595,10 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) entity_list.QueueClients(this, app, true); } else if (sa->type == AT_Anon) { + if(!anon_toggle_timer.Check()) { + return; + } + // For Anon/Roleplay if (sa->parameter == 1) { // Anon m_pp.anon = 1; @@ -12711,13 +12620,18 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) return; } else if (sa->type == AT_AFK) { - this->AFK = (sa->parameter == 1); - entity_list.QueueClients(this, app, true); + if(afk_toggle_timer.Check()) { + AFK = (sa->parameter == 1); + entity_list.QueueClients(this, app, true); + } } else if (sa->type == AT_Split) { m_pp.autosplit = (sa->parameter == 1); } else if (sa->type == AT_Sneak) { + if(sneaking == 0) + return; + if (sa->parameter != 0) { if (!HasSkill(SkillSneak)) @@ -12729,7 +12643,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) } return; } - this->sneaking = 0; + sneaking = 0; entity_list.QueueClients(this, app, true); } else if (sa->type == AT_Size) @@ -12741,7 +12655,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) } else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield) { - entity_list.QueueClients(this, app, false); + //don't do anything with this } else if (sa->type == AT_Levitate) { @@ -12750,8 +12664,10 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) } else if (sa->type == AT_ShowHelm) { - m_pp.showhelm = (sa->parameter == 1); - entity_list.QueueClients(this, app, true); + if(helm_toggle_timer.Check()) { + m_pp.showhelm = (sa->parameter == 1); + entity_list.QueueClients(this, app, true); + } } else { std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec diff --git a/zone/client_process.cpp b/zone/client_process.cpp index bddc874c2..3f9d0ee3a 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -239,7 +239,8 @@ bool Client::Process() { if(IsAIControlled()) AI_Process(); - if (bindwound_timer.Check() && bindwound_target != 0) { + // Don't reset the bindwound timer so we can check it in BindWound as well. + if (bindwound_timer.Check(false) && bindwound_target != 0) { BindWound(bindwound_target, false); } @@ -260,6 +261,13 @@ bool Client::Process() { } } + if(light_update_timer.Check()) { + UpdateEquipLightValue(); + if(UpdateActiveLightValue()) { + SendAppearancePacket(AT_Light, GetActiveLightValue()); + } + } + bool may_use_attacks = false; /* Things which prevent us from attacking: diff --git a/zone/command.cpp b/zone/command.cpp index 35d1ab353..e15379704 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -2614,7 +2614,7 @@ void command_peekinv(Client *c, const Seperator *sep) } else { int cursorDepth = 0; - for (auto it = targetClient->GetInv().cursor_begin(); (it != targetClient->GetInv().cursor_end()); ++it, ++cursorDepth) { + for (auto it = targetClient->GetInv().cursor_cbegin(); (it != targetClient->GetInv().cursor_cend()); ++it, ++cursorDepth) { inst_main = *it; item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem(); linker.SetItemInst(inst_main); @@ -4280,31 +4280,49 @@ void command_goto(Client *c, const Seperator *sep) void command_iteminfo(Client *c, const Seperator *sep) { - const ItemInst* inst = c->GetInv()[MainCursor]; - - if (!inst) - c->Message(13, "Error: You need an item on your cursor for this command"); - else { - const Item_Struct* item = inst->GetItem(); - c->Message(0, "ID: %i Name: %s", item->ID, item->Name); - c->Message(0, " Lore: %s ND: %i NS: %i Type: %i", (item->LoreFlag) ? "true":"false", item->NoDrop, item->NoRent, item->ItemClass); - c->Message(0, " IDF: %s Size: %i Weight: %i icon_id: %i Price: %i", item->IDFile, item->Size, item->Weight, item->Icon, item->Price); - if (c->Admin() >= 200) - c->Message(0, "MinStatus: %i", item->MinStatus); - if (item->ItemClass==ItemClassBook) - c->Message(0, " This item is a Book: %s", item->Filename); - else if (item->ItemClass==ItemClassContainer) - c->Message(0, " This item is a container with %i slots", item->BagSlots); - else { - c->Message(0, " equipableSlots: %u equipable Classes: %u", item->Slots, item->Classes); - c->Message(0, " Magic: %i SpellID: %i Proc Level: %i DBCharges: %i CurCharges: %i", item->Magic, item->Click.Effect, item->Click.Level, item->MaxCharges, inst->GetCharges()); - c->Message(0, " EffectType: 0x%02x CastTime: %.2f", (uint8) item->Click.Type, (double) item->CastTime/1000); - c->Message(0, " Material: 0x%02x Color: 0x%08x Skill: %i", item->Material, item->Color, item->ItemType); - c->Message(0, " Required level: %i Required skill: %i Recommended level:%i", item->ReqLevel, item->RecSkill, item->RecLevel); - c->Message(0, " Skill mod: %i percent: %i", item->SkillModType, item->SkillModValue); - c->Message(0, " BaneRace: %i BaneBody: %i BaneDMG: %i", item->BaneDmgRace, item->BaneDmgBody, item->BaneDmgAmt); - } + auto inst = c->GetInv()[MainCursor]; + if (!inst) { c->Message(13, "Error: You need an item on your cursor for this command"); } + auto item = inst->GetItem(); + if (!item) { + Log.Out(Logs::General, Logs::Inventory, "(%s) Command #iteminfo processed an item with no data pointer"); + c->Message(13, "Error: This item has no data reference"); } + + Client::TextLink linker; + linker.SetLinkType(linker.linkItemInst); + linker.SetItemInst(inst); + + auto item_link = linker.GenerateLink(); + + c->Message(0, "*** Item Info for [%s] ***", item_link.c_str()); + c->Message(0, ">> ID: %u, ItemUseType: %u, ItemClassType: %u", item->ID, item->ItemType, item->ItemClass); + c->Message(0, ">> IDFile: '%s', IconID: %u", item->IDFile, item->Icon); + c->Message(0, ">> Size: %u, Weight: %u, Price: %u, LDoNPrice: %u", item->Size, item->Weight, item->Price, item->LDoNPrice); + c->Message(0, ">> Material: 0x%02X, Color: 0x%08X, Tint: 0x%08X, Light: 0x%02X", item->Material, item->Color, inst->GetColor(), item->Light); + c->Message(0, ">> IsLore: %s, LoreGroup: %u, Lore: '%s'", (item->LoreFlag ? "TRUE" : "FALSE"), item->LoreGroup, item->Lore); + c->Message(0, ">> NoDrop: %u, NoRent: %u, NoPet: %u, NoTransfer: %u, FVNoDrop: %u", + item->NoDrop, item->NoRent, (uint8)item->NoPet, (uint8)item->NoTransfer, item->FVNoDrop); + + if (item->ItemClass == ItemClassBook) { + c->Message(0, "*** This item is a Book (filename:'%s') ***", item->Filename); + } + else if (item->ItemClass == ItemClassContainer) { + c->Message(0, "*** This item is a Container (%u slots) ***", item->BagSlots); + } + else { + c->Message(0, "*** This item is Common ***"); + c->Message(0, ">> Classes: %u, Races: %u, Slots: %u", item->Classes, item->Races, item->Slots); + c->Message(0, ">> ReqSkill: %u, ReqLevel: %u, RecLevel: %u", item->RecSkill, item->ReqLevel, item->RecLevel); + c->Message(0, ">> SkillModType: %u, SkillModValue: %i", item->SkillModType, item->SkillModValue); + c->Message(0, ">> BaneRaceType: %u, BaneRaceDamage: %u, BaneBodyType: %u, BaneBodyDamage: %i", + item->BaneDmgRace, item->BaneDmgRaceAmt, item->BaneDmgBody, item->BaneDmgAmt); + c->Message(0, ">> Magic: %s, SpellID: %i, ProcLevel: %u, Charges: %u, MaxCharges: %u", + (item->Magic ? "TRUE" : "FALSE"), item->Click.Effect, item->Click.Level, inst->GetCharges(), item->MaxCharges); + c->Message(0, ">> EffectType: 0x%02X, CastTime: %.2f", (uint8)item->Click.Type, ((double)item->CastTime / 1000)); + } + + if (c->Admin() >= 200) + c->Message(0, ">> MinStatus: %u", item->MinStatus); } void command_uptime(Client *c, const Seperator *sep) diff --git a/zone/common.h b/zone/common.h index 639eee91e..56ab6f819 100644 --- a/zone/common.h +++ b/zone/common.h @@ -136,7 +136,8 @@ enum { ALLOW_TO_TANK = 41, IGNORE_ROOT_AGGRO_RULES = 42, CASTING_RESIST_DIFF = 43, - MAX_SPECIAL_ATTACK = 44 + COUNTER_AVOID_DAMAGE = 44, + MAX_SPECIAL_ATTACK = 45 }; typedef enum { //fear states diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 0dd4ed1f3..9f91b584b 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -361,8 +361,8 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( database.QueryDatabase(ss.str().c_str()); } - auto start = client->GetInv().cursor_begin(); - auto finish = client->GetInv().cursor_end(); + auto start = client->GetInv().cursor_cbegin(); + auto finish = client->GetInv().cursor_cend(); database.SaveCursor(client->CharacterID(), start, finish); client->CalcBonuses(); diff --git a/zone/groups.h b/zone/groups.h index 71935bf81..7dcbd848b 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -73,7 +73,7 @@ public: void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = nullptr); inline void SetLeader(Mob* newleader){ leader=newleader; }; inline Mob* GetLeader() { return leader; }; - const char* GetLeaderName() { return leader->GetName(); }; + const char* GetLeaderName() { return membername[0]; }; void SendHPPacketsTo(Mob* newmember); void SendHPPacketsFrom(Mob* newmember); bool UpdatePlayer(Mob* update); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 1da2fb13d..69f1de882 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -177,16 +177,16 @@ uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) { } -bool Client::CheckLoreConflict(const Item_Struct* item) { - if (!item) - return false; - if (!(item->LoreFlag)) - return false; +bool Client::CheckLoreConflict(const Item_Struct* item) +{ + if (!item) { return false; } + if (!item->LoreFlag) { return false; } + if (item->LoreGroup == 0) { return false; } - if (item->LoreGroup == -1) // Standard lore items; look everywhere except the shared bank, return the result + if (item->LoreGroup == 0xFFFFFFFF) // Standard lore items; look everywhere except the shared bank, return the result return (m_inv.HasItem(item->ID, 0, ~invWhereSharedBank) != INVALID_INDEX); - //If the item has a lore group, we check for other items with the same group and return the result + // If the item has a lore group, we check for other items with the same group and return the result return (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != INVALID_INDEX); } @@ -619,7 +619,7 @@ void Client::DropItem(int16 slot_id) // Save client inventory change to database if (slot_id == MainCursor) { SendCursorBuffer(); - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(CharacterID(), s, e); } else { database.SaveInventory(CharacterID(), nullptr, slot_id); @@ -680,20 +680,37 @@ int32 Client::GetAugmentIDAt(int16 slot_id, uint8 augslot) { return INVALID_ID; } -void Client::SendCursorBuffer() { +void Client::SendCursorBuffer() +{ // Temporary work-around for the RoF+ Client Buffer // Instead of dealing with client moving items in cursor buffer, // we can just send the next item in the cursor buffer to the cursor. - if (GetClientVersion() >= ClientVersion::RoF) - { - if (!GetInv().CursorEmpty()) - { - const ItemInst* inst = GetInv().GetCursorItem(); - if (inst) - { - SendItemPacket(MainCursor, inst, ItemPacketSummonItem); - } - } + if (GetClientVersion() < ClientVersion::RoF) { return; } + if (GetInv().CursorEmpty()) { return; } + + auto test_inst = GetInv().GetCursorItem(); + if (test_inst == nullptr) { return; } + auto test_item = test_inst->GetItem(); + if (test_item == nullptr) { return; } + + bool lore_pass = true; + if (test_item->LoreGroup == 0xFFFFFFFF) { + lore_pass = (m_inv.HasItem(test_item->ID, 0, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); + } + else if (test_item->LoreGroup != 0) { + lore_pass = (m_inv.HasItemByLoreGroup(test_item->LoreGroup, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); + } + + if (!lore_pass) { + Log.Out(Logs::General, Logs::Inventory, "(%s) Duplicate lore items are not allowed - destroying item %s(id:%u) on cursor", + GetName(), test_item->Name, test_item->ID); + Message_StringID(MT_LootMessages, 290); + parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0); + DeleteItemInInventory(MainCursor); + SendCursorBuffer(); + } + else { + SendItemPacket(MainCursor, test_inst, ItemPacketSummonItem); } } @@ -772,7 +789,7 @@ void Client::DeleteItemInInventory(int16 slot_id, int8 quantity, bool client_upd const ItemInst* inst = nullptr; if (slot_id == MainCursor) { - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); if(update_db) database.SaveCursor(character_id, s, e); } @@ -826,7 +843,7 @@ bool Client::PushItemOnCursor(const ItemInst& inst, bool client_update) SendItemPacket(MainCursor, &inst, ItemPacketSummonItem); } - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); return database.SaveCursor(CharacterID(), s, e); } @@ -851,7 +868,7 @@ bool Client::PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client } if (slot_id == MainCursor) { - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); return database.SaveCursor(this->CharacterID(), s, e); } else { @@ -870,7 +887,7 @@ void Client::PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootI SendLootItemInPacket(&inst, slot_id); if (slot_id == MainCursor) { - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(this->CharacterID(), s, e); } else { @@ -1009,7 +1026,7 @@ void Client::MoveItemCharges(ItemInst &from, int16 to_slot, uint8 type) from.SetCharges(from.GetCharges() - charges_to_move); SendLootItemInPacket(tmp_inst, to_slot); if (to_slot == MainCursor) { - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(this->CharacterID(), s, e); } else { @@ -1320,10 +1337,33 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { return false; } - // This could be expounded upon at some point to let the server know that - // the client has moved a buffered cursor item onto the active cursor -U if (move_in->from_slot == move_in->to_slot) { // Item summon, no further processing needed if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit + if (GetClientVersion() >= ClientVersion::RoF) { return true; } // Can't do RoF+ + + if (move_in->to_slot == MainCursor) { + auto test_inst = m_inv.GetItem(MainCursor); + if (test_inst == nullptr) { return true; } + auto test_item = test_inst->GetItem(); + if (test_item == nullptr) { return true; } + if (!test_item->LoreFlag) { return true; } + + bool lore_pass = true; + if (test_item->LoreGroup == 0xFFFFFFFF) { + lore_pass = (m_inv.HasItem(test_item->ID, 0, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); + } + else if (test_item->LoreGroup != 0) { + lore_pass = (m_inv.HasItemByLoreGroup(test_item->LoreGroup, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); + } + + if (!lore_pass) { + Log.Out(Logs::General, Logs::Inventory, "(%s) Duplicate lore items are not allowed - destroying item %s(id:%u) on cursor", + GetName(), test_item->Name, test_item->ID); + Message_StringID(MT_LootMessages, 290); + parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0); + DeleteItemInInventory(MainCursor, 0, true); + } + } return true; } @@ -1567,7 +1607,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { { SendCursorBuffer(); } - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(character_id, s, e); } else @@ -1726,7 +1766,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { { SendCursorBuffer(); } - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(character_id, s, e); } else { @@ -1734,7 +1774,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } if (dst_slot_id == MainCursor) { - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(character_id, s, e); } else { @@ -2170,7 +2210,7 @@ void Client::RemoveNoRent(bool client_update) } local.clear(); - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(this->CharacterID(), s, e); } } @@ -2298,7 +2338,7 @@ void Client::RemoveDuplicateLore(bool client_update) } local_2.clear(); - auto s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); database.SaveCursor(this->CharacterID(), s, e); } } @@ -2826,9 +2866,9 @@ bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool } int limbo = 0; - for (auto cursor_itr = m_inv.cursor_begin(); cursor_itr != m_inv.cursor_end(); ++cursor_itr, ++limbo) { + for (auto cursor_itr = m_inv.cursor_cbegin(); cursor_itr != m_inv.cursor_cend(); ++cursor_itr, ++limbo) { // m_inv.cursor_begin() is referenced as MainCursor in MapPossessions above - if (cursor_itr == m_inv.cursor_begin()) + if (cursor_itr == m_inv.cursor_cbegin()) continue; instmap[8000 + limbo] = *cursor_itr; diff --git a/zone/merc.cpp b/zone/merc.cpp index 2fea4aba4..059ccb316 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -449,11 +449,11 @@ void Merc::AddItemBonuses(const Item_Struct *item, StatBonuses* newbon) { newbon->DSMitigation += item->DSMitigation; } if (item->Worn.Effect>0 && (item->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, true); + ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type); } if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects - ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0, true); + ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0); } switch(item->BardType) diff --git a/zone/mob.cpp b/zone/mob.cpp index e9cee33e4..ea86ff4dc 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -170,6 +170,7 @@ Mob::Mob(const char* in_name, findable = false; trackable = true; has_shieldequiped = false; + has_twohandbluntequiped = false; has_numhits = false; has_MGB = false; has_ProjectIllusion = false; diff --git a/zone/mob.h b/zone/mob.h index 46531b819..fb8148713 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -194,7 +194,7 @@ public: bool IsBeneficialAllowed(Mob *target); virtual int GetCasterLevel(uint16 spell_id); void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0, - bool item_bonus = false, bool IsWornEffect = false, uint32 ticsremaining = 0, int buffslot = -1, + uint8 WornType = 0, uint32 ticsremaining = 0, int buffslot = -1, bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0); void NegateSpellsBonuses(uint16 spell_id); virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false); @@ -308,7 +308,9 @@ public: void SetTargetable(bool on); bool IsTargetable() const { return m_targetable; } bool HasShieldEquiped() const { return has_shieldequiped; } - inline void ShieldEquiped(bool val) { has_shieldequiped = val; } + inline void SetShieldEquiped(bool val) { has_shieldequiped = val; } + bool HasTwoHandBluntEquiped() const { return has_twohandbluntequiped; } + inline void SetTwoHandBluntEquiped(bool val) { has_twohandbluntequiped = val; } virtual uint16 GetSkill(SkillUseTypes skill_num) const { return 0; } virtual uint32 GetEquipment(uint8 material_slot) const { return(0); } virtual int32 GetEquipmentMaterial(uint8 material_slot) const; @@ -1150,6 +1152,7 @@ protected: uint16 viral_spells[MAX_SPELL_TRIGGER*2]; // Stores the spell ids of the viruses on target and caster ids bool offhand; bool has_shieldequiped; + bool has_twohandbluntequiped; bool has_numhits; bool has_MGB; bool has_ProjectIllusion; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 371a8e523..452ca71fd 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -2559,7 +2559,7 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon) for(int i=0; i < AIspellsEffects.size(); i++) { - ApplySpellsBonuses(0, 0, newbon, 0, false, 0,-1, + ApplySpellsBonuses(0, 0, newbon, 0, 0, 0,-1, true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max); } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 6c181bf1e..7dcb4e457 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -297,7 +297,7 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id) } } - database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), 0); + database.UpdateRespawnTime(spawn2_id, zone->GetInstanceID(), 0); found_spawn->SetCurrentNPCID(npcid); auto position = glm::vec4(found_spawn->GetX(), found_spawn->GetY(), found_spawn->GetZ(), found_spawn->GetHeading()); @@ -2388,7 +2388,7 @@ void QuestManager::UpdateSpawnTimer(uint32 id, uint32 newTime) { bool found = false; - database.UpdateSpawn2Timeleft(id, 0, (newTime/1000)); + database.UpdateRespawnTime(id, 0, (newTime/1000)); LinkedListIterator iterator(zone->spawn2_list); iterator.Reset(); while (iterator.MoreElements()) diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index f3440f127..068774091 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -214,9 +214,6 @@ bool Spawn2::Process() { if(IsDespawned) return true; - if(spawn2_id) - database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), 0); - currentnpcid = npcid; NPC* npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), FlyMode3); @@ -348,7 +345,7 @@ void Spawn2::DeathReset(bool realdeath) //if we have a valid spawn id if(spawn2_id) { - database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), (cur/1000)); + database.UpdateRespawnTime(spawn2_id, zone->GetInstanceID(), (cur/1000)); Log.Out(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn reset by death, repop in %d ms", spawn2_id, timer.GetRemainingTime()); //store it to database too } @@ -356,28 +353,92 @@ void Spawn2::DeathReset(bool realdeath) bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList &spawn2_list, int16 version, uint32 repopdelay) { + std::unordered_map spawn_times; + + timeval tv; + gettimeofday(&tv, nullptr); + + std::string spawn_query = StringFormat( + "SELECT " + "respawn_times.id, " + "respawn_times.`start`, " + "respawn_times.duration " + "FROM " + "respawn_times " + "WHERE instance_id = %u", + zone->GetInstanceID() + ); + auto results = QueryDatabase(spawn_query); + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 start_duration = atoi(row[1]) > 0 ? atoi(row[1]) : 0; + uint32 end_duration = atoi(row[2]) > 0 ? atoi(row[2]) : 0; + + /* Our current time was expired */ + if ((start_duration + end_duration) <= tv.tv_sec) { + spawn_times[atoi(row[0])] = 0; + } + /* We still have time left on this timer */ + else { + spawn_times[atoi(row[0])] = ((start_duration + end_duration) - tv.tv_sec) * 1000; + } + } + const char *zone_name = database.GetZoneName(zoneid); - std::string query = StringFormat("SELECT id, spawngroupID, x, y, z, heading, " - "respawntime, variance, pathgrid, _condition, " - "cond_value, enabled, animation FROM spawn2 " - "WHERE zone = '%s' AND version = %u", - zone_name, version); - auto results = QueryDatabase(query); - if (!results.Success()) { + std::string query = StringFormat( + "SELECT " + "id, " + "spawngroupID, " + "x, " + "y, " + "z, " + "heading, " + "respawntime, " + "variance, " + "pathgrid, " + "_condition, " + "cond_value, " + "enabled, " + "animation " + "FROM " + "spawn2 " + "WHERE zone = '%s' AND version = %u", + zone_name, + version + ); + results = QueryDatabase(query); + + if (!results.Success()) { return false; - } + } - for (auto row = results.begin(); row != results.end(); ++row) { - Spawn2* newSpawn = 0; + for (auto row = results.begin(); row != results.end(); ++row) { - bool perl_enabled = atoi(row[11]) == 1? true: false; - uint32 spawnLeft = (GetSpawnTimeLeft(atoi(row[0]), zone->GetInstanceID()) * 1000); - newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]), - atof(row[5]), atoi(row[6]), atoi(row[7]), spawnLeft, atoi(row[8]), - atoi(row[9]), atoi(row[10]), perl_enabled, (EmuAppearance)atoi(row[12])); + uint32 spawn_time_left = 0; + Spawn2* new_spawn = 0; + bool perl_enabled = atoi(row[11]) == 1 ? true : false; - spawn2_list.Insert(newSpawn); - } + if (spawn_times.count(atoi(row[0])) != 0) + spawn_time_left = spawn_times[atoi(row[0])]; + + new_spawn = new Spawn2( // + atoi(row[0]), // uint32 in_spawn2_id + atoi(row[1]), // uint32 spawngroup_id + atof(row[2]), // float in_x + atof(row[3]), // float in_y + atof(row[4]), // float in_z + atof(row[5]), // float in_heading + atoi(row[6]), // uint32 respawn + atoi(row[7]), // uint32 variance + spawn_time_left, // uint32 timeleft + atoi(row[8]), // uint32 grid + atoi(row[9]), // uint16 in_cond_id + atoi(row[10]), // int16 in_min_value + perl_enabled, // bool in_enabled + (EmuAppearance)atoi(row[12]) // EmuAppearance anim + ); + + spawn2_list.Insert(new_spawn); + } return true; } @@ -427,8 +488,6 @@ bool ZoneDatabase::CreateSpawn2(Client *client, uint32 spawngroup, const char* z if (results.RowsAffected() != 1) return false; - if(client) - return true; } diff --git a/zone/trading.cpp b/zone/trading.cpp index 4d30691f5..4874716ae 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1479,8 +1479,6 @@ static void BazaarAuditTrail(const char *seller, const char *buyer, const char * database.QueryDatabase(query); } - - void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicationPacket* app){ if(!Trader) return; @@ -1509,15 +1507,15 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat BuyItem->GetItem()->Name, BuyItem->IsStackable(), tbs->Quantity, BuyItem->GetCharges()); // If the item is not stackable, then we can only be buying one of them. if(!BuyItem->IsStackable()) - outtbs->Quantity = tbs->Quantity; + outtbs->Quantity = 1; // normally you can't send more than 1 here else { // Stackable items, arrows, diamonds, etc - int ItemCharges = BuyItem->GetCharges(); + int32 ItemCharges = BuyItem->GetCharges(); // ItemCharges for stackables should not be <= 0 if(ItemCharges <= 0) outtbs->Quantity = 1; // If the purchaser requested more than is in the stack, just sell them how many are actually in the stack. - else if(ItemCharges < (int16)tbs->Quantity) + else if(static_cast(ItemCharges) < tbs->Quantity) outtbs->Quantity = ItemCharges; else outtbs->Quantity = tbs->Quantity; @@ -1609,7 +1607,6 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat safe_delete(outapp); safe_delete(outapp2); - } void Client::SendBazaarWelcome() diff --git a/zone/zone.cpp b/zone/zone.cpp index 444e217da..e79a9f9af 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -931,6 +931,9 @@ bool Zone::Init(bool iStaticZone) { Log.Out(Logs::General, Logs::Error, "Loading World Objects failed. continuing."); } + Log.Out(Logs::General, Logs::Status, "Flushing old respawn timers..."); + database.QueryDatabase("DELETE FROM `respawn_times` WHERE (`start` + `duration`) < UNIX_TIMESTAMP(NOW())"); + //load up the zone's doors (prints inside) zone->LoadZoneDoors(zone->GetShortName(), zone->GetInstanceVersion()); zone->LoadBlockedSpells(zone->GetZoneID()); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 07fe67679..6cb15d16e 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -185,30 +185,40 @@ bool ZoneDatabase::GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct return true; } -//updates or clears the respawn time in the database for the current spawn id -void ZoneDatabase::UpdateSpawn2Timeleft(uint32 id, uint16 instance_id, uint32 timeleft) +void ZoneDatabase::UpdateRespawnTime(uint32 spawn2_id, uint16 instance_id, uint32 time_left) { + timeval tv; gettimeofday(&tv, nullptr); - uint32 cur = tv.tv_sec; + uint32 current_time = tv.tv_sec; - //if we pass timeleft as 0 that means we clear from respawn time - //otherwise we update with a REPLACE INTO - if(timeleft == 0) { - std::string query = StringFormat("DELETE FROM respawn_times WHERE id=%lu " - "AND instance_id = %lu",(unsigned long)id, (unsigned long)instance_id); - auto results = QueryDatabase(query); - if (!results.Success()) + /* If we pass timeleft as 0 that means we clear from respawn time + otherwise we update with a REPLACE INTO + */ + if(time_left == 0) { + std::string query = StringFormat("DELETE FROM `respawn_times` WHERE `id` = %u AND `instance_id` = %u", spawn2_id, instance_id); + QueryDatabase(query); return; } - std::string query = StringFormat("REPLACE INTO respawn_times (id, start, duration, instance_id) " - "VALUES (%lu, %lu, %lu, %lu)", - (unsigned long)id, (unsigned long)cur, - (unsigned long)timeleft, (unsigned long)instance_id); - auto results = QueryDatabase(query); - if (!results.Success()) + std::string query = StringFormat( + "REPLACE INTO `respawn_times` " + "(id, " + "start, " + "duration, " + "instance_id) " + "VALUES " + "(%u, " + "%u, " + "%u, " + "%u)", + spawn2_id, + current_time, + time_left, + instance_id + ); + QueryDatabase(query); return; } @@ -3246,7 +3256,7 @@ bool ZoneDatabase::LoadFactionData() auto row = results.begin(); - max_faction = atoi(row[0]); + max_faction = row[0] ? atoi(row[0]) : 0; faction_array = new Faction*[max_faction+1]; for(unsigned int index=0; index &spawn2_list, int16 version, uint32 repopdelay = 0); Spawn2* LoadSpawn2(LinkedList &spawn2_list, uint32 spawn2id, uint32 timeleft); bool CreateSpawn2(Client *c, uint32 spawngroup, const char* zone, const glm::vec4& position, uint32 respawn, uint32 variance, uint16 condition, int16 cond_value); - void UpdateSpawn2Timeleft(uint32 id, uint16 instance_id,uint32 timeleft); + void UpdateRespawnTime(uint32 id, uint16 instance_id,uint32 timeleft); uint32 GetSpawnTimeLeft(uint32 id, uint16 instance_id); void UpdateSpawn2Status(uint32 id, uint8 new_status);