diff --git a/changelog.txt b/changelog.txt index cdb2fd5f8..bc96a8ef1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 06/07/2015 == +Uleat: Implemented optional rule for using disenchanted bags. Action triggers at the same point that temporary items are removed. +Optional SQL: utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql == 05/25/2015 == Akkadius: Implemented disjointed zone based time, this can be triggered via quest methods diff --git a/common/ruletypes.h b/common/ruletypes.h index cf77dddf6..9a36b77f4 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -345,6 +345,7 @@ RULE_BOOL ( Spells, SwarmPetTargetLock, false) // Use old method of swarm pets t RULE_BOOL ( Spells, NPC_UseFocusFromSpells, true) // Allow npcs to use most spell derived focus effects. RULE_BOOL ( Spells, NPC_UseFocusFromItems, false) // Allow npcs to use most item derived focus effects. RULE_BOOL ( Spells, UseAdditiveFocusFromWornSlot, false) // Allows an additive focus effect to be calculated from worn slot. +RULE_BOOL ( Spells, AlwaysSendTargetsBuffs, false) // ignore LAA level if true RULE_CATEGORY_END() RULE_CATEGORY( Combat ) @@ -611,6 +612,7 @@ RULE_BOOL ( Inventory, EnforceAugmentUsability, true) // Forces augmented item u RULE_BOOL ( Inventory, EnforceAugmentWear, true) // Forces augment wear slot validation RULE_BOOL ( Inventory, DeleteTransformationMold, true) //False if you want mold to last forever RULE_BOOL ( Inventory, AllowAnyWeaponTransformation, false) //Weapons can use any weapon transformation +RULE_BOOL ( Inventory, TransformSummonedBags, false) //Transforms summoned bags into disenchanted ones instead of deleting RULE_CATEGORY_END() RULE_CATEGORY( Client ) diff --git a/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql b/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql index e89d4a437..25ec3734d 100644 --- a/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql +++ b/utils/sql/git/optional/2014_03_17_EnforceAugmentRules.sql @@ -1,3 +1,3 @@ INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentRestriction', 'false', 'Forces augment slot restrictions.'); INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentUsability', 'false', 'Forces augmented item usability.'); -INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentWear', 'false', 'Forces augment wear slot validation.'); \ No newline at end of file +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:EnforceAugmentWear', 'false', 'Forces augment wear slot validation.'); diff --git a/utils/sql/git/optional/2015_06_07_SpellsTargetBuffsRule.sql b/utils/sql/git/optional/2015_06_07_SpellsTargetBuffsRule.sql new file mode 100644 index 000000000..1dcc9d922 --- /dev/null +++ b/utils/sql/git/optional/2015_06_07_SpellsTargetBuffsRule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AlwaysSendTargetBuffs', 'false', 'Allows the server to send the targets buffs ignoring the LAA.'); diff --git a/utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql b/utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql new file mode 100644 index 000000000..d0af46a24 --- /dev/null +++ b/utils/sql/git/optional/2015_06_07_TransformSummonedBagsRule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Inventory:TransformSummonedBags', 'true', 'Transforms summoned bags into disenchanted ones instead of deleting.'); diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 4340ff6da..a68945155 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -20,7 +20,7 @@ SET(zone_sources embparser_api.cpp embperl.cpp embxs.cpp - encounter.cpp + encounter.cpp entity.cpp exp.cpp fearpath.cpp @@ -36,6 +36,7 @@ SET(zone_sources lua_corpse.cpp lua_client.cpp lua_door.cpp + lua_encounter.cpp lua_entity.cpp lua_entity_list.cpp lua_general.cpp @@ -139,7 +140,7 @@ SET(zone_headers embparser.h embperl.h embxs.h - encounter.h + encounter.h entity.h errmsg.h event_codes.h @@ -151,6 +152,7 @@ SET(zone_headers lua_bit.h lua_client.h lua_corpse.h + lua_encounter.h lua_entity.h lua_entity_list.h lua_general.h diff --git a/zone/attack.cpp b/zone/attack.cpp index 666cb19f8..a229f197e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3693,7 +3693,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons //send an HP update if we are hurt if(GetHP() < GetMaxHP()) - SendHPUpdate(); + SendHPUpdate(!iBuffTic); // the OP_Damage actually updates the client in these cases, so we skill them } //end `if damage was done` //send damage packet... diff --git a/zone/bot.cpp b/zone/bot.cpp index e76389b24..8b3741443 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -755,7 +755,7 @@ void Bot::GenerateBaseStats() { this->Corrup = CorruptionResist; SetBotSpellID(BotSpellID); this->size = BotSize; - + this->pAggroRange = 0; this->pAssistRange = 0; this->raid_target = false; @@ -1381,18 +1381,18 @@ int32 Bot::GenerateBaseHitPoints() uint32 lm = GetClassLevelFactor(); int32 Post255; int32 NormalSTA = GetSTA(); - + if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { float SoDPost255; - + if(((NormalSTA - 255) / 2) > 0) SoDPost255 = ((NormalSTA - 255) / 2); else SoDPost255 = 0; - + int hp_factor = GetClassHPFactor(); - + if(level < 41) { new_base_hp = (5 + (GetLevel() * hp_factor / 12) + ((NormalSTA - SoDPost255) * GetLevel() * hp_factor / 3600)); @@ -1420,7 +1420,7 @@ int32 Bot::GenerateBaseHitPoints() new_base_hp = (5)+(GetLevel()*lm/10) + (((NormalSTA-Post255)*GetLevel()*lm/3000)) + ((Post255*1)*lm/6000); } this->base_hp = new_base_hp; - + return new_base_hp; } @@ -2342,7 +2342,7 @@ bool Bot::IsBotNameAvailable(char *botName, std::string* errorMessage) { if (results.RowCount()) { //Name already in use! return false; } - + return true; //We made it with a valid name! } @@ -2901,7 +2901,7 @@ bool Bot::Process() SetEndurance(GetEndurance() + CalcEnduranceRegen() + RestRegenEndurance); } - if (sendhpupdate_timer.Check()) { + if (sendhpupdate_timer.Check(false)) { SendHPUpdate(); if(HasPet()) @@ -5894,7 +5894,7 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, SkillUseTypes att // delete from group data RemoveBotFromGroup(this, g); - + //Make sure group still exists if it doesnt they were already updated in RemoveBotFromGroup g = GetGroup(); if (!g) @@ -5912,7 +5912,7 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, SkillUseTypes att g->members[j] = nullptr; } } - + // update the client group EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); GroupJoin_Struct* gu = (GroupJoin_Struct*)outapp->pBuffer; @@ -10823,7 +10823,7 @@ void Bot::CalcItemBonuses(StatBonuses* newbon) AddItemBonuses(item, newbon); } } - + // Caps if(newbon->HPRegen > CalcHPRegenCap()) newbon->HPRegen = CalcHPRegenCap(); diff --git a/zone/client.cpp b/zone/client.cpp index 7471a6a62..bdcbbbf68 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -116,7 +116,7 @@ Client::Client(EQStreamInterface* ieqs) ), //these must be listed in the order they appear in client.h position_timer(250), - hpupdate_timer(1800), + hpupdate_timer(2000), camp_timer(29000), process_timer(100), stamina_timer(40000), @@ -8631,4 +8631,4 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold, QueuePacket(outapp, false, Client::CLIENT_CONNECTED); safe_delete(outapp); -} \ No newline at end of file +} diff --git a/zone/client.h b/zone/client.h index 73bde1a69..de093990c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -906,6 +906,7 @@ public: bool DecreaseByID(uint32 type, uint8 amt); uint8 SlotConvert2(uint8 slot); //Maybe not needed. void Escape(); //AA Escape + void DisenchantSummonedBags(bool client_update = true); void RemoveNoRent(bool client_update = true); void RemoveDuplicateLore(bool client_update = true); void MoveSlotNotAllowed(bool client_update = true); @@ -1257,6 +1258,8 @@ public: int32 GetMeleeDamage(Mob* other, bool GetMinDamage = false); void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false); + + void ResetHPUpdateTimer() { hpupdate_timer.Start(); } protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 112e8f33f..dbc37aa7f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12899,7 +12899,7 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) inspect_buffs = group->GetLeadershipAA(groupAAInspectBuffs); } } - if (nt == this || inspect_buffs || (nt->IsClient() && !nt->CastToClient()->GetPVP()) || + if (GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs) || nt == this || inspect_buffs || (nt->IsClient() && !nt->CastToClient()->GetPVP()) || (nt->IsPet() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) || (nt->IsMerc() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP())) nt->SendBuffsToClient(this); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 42681340b..4a16e7698 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -129,7 +129,9 @@ bool Client::Process() { if(IsTracking() && (GetClientVersion() >= ClientVersion::SoD) && TrackingTimer.Check()) DoTracking(); - if(hpupdate_timer.Check()) + // SendHPUpdate calls hpupdate_timer.Start so it can delay this timer, so lets not reset with the check + // since the function will anyways + if(hpupdate_timer.Check(false)) SendHPUpdate(); if(mana_timer.Check()) @@ -836,7 +838,11 @@ void Client::BulkSendInventoryItems() { } bool deletenorent = database.NoRentExpired(GetName()); - if(deletenorent){ RemoveNoRent(false); } //client was offline for more than 30 minutes, delete no rent items + if (deletenorent) { //client was offline for more than 30 minutes, delete no rent items + if (RuleB(Inventory, TransformSummonedBags)) + DisenchantSummonedBags(false); + RemoveNoRent(false); + } RemoveDuplicateLore(false); MoveSlotNotAllowed(false); diff --git a/zone/entity.cpp b/zone/entity.cpp index a73b21f4d..5033c52fe 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -78,7 +78,7 @@ Client *Entity::CastToClient() } #ifdef _EQDEBUG if (!IsClient()) { - Log.Out(Logs::General, Logs::Error, "CastToClient error (not client)"); + Log.Out(Logs::General, Logs::Error, "CastToClient error (not client)"); return 0; } #endif @@ -267,7 +267,7 @@ const Beacon* Entity::CastToBeacon() const return static_cast(this); } -const Encounter* Entity::CastToEncounter() const +const Encounter* Entity::CastToEncounter() const { return static_cast(this); } @@ -564,7 +564,7 @@ void EntityList::AddGroup(Group *group) uint32 gid = worldserver.NextGroupID(); if (gid == 0) { - Log.Out(Logs::General, Logs::Error, + Log.Out(Logs::General, Logs::Error, "Unable to get new group ID from world server. group is going to be broken."); return; } @@ -593,7 +593,7 @@ void EntityList::AddRaid(Raid *raid) uint32 gid = worldserver.NextGroupID(); if (gid == 0) { - Log.Out(Logs::General, Logs::Error, + Log.Out(Logs::General, Logs::Error, "Unable to get new group ID from world server. group is going to be broken."); return; } @@ -1427,7 +1427,9 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap if (c != sender) { if (Target == sender) { if (inspect_buffs) { // if inspect_buffs is true we're sending a mob's buffs to those with the LAA - if (c->IsRaidGrouped()) { + if (c->GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs)) { + Send = true; + } else if (c->IsRaidGrouped()) { Raid *raid = c->GetRaid(); if (!raid) continue; diff --git a/zone/exp.cpp b/zone/exp.cpp index ab8506aca..826b12e67 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -705,7 +705,7 @@ void Group::SplitExp(uint32 exp, Mob* other) { groupmod = 2.16; else groupmod = 1.0; - if(membercount > 1 && membercount < 6) + if(membercount > 1 && membercount <= 6) groupexp += (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier))); int conlevel = Mob::GetLevelCon(maxlevel, other->GetLevel()); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 2ce30fe5e..e1904d59f 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -2158,6 +2158,187 @@ bool Client::DecreaseByID(uint32 type, uint8 amt) { return true; } +static bool IsSummonedBagID(uint32 item_id) +{ + switch (item_id) { + case 17147: // "Spiritual Prismatic Pack" + case 17303: // "Spirit Pouch" + case 17304: // "Dimensional Pocket" + case 17305: // "Dimensional Hole" + case 17306: // "Glowing Backpack" + case 17307: // "Quiver of Marr" + case 17308: // "Bandoleer of Luclin" + case 17309: // "Pouch of Quellious" + case 17310: // "Phantom Satchel" + case 17510: // "Glowing Chest" + case 17900: // "Grandmaster's Satchel" + case 57260: // "Glowing Backpack" + case 57261: // "Pouch of Quellious" + case 57262: // "Phantom Satchel" + case 60224: // "Faded-Glyph Tablet" + case 95199: // "Beginner Artisan Satchel" + case 95200: // "Apprentice Artisan Satchel" + case 95201: // "Freshman Artisan Satchel" + case 95202: // "Journeyman Artisan Satchel" + case 95203: // "Expert Artisan Satchel" + case 95204: // "Master Artisan Satchel" + //case 96960: // "Artisan Satchel" - no 12-slot disenchanted bags + return true; + default: + return false; + } +} + +static uint32 GetDisenchantedBagID(uint8 bag_slots) +{ + switch (bag_slots) { + case 4: + return 77772; // "Small Disenchanted Backpack" + case 6: + return 77774; // "Disenchanted Backpack" + case 8: + return 77776; // "Large Disenchanted Backpack" + case 10: + return 77778; // "Huge Disenchanted Backpack" + default: + return 0; // no suitable conversions + } +} + +static bool CopyBagContents(ItemInst* new_bag, const ItemInst* old_bag) +{ + if (!new_bag || !old_bag) { return false; } + if (new_bag->GetItem()->BagSlots < old_bag->GetItem()->BagSlots) { return false; } + + // pre-check for size comparisons + for (auto bag_slot = 0; bag_slot < old_bag->GetItem()->BagSlots; ++bag_slot) { + if (!old_bag->GetItem(bag_slot)) { continue; } + if (old_bag->GetItem(bag_slot)->GetItem()->Size > new_bag->GetItem()->BagSize) { + Log.Out(Logs::General, Logs::Inventory, "Copy Bag Contents: Failure due to %s is larger than size capacity of %s (%i > %i)", + old_bag->GetItem(bag_slot)->GetItem()->Name, new_bag->GetItem()->Name, old_bag->GetItem(bag_slot)->GetItem()->Size, new_bag->GetItem()->BagSize); + return false; + } + } + + for (auto bag_slot = 0; bag_slot < old_bag->GetItem()->BagSlots; ++bag_slot) { + if (!old_bag->GetItem(bag_slot)) { continue; } + new_bag->PutItem(bag_slot, *(old_bag->GetItem(bag_slot))); + } + + return true; +} + +void Client::DisenchantSummonedBags(bool client_update) +{ + for (auto slot_id = EmuConstants::GENERAL_BEGIN; slot_id <= EmuConstants::GENERAL_END; ++slot_id) { + auto inst = m_inv[slot_id]; + if (!inst) { continue; } + if (!IsSummonedBagID(inst->GetItem()->ID)) { continue; } + if (inst->GetItem()->ItemClass != ItemClassContainer) { continue; } + if (inst->GetTotalItemCount() == 1) { continue; } + + auto new_id = GetDisenchantedBagID(inst->GetItem()->BagSlots); + if (!new_id) { continue; } + auto new_item = database.GetItem(new_id); + if (!new_item) { continue; } + auto new_inst = database.CreateBaseItem(new_item); + if (!new_inst) { continue; } + + if (CopyBagContents(new_inst, inst)) { + Log.Out(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id); + PutItemInInventory(slot_id, *new_inst, client_update); + } + safe_delete(new_inst); + } + + for (auto slot_id = EmuConstants::BANK_BEGIN; slot_id <= EmuConstants::BANK_END; ++slot_id) { + auto inst = m_inv[slot_id]; + if (!inst) { continue; } + if (!IsSummonedBagID(inst->GetItem()->ID)) { continue; } + if (inst->GetItem()->ItemClass != ItemClassContainer) { continue; } + if (inst->GetTotalItemCount() == 1) { continue; } + + auto new_id = GetDisenchantedBagID(inst->GetItem()->BagSlots); + if (!new_id) { continue; } + auto new_item = database.GetItem(new_id); + if (!new_item) { continue; } + auto new_inst = database.CreateBaseItem(new_item); + if (!new_inst) { continue; } + + if (CopyBagContents(new_inst, inst)) { + Log.Out(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id); + PutItemInInventory(slot_id, *new_inst, client_update); + } + safe_delete(new_inst); + } + + for (auto slot_id = EmuConstants::SHARED_BANK_BEGIN; slot_id <= EmuConstants::SHARED_BANK_END; ++slot_id) { + auto inst = m_inv[slot_id]; + if (!inst) { continue; } + if (!IsSummonedBagID(inst->GetItem()->ID)) { continue; } + if (inst->GetItem()->ItemClass != ItemClassContainer) { continue; } + if (inst->GetTotalItemCount() == 1) { continue; } + + auto new_id = GetDisenchantedBagID(inst->GetItem()->BagSlots); + if (!new_id) { continue; } + auto new_item = database.GetItem(new_id); + if (!new_item) { continue; } + auto new_inst = database.CreateBaseItem(new_item); + if (!new_inst) { continue; } + + if (CopyBagContents(new_inst, inst)) { + Log.Out(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, slot_id); + PutItemInInventory(slot_id, *new_inst, client_update); + } + safe_delete(new_inst); + } + + while (!m_inv.CursorEmpty()) { + auto inst = m_inv[MainCursor]; + if (!inst) { break; } + if (!IsSummonedBagID(inst->GetItem()->ID)) { break; } + if (inst->GetItem()->ItemClass != ItemClassContainer) { break; } + if (inst->GetTotalItemCount() == 1) { break; } + + auto new_id = GetDisenchantedBagID(inst->GetItem()->BagSlots); + if (!new_id) { break; } + auto new_item = database.GetItem(new_id); + if (!new_item) { break; } + auto new_inst = database.CreateBaseItem(new_item); + if (!new_inst) { break; } + + if (CopyBagContents(new_inst, inst)) { + Log.Out(Logs::General, Logs::Inventory, "Disenchant Summoned Bags: Replacing %s with %s in slot %i", inst->GetItem()->Name, new_inst->GetItem()->Name, MainCursor); + std::list local; + local.push_front(new_inst); + m_inv.PopItem(MainCursor); + safe_delete(inst); + + while (!m_inv.CursorEmpty()) { + auto limbo_inst = m_inv.PopItem(MainCursor); + if (limbo_inst == nullptr) { continue; } + local.push_back(limbo_inst); + } + + for (auto iter = local.begin(); iter != local.end(); ++iter) { + auto cur_inst = *iter; + if (cur_inst == nullptr) { continue; } + m_inv.PushCursor(*cur_inst); + safe_delete(cur_inst); + } + local.clear(); + + auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); + database.SaveCursor(this->CharacterID(), s, e); + } + else { + safe_delete(new_inst); // deletes disenchanted bag if not used + } + + break; + } +} + void Client::RemoveNoRent(bool client_update) { for (auto slot_id = EmuConstants::EQUIPMENT_BEGIN; slot_id <= EmuConstants::EQUIPMENT_END; ++slot_id) { diff --git a/zone/lua_encounter.cpp b/zone/lua_encounter.cpp new file mode 100644 index 000000000..08a4f95d3 --- /dev/null +++ b/zone/lua_encounter.cpp @@ -0,0 +1,13 @@ +#ifdef LUA_EQEMU + +#include "lua.hpp" +#include +#include "lua_encounter.h" +#include "encounter.h" + + +luabind::scope lua_register_encounter() { + return luabind::class_("Encounter"); +} + +#endif \ No newline at end of file diff --git a/zone/lua_encounter.h b/zone/lua_encounter.h new file mode 100644 index 000000000..d4ce63bb9 --- /dev/null +++ b/zone/lua_encounter.h @@ -0,0 +1,30 @@ +#ifndef EQEMU_LUA_ENCOUNTER_H +#define EQEMU_LUA_ENCOUNTER_H +#ifdef LUA_EQEMU + +#include "lua_ptr.h" + +class Encounter; + +namespace luabind { + struct scope; + class object; +} + +luabind::scope lua_register_encounter(); + +class Lua_Encounter : public Lua_Ptr +{ + typedef Encounter NativeType; +public: + Lua_Encounter() { SetLuaPtrData(nullptr); } + Lua_Encounter(Encounter *d) { SetLuaPtrData(reinterpret_cast(d)); } + virtual ~Lua_Encounter() { } + + operator Encounter*() { + return reinterpret_cast(GetLuaPtrData()); + } + +}; +#endif +#endif \ No newline at end of file diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 139951724..bf65fea7c 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -19,6 +19,7 @@ #include "../common/timer.h" #include "../common/eqemu_logsys.h" #include "encounter.h" +#include "lua_encounter.h" struct Events { }; struct Factions { }; @@ -296,6 +297,10 @@ void lua_set_timer(const char *timer, int time_ms, Lua_Mob mob) { quest_manager.settimerMS(timer, time_ms, mob); } +void lua_set_timer(const char *timer, int time_ms, Lua_Encounter enc) { + quest_manager.settimerMS(timer, time_ms, enc); +} + void lua_stop_timer(const char *timer) { quest_manager.stoptimer(timer); } @@ -308,6 +313,10 @@ void lua_stop_timer(const char *timer, Lua_Mob mob) { quest_manager.stoptimer(timer, mob); } +void lua_stop_timer(const char *timer, Lua_Encounter enc) { + quest_manager.stoptimer(timer, enc); +} + void lua_stop_all_timers() { quest_manager.stopalltimers(); } @@ -320,6 +329,10 @@ void lua_stop_all_timers(Lua_Mob mob) { quest_manager.stopalltimers(mob); } +void lua_stop_all_timers(Lua_Encounter enc) { + quest_manager.stopalltimers(enc); +} + void lua_depop() { quest_manager.depop(0); } @@ -1457,12 +1470,15 @@ luabind::scope lua_register_general() { luabind::def("set_timer", (void(*)(const char*, int))&lua_set_timer), luabind::def("set_timer", (void(*)(const char*, int, Lua_ItemInst))&lua_set_timer), luabind::def("set_timer", (void(*)(const char*, int, Lua_Mob))&lua_set_timer), + luabind::def("set_timer", (void(*)(const char*, int, Lua_Encounter))&lua_set_timer), luabind::def("stop_timer", (void(*)(const char*))&lua_stop_timer), luabind::def("stop_timer", (void(*)(const char*, Lua_ItemInst))&lua_stop_timer), luabind::def("stop_timer", (void(*)(const char*, Lua_Mob))&lua_stop_timer), + luabind::def("stop_timer", (void(*)(const char*, Lua_Encounter))&lua_stop_timer), luabind::def("stop_all_timers", (void(*)(void))&lua_stop_all_timers), luabind::def("stop_all_timers", (void(*)(Lua_ItemInst))&lua_stop_all_timers), luabind::def("stop_all_timers", (void(*)(Lua_Mob))&lua_stop_all_timers), + luabind::def("stop_all_timers", (void(*)(Lua_Encounter))&lua_stop_all_timers), luabind::def("depop", (void(*)(void))&lua_depop), luabind::def("depop", (void(*)(int))&lua_depop), luabind::def("depop_with_timer", (void(*)(void))&lua_depop_with_timer), diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 4b5a1faad..24f25948e 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -34,6 +34,7 @@ #include "questmgr.h" #include "zone.h" #include "lua_parser.h" +#include "lua_encounter.h" const char *LuaEvents[_LargestEventID] = { "event_say", @@ -610,10 +611,11 @@ int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std:: lua_pushstring(L, encounter_name.c_str()); lua_setfield(L, -2, "name"); - auto arg_function = EncounterArgumentDispatch[evt]; - arg_function(this, L, data, extra_data, extra_pointers); - Encounter *enc = lua_encounters[encounter_name]; + + auto arg_function = EncounterArgumentDispatch[evt]; + arg_function(this, L, enc, data, extra_data, extra_pointers); + quest_manager.StartQuest(enc, nullptr, nullptr, encounter_name); if(lua_pcall(L, 1, 1, 0)) { std::string error = lua_tostring(L, -1); @@ -977,6 +979,7 @@ void LuaParser::MapFunctions(lua_State *L) { lua_register_client_version(), lua_register_appearance(), lua_register_entity(), + lua_register_encounter(), lua_register_mob(), lua_register_special_abilities(), lua_register_npc(), diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 5428304a6..840bcb40d 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -22,6 +22,7 @@ #include "lua_door.h" #include "lua_object.h" #include "lua_packet.h" +#include "lua_encounter.h" #include "zone.h" #include "lua_parser_events.h" @@ -704,14 +705,20 @@ void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* cl std::vector *extra_pointers) { } -void handle_encounter_timer(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_timer(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers) { lua_pushstring(L, data.c_str()); lua_setfield(L, -2, "timer"); } -void handle_encounter_load(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_load(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers) { + if (encounter) { + Lua_Encounter l_enc(encounter); + luabind::adl::object l_enc_o = luabind::adl::object(L, l_enc); + l_enc_o.push(L); + lua_setfield(L, -2, "encounter"); + } if (extra_pointers) { std::string *str = EQEmu::any_cast(extra_pointers->at(0)); lua_pushstring(L, str->c_str()); @@ -719,7 +726,7 @@ void handle_encounter_load(QuestInterface *parse, lua_State* L, std::string data } } -void handle_encounter_unload(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_unload(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers) { if (extra_pointers) { std::string *str = EQEmu::any_cast(extra_pointers->at(0)); @@ -728,7 +735,7 @@ void handle_encounter_unload(QuestInterface *parse, lua_State* L, std::string da } } -void handle_encounter_null(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_null(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers) { } diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 46609fb06..0a2ab5ad9 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -6,7 +6,7 @@ typedef void(*NPCArgumentHandler)(QuestInterface*, lua_State*, NPC*, Mob*, std:: typedef void(*PlayerArgumentHandler)(QuestInterface*, lua_State*, Client*, std::string, uint32, std::vector*); typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, ItemInst*, Mob*, std::string, uint32, std::vector*); typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, NPC*, Client*, uint32, uint32, std::vector*); -typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, std::string, uint32, std::vector*); +typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector*); //NPC void handle_npc_event_say(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, @@ -130,13 +130,13 @@ void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* cl //Encounter -void handle_encounter_timer(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_timer(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers); -void handle_encounter_load(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_load(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers); -void handle_encounter_unload(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_unload(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers); -void handle_encounter_null(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, +void handle_encounter_null(QuestInterface *parse, lua_State* L, Encounter* encounter, std::string data, uint32 extra_data, std::vector *extra_pointers); #endif diff --git a/zone/mob.cpp b/zone/mob.cpp index 8a224db0b..1591cc057 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1266,7 +1266,7 @@ void Mob::CreateHPPacket(EQApplicationPacket* app) } // sends hp update of this mob to people who might care -void Mob::SendHPUpdate() +void Mob::SendHPUpdate(bool skip_self) { EQApplicationPacket hp_app; Group *group; @@ -1355,8 +1355,7 @@ void Mob::SendHPUpdate() } // send to self - we need the actual hps here - if(IsClient()) - { + if(IsClient() && !skip_self) { EQApplicationPacket* hp_app2 = new EQApplicationPacket(OP_HPUpdate,sizeof(SpawnHPUpdate_Struct)); SpawnHPUpdate_Struct* ds = (SpawnHPUpdate_Struct*)hp_app2->pBuffer; ds->cur_hp = CastToClient()->GetHP() - itembonuses.HP; @@ -1365,6 +1364,7 @@ void Mob::SendHPUpdate() CastToClient()->QueuePacket(hp_app2); safe_delete(hp_app2); } + ResetHPUpdateTimer(); // delay the timer } // this one just warps the mob to the current location @@ -4205,25 +4205,25 @@ int32 Mob::GetItemStat(uint32 itemid, const char *identifier) std::string Mob::GetGlobal(const char *varname) { int qgCharid = 0; int qgNpcid = 0; - + if (this->IsNPC()) qgNpcid = this->GetNPCTypeID(); - + if (this->IsClient()) qgCharid = this->CastToClient()->CharacterID(); - + QGlobalCache *qglobals = nullptr; std::list globalMap; - + if (this->IsClient()) qglobals = this->CastToClient()->GetQGlobals(); - + if (this->IsNPC()) qglobals = this->CastToNPC()->GetQGlobals(); if(qglobals) QGlobalCache::Combine(globalMap, qglobals->GetBucket(), qgNpcid, qgCharid, zone->GetZoneID()); - + std::list::iterator iter = globalMap.begin(); while(iter != globalMap.end()) { if ((*iter).name.compare(varname) == 0) @@ -4231,7 +4231,7 @@ std::string Mob::GetGlobal(const char *varname) { ++iter; } - + return "Undefined"; } diff --git a/zone/mob.h b/zone/mob.h index 74a53346e..ded9c04dc 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -508,7 +508,8 @@ public: static void CreateSpawnPacket(EQApplicationPacket* app, NewSpawn_Struct* ns); virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); void CreateHPPacket(EQApplicationPacket* app); - void SendHPUpdate(); + void SendHPUpdate(bool skip_self = false); + virtual void ResetHPUpdateTimer() {}; // does nothing //Util static uint32 RandomTimer(int min, int max); diff --git a/zone/npc.cpp b/zone/npc.cpp index 0ece7ab6c..d4a33b6d0 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -115,7 +115,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if knightattack_timer(1000), assist_timer(AIassistcheck_delay), qglobal_purge_timer(30000), - sendhpupdate_timer(1000), + sendhpupdate_timer(2000), enraged_timer(1000), taunt_timer(TauntReuseTime * 1000), m_SpawnPoint(position), @@ -650,7 +650,8 @@ bool NPC::Process() } } - if (sendhpupdate_timer.Check() && (IsTargeted() || (IsPet() && GetOwner() && GetOwner()->IsClient()))) { + // we might actually want to reset in this check ... won't until issues arise at least :P + if (sendhpupdate_timer.Check(false) && (IsTargeted() || (IsPet() && GetOwner() && GetOwner()->IsClient()))) { if(!IsFullHP || cur_hp