diff --git a/common/ruletypes.h b/common/ruletypes.h index 0fd413e5a..91ade257b 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -76,6 +76,7 @@ RULE_BOOL ( Character, SharedBankPlat, false) //off by default to prevent duping RULE_BOOL ( Character, BindAnywhere, false) RULE_INT ( Character, RestRegenPercent, 0) // Set to >0 to enable rest state bonus HP and mana regen. RULE_INT ( Character, RestRegenTimeToActivate, 30) // Time in seconds for rest state regen to kick in. +RULE_INT ( Character, RestRegenRaidTimeToActivate, 300) // Time in seconds for rest state regen to kick in with a raid target. RULE_BOOL ( Character, RestRegenEndurance, false) // Whether rest regen will work for endurance or not. RULE_INT ( Character, KillsPerGroupLeadershipAA, 250) // Number of dark blues or above per Group Leadership AA RULE_INT ( Character, KillsPerRaidLeadershipAA, 250) // Number of dark blues or above per Raid Leadership AA diff --git a/zone/client.cpp b/zone/client.cpp index 6b7828f1d..aa93478ba 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -324,6 +324,9 @@ Client::Client(EQStreamInterface* ieqs) initial_respawn_selection = 0; alternate_currency_loaded = false; + + EngagedRaidTarget = false; + SavedRaidRestTimer = 0; } Client::~Client() { @@ -4330,12 +4333,16 @@ void Client::IncrementAggroCount() { if(!RuleI(Character, RestRegenPercent)) return; - + // If we already had aggro before this method was called, the combat indicator should already be up for SoF clients, // so we don't need to send it again. // if(AggroCount > 1) return; + + // Pause the rest timer + if (AggroCount == 1) + SavedRaidRestTimer = rest_timer.GetRemainingTime(); if(GetClientVersion() >= EQClientSoF) { @@ -4367,14 +4374,27 @@ void Client::DecrementAggroCount() { // Something else is still aggro on us, can't rest yet. if(AggroCount) return; - rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000); - + uint32 time_until_rest; + if (GetEngagedRaidTarget()) { + time_until_rest = RuleI(Character, RestRegenRaidTimeToActivate) * 1000; + SetEngagedRaidTarget(false); + } else { + if (SavedRaidRestTimer > (RuleI(Character, RestRegenTimeToActivate) * 1000)) { + time_until_rest = SavedRaidRestTimer; + SavedRaidRestTimer = 0; + } else { + time_until_rest = RuleI(Character, RestRegenTimeToActivate) * 1000; + } + } + + rest_timer.Start(time_until_rest); + if(GetClientVersion() >= EQClientSoF) { EQApplicationPacket *outapp = new EQApplicationPacket(OP_RestState, 5); char *Buffer = (char *)outapp->pBuffer; VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0x00); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, RuleI(Character, RestRegenTimeToActivate)); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, (uint32)(time_until_rest / 1000)); QueuePacket(outapp); safe_delete(outapp); } diff --git a/zone/client.h b/zone/client.h index cce84673c..cadf9b583 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1198,6 +1198,9 @@ public: int mod_food_value(const Item_Struct *item, int change); int mod_drink_value(const Item_Struct *item, int change); + void SetEngagedRaidTarget(bool value) { EngagedRaidTarget = value; } + bool GetEngagedRaidTarget() const { return EngagedRaidTarget; } + protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); @@ -1442,6 +1445,9 @@ private: unsigned int RestRegenHP; unsigned int RestRegenMana; unsigned int RestRegenEndurance; + + bool EngagedRaidTarget; + uint32 SavedRaidRestTimer; std::set zone_flags; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 01e768c1f..0d44bfa93 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -9140,7 +9140,8 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) { m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; - if(m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) + // Reset rest timer if the durations have been lowered in the database + if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate))) m_pp.RestTimer = 0; //This checksum should disappear once dynamic structs are in... each struct strategy will do it diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 1b585ca32..2d7bd3ea6 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -198,8 +198,11 @@ void HateList::Add(Mob *ent, int32 in_hate, int32 in_dam, bool bFrenzy, bool iAd list.push_back(p); parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), ent, "1", 0); - if(ent->IsClient()) + if (ent->IsClient()) { + if (owner->CastToNPC()->IsRaidTarget()) + ent->CastToClient()->SetEngagedRaidTarget(true); ent->CastToClient()->IncrementAggroCount(); + } } } diff --git a/zone/mob.h b/zone/mob.h index 15f1728ec..a0c8dbdc4 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -524,6 +524,7 @@ public: //More stuff to sort: + virtual bool IsRaidTarget() { return false; }; virtual bool IsAttackAllowed(Mob *target, bool isSpellAttack = false); bool IsTargeted() const { return (targeted > 0); } inline void IsTargeted(int in_tar) { targeted += in_tar; if(targeted < 0) targeted = 0;} diff --git a/zone/npc.cpp b/zone/npc.cpp index 565a89598..f4e307bbc 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -358,6 +358,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float SetEmoteID(d->emoteid); InitializeBuffSlots(); CalcBonuses(); + raid_target = d->raid_target; } NPC::~NPC() diff --git a/zone/npc.h b/zone/npc.h index 6bc9813fe..5c8fd43bb 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -395,6 +395,8 @@ public: void mod_npc_killed_merit(Mob* c); void mod_npc_killed(Mob* oos); void AISpellsList(Client *c); + + bool IsRaidTarget() const { return raid_target; }; protected: @@ -500,6 +502,8 @@ protected: //mercenary stuff std::list mercTypeList; std::list mercDataList; + + bool raid_target; private: uint32 loottable_id; diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index b14e63f26..0d1653453 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1115,7 +1115,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { "npc_types.emoteid," "npc_types.spellscale," "npc_types.healscale," - "npc_types.no_target_hotkey"; + "npc_types.no_target_hotkey," + "npc_types.raid_target"; MakeAnyLenString(&query, "%s FROM npc_types WHERE id=%d", basic_query, id); @@ -1302,6 +1303,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { tmpNPCType->spellscale = atoi(row[r++]); tmpNPCType->healscale = atoi(row[r++]); tmpNPCType->no_target_hotkey = atoi(row[r++]) == 1 ? true : false; + tmpNPCType->raid_target = atoi(row[r++]) == 0 ? false : true; // If NPC with duplicate NPC id already in table, // free item we attempted to add. diff --git a/zone/zonedump.h b/zone/zonedump.h index 3b5b91577..98841630a 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -125,6 +125,7 @@ struct NPCType float spellscale; float healscale; bool no_target_hotkey; + bool raid_target; }; /*