mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
Rework regens to match modern clients
This commit is contained in:
parent
43f459b194
commit
8400994c57
@ -1,5 +1,14 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 10/08/2017 ==
|
||||
Mackal: Rework regens
|
||||
|
||||
Regen will now match whats reported by modern clients, besides where they lie due to known bugs
|
||||
|
||||
HP and END regens are now based on the BaseData.txt values allowing easy customization
|
||||
Those cases:
|
||||
- The client always applies hunger penalties, it appears they don't exist anymore on live you can turn them on with a rule
|
||||
- The way the client gets buff mana/end regen benefits incorrectly applies the bard mod making these values lie sometimes
|
||||
|
||||
== 9/17/2017 ==
|
||||
Akkadius: Add model/race offset to FixZ calc (KLS)
|
||||
|
||||
@ -24,8 +24,8 @@ struct BaseDataStruct
|
||||
double base_hp;
|
||||
double base_mana;
|
||||
double base_end;
|
||||
double unk1;
|
||||
double unk2;
|
||||
double hp_regen;
|
||||
double end_regen;
|
||||
double hp_factor;
|
||||
double mana_factor;
|
||||
double endurance_factor;
|
||||
|
||||
@ -854,6 +854,7 @@ static const uint32 MAX_PP_REF_SPELLBOOK = 480; // Set for Player Profile size r
|
||||
static const uint32 MAX_PP_REF_MEMSPELL = 9; // Set for Player Profile size retain
|
||||
|
||||
static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size
|
||||
static const uint32 MAX_PP_INNATE_SKILL = 25;
|
||||
static const uint32 MAX_PP_AA_ARRAY = 240;
|
||||
static const uint32 MAX_GROUP_MEMBERS = 6;
|
||||
static const uint32 MAX_RECAST_TYPES = 20;
|
||||
@ -993,7 +994,8 @@ struct PlayerProfile_Struct
|
||||
/*4768*/ int32 platinum_shared; // Platinum shared between characters
|
||||
/*4772*/ uint8 unknown4808[24];
|
||||
/*4796*/ uint32 skills[MAX_PP_SKILL]; // [400] List of skills // 100 dword buffer
|
||||
/*5196*/ uint8 unknown5132[184];
|
||||
/*5196*/ uint32 InnateSkills[MAX_PP_INNATE_SKILL];
|
||||
/*5296*/ uint8 unknown5132[84];
|
||||
/*5380*/ uint32 pvp2; //
|
||||
/*5384*/ uint32 unknown5420; //
|
||||
/*5388*/ uint32 pvptype; //
|
||||
|
||||
@ -2184,11 +2184,11 @@ namespace RoF
|
||||
outapp->WriteUInt32(emu->skills[r]);
|
||||
}
|
||||
|
||||
outapp->WriteUInt32(25); // Unknown count
|
||||
outapp->WriteUInt32(structs::MAX_PP_INNATE_SKILL); // Innate Skills count
|
||||
|
||||
for (uint32 r = 0; r < 25; r++)
|
||||
for (uint32 r = 0; r < structs::MAX_PP_INNATE_SKILL; r++)
|
||||
{
|
||||
outapp->WriteUInt32(0); // Unknown
|
||||
outapp->WriteUInt32(emu->InnateSkills[r]); // Innate Skills (regen, slam, etc)
|
||||
}
|
||||
|
||||
outapp->WriteUInt32(structs::MAX_PP_DISCIPLINES); // Discipline count
|
||||
|
||||
@ -2261,11 +2261,11 @@ namespace RoF2
|
||||
outapp->WriteUInt32(emu->skills[r]);
|
||||
}
|
||||
|
||||
outapp->WriteUInt32(25); // Unknown count
|
||||
outapp->WriteUInt32(structs::MAX_PP_INNATE_SKILL); // Innate Skills count
|
||||
|
||||
for (uint32 r = 0; r < 25; r++)
|
||||
for (uint32 r = 0; r < structs::MAX_PP_INNATE_SKILL; r++)
|
||||
{
|
||||
outapp->WriteUInt32(0); // Unknown
|
||||
outapp->WriteUInt32(emu->InnateSkills[r]); // Innate Skills (regen, slam, etc)
|
||||
}
|
||||
|
||||
outapp->WriteUInt32(structs::MAX_PP_DISCIPLINES); // Discipline count
|
||||
|
||||
@ -131,6 +131,7 @@ static const uint32 MAX_PP_LANGUAGE = 32; // was 25
|
||||
static const uint32 MAX_PP_SPELLBOOK = 720; // was 480
|
||||
static const uint32 MAX_PP_MEMSPELL = 16; // was 12
|
||||
static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size
|
||||
static const uint32 MAX_PP_INNATE_SKILL = 25;
|
||||
static const uint32 MAX_PP_AA_ARRAY = 300;
|
||||
static const uint32 MAX_PP_DISCIPLINES = 300; // was 200
|
||||
static const uint32 MAX_GROUP_MEMBERS = 6;
|
||||
@ -1155,8 +1156,8 @@ union
|
||||
/*01012*/ AA_Array aa_array[MAX_PP_AA_ARRAY]; // [300] 3600 bytes - AAs 12 bytes each
|
||||
/*04612*/ uint32 skill_count; // Seen 100
|
||||
/*04616*/ uint32 skills[MAX_PP_SKILL]; // [100] 400 bytes - List of skills
|
||||
/*05016*/ uint32 unknown15_count; // Seen 25
|
||||
/*05020*/ uint32 unknown_rof15[25]; // Most are 255 or 0
|
||||
/*05016*/ uint32 InnateSkills_count; // Seen 25
|
||||
/*05020*/ uint32 InnateSkills[MAX_PP_INNATE_SKILL]; // Most are 255 or 0
|
||||
/*05120*/ uint32 discipline_count; // Seen 200
|
||||
/*05124*/ Disciplines_Struct disciplines; // [200] 800 bytes Known disciplines
|
||||
/*05924*/ uint32 timestamp_count; // Seen 20
|
||||
|
||||
@ -131,6 +131,7 @@ static const uint32 MAX_PP_LANGUAGE = 32; // was 25
|
||||
static const uint32 MAX_PP_SPELLBOOK = 720; // was 480
|
||||
static const uint32 MAX_PP_MEMSPELL = 16; // was 12
|
||||
static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size
|
||||
static const uint32 MAX_PP_INNATE_SKILL = 25;
|
||||
static const uint32 MAX_PP_AA_ARRAY = 300;
|
||||
static const uint32 MAX_PP_DISCIPLINES = 200; // was 100
|
||||
static const uint32 MAX_GROUP_MEMBERS = 6;
|
||||
@ -1096,8 +1097,8 @@ union
|
||||
/*01012*/ AA_Array aa_array[MAX_PP_AA_ARRAY]; // [300] 3600 bytes - AAs 12 bytes each
|
||||
/*04612*/ uint32 skill_count; // Seen 100
|
||||
/*04616*/ uint32 skills[MAX_PP_SKILL]; // [100] 400 bytes - List of skills
|
||||
/*05016*/ uint32 unknown15_count; // Seen 25
|
||||
/*05020*/ uint32 unknown_rof15[25]; // Most are 255 or 0
|
||||
/*05016*/ uint32 InnateSkills_count; // Seen 25
|
||||
/*05020*/ uint32 InnateSkills[MAX_PP_INNATE_SKILL]; // Most are 255 or 0
|
||||
/*05120*/ uint32 discipline_count; // Seen 200
|
||||
/*05124*/ Disciplines_Struct disciplines; // [200] 800 bytes Known disciplines
|
||||
/*05924*/ uint32 timestamp_count; // Seen 20
|
||||
|
||||
@ -1605,6 +1605,7 @@ namespace SoD
|
||||
OUT(copper_cursor);
|
||||
|
||||
OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword)
|
||||
OUT_array(InnateSkills, structs::MAX_PP_INNATE_SKILL); // 1:1 direct copy (25 dword)
|
||||
|
||||
// OUT(unknown04760[236]);
|
||||
OUT(toxicity);
|
||||
|
||||
@ -818,6 +818,7 @@ static const uint32 MAX_PP_LANGUAGE = 25; //
|
||||
static const uint32 MAX_PP_SPELLBOOK = 480; // Confirmed 60 pages on Live now
|
||||
static const uint32 MAX_PP_MEMSPELL = 10; //was 9 now 10 on Live
|
||||
static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size
|
||||
static const uint32 MAX_PP_INNATE_SKILL = 25;
|
||||
static const uint32 MAX_PP_AA_ARRAY = 300; //was 299
|
||||
static const uint32 MAX_GROUP_MEMBERS = 6;
|
||||
static const uint32 MAX_RECAST_TYPES = 20;
|
||||
@ -923,7 +924,8 @@ struct PlayerProfile_Struct
|
||||
/*06488*/ uint32 silver_cursor; // Silver Pieces on cursor
|
||||
/*06492*/ uint32 copper_cursor; // Copper Pieces on cursor
|
||||
/*06496*/ uint32 skills[MAX_PP_SKILL]; // [400] List of skills // 100 dword buffer
|
||||
/*06896*/ uint8 unknown04760[136];
|
||||
/*06896*/ uint32 InnateSkills[MAX_PP_SKILL];
|
||||
/*06996*/ uint8 unknown04760[36];
|
||||
/*07032*/ uint32 toxicity; // Potion Toxicity (15=too toxic, each potion adds 3)
|
||||
/*07036*/ uint32 thirst_level; // Drink (ticks till next drink)
|
||||
/*07040*/ uint32 hunger_level; // Food (ticks till next eat)
|
||||
|
||||
@ -1276,6 +1276,7 @@ namespace SoF
|
||||
OUT(copper_cursor);
|
||||
|
||||
OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword)
|
||||
OUT_array(InnateSkills, structs::MAX_PP_INNATE_SKILL); // 1:1 direct copy (25 dword)
|
||||
|
||||
// OUT(unknown04760[236]);
|
||||
OUT(toxicity);
|
||||
|
||||
@ -799,6 +799,7 @@ static const uint32 MAX_PP_LANGUAGE = 25; //
|
||||
static const uint32 MAX_PP_SPELLBOOK = 480; // Confirmed 60 pages on Live now
|
||||
static const uint32 MAX_PP_MEMSPELL = 10; //was 9 now 10 on Live
|
||||
static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size
|
||||
static const uint32 MAX_PP_INNATE_SKILL = 25;
|
||||
static const uint32 MAX_PP_AA_ARRAY = 300; //was 299
|
||||
static const uint32 MAX_GROUP_MEMBERS = 6;
|
||||
static const uint32 MAX_RECAST_TYPES = 20;
|
||||
@ -903,7 +904,8 @@ struct PlayerProfile_Struct //23576 Octets
|
||||
/*06488*/ uint32 silver_cursor; // Silver Pieces on cursor
|
||||
/*06492*/ uint32 copper_cursor; // Copper Pieces on cursor
|
||||
/*06496*/ uint32 skills[MAX_PP_SKILL]; // [400] List of skills // 100 dword buffer
|
||||
/*06896*/ uint8 unknown04760[136];
|
||||
/*06896*/ uint32 InnateSkills[MAX_PP_INNATE_SKILL];
|
||||
/*06996*/ uint8 unknown04760[36];
|
||||
/*07032*/ uint32 toxicity; // Potion Toxicity (15=too toxic, each potion adds 3)
|
||||
/*07036*/ uint32 thirst_level; // Drink (ticks till next drink)
|
||||
/*07040*/ uint32 hunger_level; // Food (ticks till next eat)
|
||||
|
||||
@ -1020,6 +1020,7 @@ namespace Titanium
|
||||
OUT(copper_cursor);
|
||||
|
||||
OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword)
|
||||
OUT_array(InnateSkills, structs::MAX_PP_INNATE_SKILL); // 1:1 direct copy (25 dword)
|
||||
|
||||
// OUT(unknown04760[236]);
|
||||
OUT(toxicity);
|
||||
|
||||
@ -740,6 +740,7 @@ static const uint32 MAX_PP_LANGUAGE = 28;
|
||||
static const uint32 MAX_PP_SPELLBOOK = 400;
|
||||
static const uint32 MAX_PP_MEMSPELL = 9;
|
||||
static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size
|
||||
static const uint32 MAX_PP_INNATE_SKILL = 25;
|
||||
static const uint32 MAX_PP_AA_ARRAY = 240;
|
||||
static const uint32 MAX_GROUP_MEMBERS = 6;
|
||||
static const uint32 MAX_RECAST_TYPES = 20;
|
||||
@ -844,7 +845,8 @@ struct PlayerProfile_Struct
|
||||
/*04452*/ uint32 silver_cursor; // Silver Pieces on cursor
|
||||
/*04456*/ uint32 copper_cursor; // Copper Pieces on cursor
|
||||
/*04460*/ uint32 skills[MAX_PP_SKILL]; // [400] List of skills // 100 dword buffer
|
||||
/*04860*/ uint8 unknown04760[136];
|
||||
/*04860*/ uint32 InnateSkills[MAX_PP_INNATE_SKILL];
|
||||
/*04960*/ uint8 unknown04760[36];
|
||||
/*04996*/ uint32 toxicity; // Potion Toxicity (15=too toxic, each potion adds 3)
|
||||
/*05000*/ uint32 thirst_level; // Drink (ticks till next drink)
|
||||
/*05004*/ uint32 hunger_level; // Food (ticks till next eat)
|
||||
|
||||
@ -1850,6 +1850,7 @@ namespace UF
|
||||
OUT(copper_cursor);
|
||||
|
||||
OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword)
|
||||
OUT_array(InnateSkills, structs::MAX_PP_INNATE_SKILL); // 1:1 direct copy (25 dword)
|
||||
|
||||
// OUT(unknown04760[236]);
|
||||
OUT(toxicity);
|
||||
|
||||
@ -848,6 +848,7 @@ static const uint32 MAX_PP_LANGUAGE = 25; //
|
||||
static const uint32 MAX_PP_SPELLBOOK = 720; // Confirmed 60 pages on Underfoot now
|
||||
static const uint32 MAX_PP_MEMSPELL = 12; //was 9 now 10 on Underfoot
|
||||
static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size
|
||||
static const uint32 MAX_PP_INNATE_SKILL = 25;
|
||||
static const uint32 MAX_PP_AA_ARRAY = 300; //was 299
|
||||
static const uint32 MAX_GROUP_MEMBERS = 6;
|
||||
static const uint32 MAX_RECAST_TYPES = 20;
|
||||
@ -954,7 +955,8 @@ struct PlayerProfile_Struct
|
||||
/*07336*/ uint32 silver_cursor; // Silver Pieces on cursor
|
||||
/*07340*/ uint32 copper_cursor; // Copper Pieces on cursor
|
||||
/*07344*/ uint32 skills[MAX_PP_SKILL]; // [400] List of skills // 100 dword buffer
|
||||
/*07744*/ uint8 unknown07644[136];
|
||||
/*07744*/ uint32 InnateSkills[MAX_PP_INNATE_SKILL];
|
||||
/*07844*/ uint8 unknown07644[36];
|
||||
/*07880*/ uint32 toxicity; // Potion Toxicity (15=too toxic, each potion adds 3)
|
||||
/*07884*/ uint32 thirst_level; // Drink (ticks till next drink)
|
||||
/*07888*/ uint32 hunger_level; // Food (ticks till next eat)
|
||||
|
||||
@ -70,7 +70,7 @@ RULE_INT(Character, ConsumptionMultiplier, 100) //item's hunger restored = this
|
||||
RULE_BOOL(Character, HealOnLevel, false)
|
||||
RULE_BOOL(Character, FeignKillsPet, false)
|
||||
RULE_INT(Character, ItemManaRegenCap, 15)
|
||||
RULE_INT(Character, ItemHealthRegenCap, 35)
|
||||
RULE_INT(Character, ItemHealthRegenCap, 30)
|
||||
RULE_INT(Character, ItemDamageShieldCap, 30)
|
||||
RULE_INT(Character, ItemAccuracyCap, 150)
|
||||
RULE_INT(Character, ItemAvoidanceCap, 100)
|
||||
@ -91,10 +91,12 @@ RULE_INT(Character, HasteCap, 100) // Haste cap for non-v3(overhaste) haste.
|
||||
RULE_INT(Character, SkillUpModifier, 100) //skill ups are at 100%
|
||||
RULE_BOOL(Character, SharedBankPlat, false) //off by default to prevent duping for now
|
||||
RULE_BOOL(Character, BindAnywhere, false)
|
||||
RULE_INT(Character, RestRegenPercent, 0) // Set to >0 to enable rest state bonus HP and mana regen.
|
||||
RULE_BOOL(Character, RestRegenEnabled, true) // Enable OOC Regen
|
||||
RULE_INT(Character, RestRegenHP, 180) // seconds until full from 0. this is actually zone setable, but most or all zones are 180
|
||||
RULE_INT(Character, RestRegenMana, 180) // seconds until full from 0. this is actually zone setable, but most or all zones are 180
|
||||
RULE_INT(Character, RestRegenEnd, 180) // seconds until full from 0. this is actually zone setable, but most or all zones are 180
|
||||
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
|
||||
RULE_INT(Character, MaxFearDurationForPlayerCharacter, 4) //4 tics, each tic calculates every 6 seconds.
|
||||
@ -118,6 +120,7 @@ RULE_BOOL(Character, EnableXTargetting, true) // Enable Extended Targetting Wind
|
||||
RULE_BOOL(Character, EnableAggroMeter, true) // Enable Aggro Meter, for users with RoF and later clients.
|
||||
RULE_BOOL(Character, KeepLevelOverMax, false) // Don't delevel a character that has somehow gone over the level cap
|
||||
RULE_INT(Character, FoodLossPerUpdate, 32) // How much food/water you lose per stamina update
|
||||
RULE_BOOL(Character, EnableHungerPenalties, false) // being hungry/thirsty has negative effects -- it does appear normal live servers do not have penalties
|
||||
RULE_INT(Character, BaseInstrumentSoftCap, 36) // Softcap for instrument mods, 36 commonly referred to as "3.6" as well.
|
||||
RULE_BOOL(Character, UseSpellFileSongCap, true) // When they removed the AA that increased the cap they removed the above and just use the spell field
|
||||
RULE_INT(Character, BaseRunSpeedCap, 158) // Base Run Speed Cap, on live it's 158% which will give you a runspeed of 1.580 hard capped to 225.
|
||||
|
||||
@ -1817,8 +1817,8 @@ void SharedDatabase::LoadBaseData(void *data, int max_level) {
|
||||
bd->base_hp = atof(row[2]);
|
||||
bd->base_mana = atof(row[3]);
|
||||
bd->base_end = atof(row[4]);
|
||||
bd->unk1 = atof(row[5]);
|
||||
bd->unk2 = atof(row[6]);
|
||||
bd->hp_regen = atof(row[5]);
|
||||
bd->end_regen = atof(row[6]);
|
||||
bd->hp_factor = atof(row[7]);
|
||||
bd->mana_factor = atof(row[8]);
|
||||
bd->endurance_factor = atof(row[9]);
|
||||
|
||||
@ -131,6 +131,11 @@ enum SpellAffectIndex {
|
||||
SAI_NPC_Special_80 = 80,
|
||||
SAI_Trap_Lock = 88
|
||||
};
|
||||
|
||||
enum class GlobalGroup {
|
||||
Lich = 46,
|
||||
};
|
||||
|
||||
enum RESISTTYPE
|
||||
{
|
||||
RESIST_NONE = 0,
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
UPDATE `rule_values` SET `rule_value` = '32' WHERE `rule_name` = 'Character:FoodLossPerUpdate';
|
||||
UPDATE `rule_values` SET `rule_value` = '30' WHERE `rule_name` = 'Character:ItemHealthRegenCap';
|
||||
|
||||
@ -915,7 +915,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
||||
newbon->GivePetGroupTarget = true;
|
||||
break;
|
||||
case SE_ItemHPRegenCapIncrease:
|
||||
newbon->ItemHPRegenCap = +base1;
|
||||
newbon->ItemHPRegenCap += base1;
|
||||
break;
|
||||
case SE_Ambidexterity:
|
||||
newbon->Ambidexterity += base1;
|
||||
|
||||
@ -6727,7 +6727,7 @@ int32 Bot::CalcATK() {
|
||||
}
|
||||
|
||||
void Bot::CalcRestState() {
|
||||
if(!RuleI(Character, RestRegenPercent))
|
||||
if(!RuleB(Character, RestRegenEnabled))
|
||||
return;
|
||||
|
||||
RestRegenHP = RestRegenMana = RestRegenEndurance = 0;
|
||||
@ -6743,10 +6743,9 @@ void Bot::CalcRestState() {
|
||||
}
|
||||
}
|
||||
|
||||
RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100);
|
||||
RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100);
|
||||
if(RuleB(Character, RestRegenEndurance))
|
||||
RestRegenEndurance = (GetMaxEndurance() * RuleI(Character, RestRegenPercent) / 100);
|
||||
RestRegenHP = 6 * (GetMaxHP() / RuleI(Character, RestRegenHP));
|
||||
RestRegenMana = 6 * (GetMaxMana() / RuleI(Character, RestRegenMana));
|
||||
RestRegenEndurance = 6 * (GetMaxEndurance() / RuleI(Character, RestRegenEnd));
|
||||
}
|
||||
|
||||
int32 Bot::LevelRegen() {
|
||||
|
||||
216
zone/client.cpp
216
zone/client.cpp
@ -160,7 +160,8 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
npc_close_scan_timer(6000),
|
||||
hp_self_update_throttle_timer(300),
|
||||
hp_other_update_throttle_timer(500),
|
||||
position_update_timer(10000)
|
||||
position_update_timer(10000),
|
||||
tmSitting(0)
|
||||
{
|
||||
|
||||
for (int client_filter = 0; client_filter < _FilterCount; client_filter++)
|
||||
@ -270,9 +271,10 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
m_ClientVersion = EQEmu::versions::ClientVersion::Unknown;
|
||||
m_ClientVersionBit = 0;
|
||||
AggroCount = 0;
|
||||
RestRegenHP = 0;
|
||||
RestRegenMana = 0;
|
||||
RestRegenEndurance = 0;
|
||||
ooc_regen = false;
|
||||
AreaHPRegen = 1.0f;
|
||||
AreaManaRegen = 1.0f;
|
||||
AreaEndRegen = 1.0f;
|
||||
XPRate = 100;
|
||||
current_endurance = 0;
|
||||
|
||||
@ -329,6 +331,9 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
|
||||
interrogateinv_flag = false;
|
||||
|
||||
for (int i = 0; i < InnateSkillMax; ++i)
|
||||
m_pp.InnateSkills[i] = InnateDisabled;
|
||||
|
||||
AI_Init();
|
||||
}
|
||||
|
||||
@ -4598,7 +4603,7 @@ void Client::IncrementAggroCount() {
|
||||
//
|
||||
AggroCount++;
|
||||
|
||||
if(!RuleI(Character, RestRegenPercent))
|
||||
if(!RuleB(Character, RestRegenEnabled))
|
||||
return;
|
||||
|
||||
// If we already had aggro before this method was called, the combat indicator should already be up for SoF clients,
|
||||
@ -4635,7 +4640,7 @@ void Client::DecrementAggroCount() {
|
||||
|
||||
AggroCount--;
|
||||
|
||||
if(!RuleI(Character, RestRegenPercent))
|
||||
if(!RuleB(Character, RestRegenEnabled))
|
||||
return;
|
||||
|
||||
// Something else is still aggro on us, can't rest yet.
|
||||
@ -6722,7 +6727,7 @@ void Client::SendStatsWindow(Client* client, bool use_window)
|
||||
cap_regen_field = itoa(CalcHPRegenCap());
|
||||
spell_regen_field = itoa(spellbonuses.HPRegen);
|
||||
aa_regen_field = itoa(aabonuses.HPRegen);
|
||||
total_regen_field = itoa(CalcHPRegen());
|
||||
total_regen_field = itoa(CalcHPRegen(true));
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
@ -6735,7 +6740,7 @@ void Client::SendStatsWindow(Client* client, bool use_window)
|
||||
cap_regen_field = itoa(CalcManaRegenCap());
|
||||
spell_regen_field = itoa(spellbonuses.ManaRegen);
|
||||
aa_regen_field = itoa(aabonuses.ManaRegen);
|
||||
total_regen_field = itoa(CalcManaRegen());
|
||||
total_regen_field = itoa(CalcManaRegen(true));
|
||||
}
|
||||
else { continue; }
|
||||
break;
|
||||
@ -6749,7 +6754,7 @@ void Client::SendStatsWindow(Client* client, bool use_window)
|
||||
cap_regen_field = itoa(CalcEnduranceRegenCap());
|
||||
spell_regen_field = itoa(spellbonuses.EnduranceRegen);
|
||||
aa_regen_field = itoa(aabonuses.EnduranceRegen);
|
||||
total_regen_field = itoa(CalcEnduranceRegen());
|
||||
total_regen_field = itoa(CalcEnduranceRegen(true));
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
@ -9066,3 +9071,196 @@ void Client::SetPetCommandState(int button, int state)
|
||||
FastQueuePacket(&app);
|
||||
}
|
||||
|
||||
bool Client::CanMedOnHorse()
|
||||
{
|
||||
// no horse is false
|
||||
if (GetHorseId() == 0)
|
||||
return false;
|
||||
|
||||
// can't med while attacking
|
||||
if (auto_attack)
|
||||
return false;
|
||||
|
||||
return animation == 0 && m_Delta.x == 0.0f && m_Delta.y == 0.0f; // TODO: animation is SpeedRun
|
||||
}
|
||||
|
||||
void Client::EnableAreaHPRegen(int value)
|
||||
{
|
||||
AreaHPRegen = value * 0.001f;
|
||||
SendAppearancePacket(AT_AreaHPRegen, value); // does this send to whole zone?
|
||||
// send test and particles?
|
||||
}
|
||||
|
||||
void Client::DisableAreaHPRegen()
|
||||
{
|
||||
AreaHPRegen = 1.0f;
|
||||
SendAppearancePacket(AT_AreaHPRegen, 1000);
|
||||
}
|
||||
|
||||
void Client::EnableAreaManaRegen(int value)
|
||||
{
|
||||
AreaManaRegen = value * 0.001f;
|
||||
SendAppearancePacket(AT_AreaManaRegen, value); // does this send to whole zone?
|
||||
// send test and particles?
|
||||
}
|
||||
|
||||
void Client::DisableAreaManaRegen()
|
||||
{
|
||||
AreaManaRegen = 1.0f;
|
||||
SendAppearancePacket(AT_AreaManaRegen, 1000);
|
||||
}
|
||||
|
||||
void Client::EnableAreaEndRegen(int value)
|
||||
{
|
||||
AreaEndRegen = value * 0.001f;
|
||||
SendAppearancePacket(AT_AreaEndRegen, value); // does this send to whole zone?
|
||||
// send test and particles?
|
||||
}
|
||||
|
||||
void Client::DisableAreaEndRegen()
|
||||
{
|
||||
AreaEndRegen = 1.0f;
|
||||
SendAppearancePacket(AT_AreaEndRegen, 1000);
|
||||
}
|
||||
|
||||
void Client::EnableAreaRegens(int value)
|
||||
{
|
||||
EnableAreaHPRegen(value);
|
||||
EnableAreaManaRegen(value);
|
||||
EnableAreaEndRegen(value);
|
||||
}
|
||||
|
||||
void Client::DisableAreaRegens()
|
||||
{
|
||||
DisableAreaHPRegen();
|
||||
DisableAreaManaRegen();
|
||||
DisableAreaEndRegen();
|
||||
}
|
||||
|
||||
void Client::InitInnates()
|
||||
{
|
||||
// this function on the client also inits the level one innate skills (like swimming, hide, etc)
|
||||
// we won't do that here, lets just do the InnateSkills for now. Basically translation of what the client is doing
|
||||
// A lot of these we could probably have ignored because they have no known use or are 100% client side
|
||||
// but I figured just in case we'll do them all out
|
||||
//
|
||||
// The client calls this in a few places. When you remove a vision buff and in SetHeights, which is called in
|
||||
// illusions, mounts, and a bunch of other cases. All of the calls to InitInnates are wrapped in restoring regen
|
||||
// besides the call initializing the first time
|
||||
auto race = GetRace();
|
||||
auto class_ = GetClass();
|
||||
|
||||
for (int i = 0; i < InnateSkillMax; ++i)
|
||||
m_pp.InnateSkills[i] = InnateDisabled;
|
||||
|
||||
m_pp.InnateSkills[InnateInspect] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateOpen] = InnateEnabled;
|
||||
if (race >= RT_FROGLOK_3) {
|
||||
if (race == RT_SKELETON_2 || race == RT_FROGLOK_3)
|
||||
m_pp.InnateSkills[InnateUltraVision] = InnateEnabled;
|
||||
else
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
}
|
||||
switch (race) {
|
||||
case RT_BARBARIAN:
|
||||
case RT_BARBARIAN_2:
|
||||
m_pp.InnateSkills[InnateSlam] = InnateEnabled;
|
||||
break;
|
||||
case RT_ERUDITE:
|
||||
case RT_ERUDITE_2:
|
||||
m_pp.InnateSkills[InnateLore] = InnateEnabled;
|
||||
break;
|
||||
case RT_WOOD_ELF:
|
||||
case RT_GUARD_3:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_HIGH_ELF:
|
||||
case RT_GUARD_2:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateLore] = InnateEnabled;
|
||||
break;
|
||||
case RT_DARK_ELF:
|
||||
case RT_DARK_ELF_2:
|
||||
case RT_VAMPIRE_2:
|
||||
m_pp.InnateSkills[InnateUltraVision] = InnateEnabled;
|
||||
break;
|
||||
case RT_TROLL:
|
||||
case RT_TROLL_2:
|
||||
m_pp.InnateSkills[InnateRegen] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateSlam] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_DWARF:
|
||||
case RT_DWARF_2:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_OGRE:
|
||||
case RT_OGRE_2:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateSlam] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateNoBash] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateBashDoor] = InnateEnabled;
|
||||
break;
|
||||
case RT_HALFLING:
|
||||
case RT_HALFLING_2:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_GNOME:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateLore] = InnateEnabled;
|
||||
break;
|
||||
case RT_IKSAR:
|
||||
m_pp.InnateSkills[InnateRegen] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_VAH_SHIR:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_FROGLOK_2:
|
||||
case RT_GHOST:
|
||||
case RT_GHOUL:
|
||||
case RT_SKELETON:
|
||||
case RT_VAMPIRE:
|
||||
case RT_WILL_O_WISP:
|
||||
case RT_ZOMBIE:
|
||||
case RT_SPECTRE:
|
||||
case RT_GHOST_2:
|
||||
case RT_GHOST_3:
|
||||
case RT_DRAGON_2:
|
||||
case RT_INNORUUK:
|
||||
m_pp.InnateSkills[InnateUltraVision] = InnateEnabled;
|
||||
break;
|
||||
case RT_HUMAN:
|
||||
case RT_GUARD:
|
||||
case RT_BEGGAR:
|
||||
case RT_HUMAN_2:
|
||||
case RT_HUMAN_3:
|
||||
case RT_FROGLOK_3: // client does froglok weird, but this should work out fine
|
||||
break;
|
||||
default:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (class_) {
|
||||
case DRUID:
|
||||
m_pp.InnateSkills[InnateHarmony] = InnateEnabled;
|
||||
break;
|
||||
case BARD:
|
||||
m_pp.InnateSkills[InnateReveal] = InnateEnabled;
|
||||
break;
|
||||
case ROGUE:
|
||||
m_pp.InnateSkills[InnateSurprise] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateReveal] = InnateEnabled;
|
||||
break;
|
||||
case RANGER:
|
||||
m_pp.InnateSkills[InnateAwareness] = InnateEnabled;
|
||||
break;
|
||||
case MONK:
|
||||
m_pp.InnateSkills[InnateSurprise] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateAwareness] = InnateEnabled;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -199,6 +199,27 @@ struct RespawnOption
|
||||
float heading;
|
||||
};
|
||||
|
||||
// do not ask what all these mean because I have no idea!
|
||||
// named from the client's CEverQuest::GetInnateDesc, they're missing some
|
||||
enum eInnateSkill {
|
||||
InnateEnabled = 0,
|
||||
InnateAwareness = 1,
|
||||
InnateBashDoor = 2,
|
||||
InnateBreathFire = 3,
|
||||
InnateHarmony = 4,
|
||||
InnateInfravision = 6,
|
||||
InnateLore = 8,
|
||||
InnateNoBash = 9,
|
||||
InnateRegen = 10,
|
||||
InnateSlam = 11,
|
||||
InnateSurprise = 12,
|
||||
InnateUltraVision = 13,
|
||||
InnateInspect = 14,
|
||||
InnateOpen = 15,
|
||||
InnateReveal = 16,
|
||||
InnateSkillMax = 25, // size of array in client
|
||||
InnateDisabled = 255
|
||||
};
|
||||
|
||||
const uint32 POPUPID_UPDATE_SHOWSTATSWINDOW = 1000000;
|
||||
|
||||
@ -406,6 +427,16 @@ public:
|
||||
const int32& SetMana(int32 amount);
|
||||
int32 CalcManaRegenCap();
|
||||
|
||||
// guild pool regen shit. Sends a SpawnAppearance with a value that regens to value * 0.001
|
||||
void EnableAreaHPRegen(int value);
|
||||
void DisableAreaHPRegen();
|
||||
void EnableAreaManaRegen(int value);
|
||||
void DisableAreaManaRegen();
|
||||
void EnableAreaEndRegen(int value);
|
||||
void DisableAreaEndRegen();
|
||||
void EnableAreaRegens(int value);
|
||||
void DisableAreaRegens();
|
||||
|
||||
void ServerFilter(SetServerFilter_Struct* filter);
|
||||
void BulkSendTraderInventory(uint32 char_id);
|
||||
void SendSingleTraderItem(uint32 char_id, int uniqueid);
|
||||
@ -540,7 +571,7 @@ public:
|
||||
/*Endurance and such*/
|
||||
void CalcMaxEndurance(); //This calculates the maximum endurance we can have
|
||||
int32 CalcBaseEndurance(); //Calculates Base End
|
||||
int32 CalcEnduranceRegen(); //Calculates endurance regen used in DoEnduranceRegen()
|
||||
int32 CalcEnduranceRegen(bool bCombat = false); //Calculates endurance regen used in DoEnduranceRegen()
|
||||
int32 GetEndurance() const {return current_endurance;} //This gets our current endurance
|
||||
int32 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call
|
||||
int32 CalcEnduranceRegenCap();
|
||||
@ -719,6 +750,7 @@ public:
|
||||
void SendTradeskillDetails(uint32 recipe_id);
|
||||
bool TradeskillExecute(DBTradeskillRecipe_Struct *spec);
|
||||
void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, EQEmu::skills::SkillType tradeskill);
|
||||
void InitInnates();
|
||||
|
||||
void GMKill();
|
||||
inline bool IsMedding() const {return medding;}
|
||||
@ -760,6 +792,9 @@ public:
|
||||
void SummonHorse(uint16 spell_id);
|
||||
void SetHorseId(uint16 horseid_in);
|
||||
uint16 GetHorseId() const { return horseId; }
|
||||
bool CanMedOnHorse();
|
||||
|
||||
bool CanFastRegen() const { return ooc_regen; }
|
||||
|
||||
void NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra = 0);
|
||||
|
||||
@ -862,6 +897,7 @@ public:
|
||||
void SetHunger(int32 in_hunger);
|
||||
void SetThirst(int32 in_thirst);
|
||||
void SetConsumption(int32 in_hunger, int32 in_thirst);
|
||||
bool IsStarved() const { if (GetGM() || !RuleB(Character, EnableHungerPenalties)) return false; return m_pp.hunger_level == 0 || m_pp.thirst_level == 0; }
|
||||
|
||||
bool CheckTradeLoreConflict(Client* other);
|
||||
bool CheckTradeNonDroppable();
|
||||
@ -1345,8 +1381,8 @@ private:
|
||||
int32 CalcCorrup();
|
||||
int32 CalcMaxHP();
|
||||
int32 CalcBaseHP();
|
||||
int32 CalcHPRegen();
|
||||
int32 CalcManaRegen();
|
||||
int32 CalcHPRegen(bool bCombat = false);
|
||||
int32 CalcManaRegen(bool bCombat = false);
|
||||
int32 CalcBaseManaRegen();
|
||||
uint32 GetClassHPFactor();
|
||||
void DoHPRegen();
|
||||
@ -1410,6 +1446,7 @@ private:
|
||||
std::string BuyerWelcomeMessage;
|
||||
bool AbilityTimer;
|
||||
int Haste; //precalced value
|
||||
uint32 tmSitting; // time stamp started sitting, used for HP regen bonus added on MAY 5, 2004
|
||||
|
||||
int32 max_end;
|
||||
int32 current_endurance;
|
||||
@ -1513,9 +1550,10 @@ private:
|
||||
|
||||
unsigned int AggroCount; // How many mobs are aggro on us.
|
||||
|
||||
unsigned int RestRegenHP;
|
||||
unsigned int RestRegenMana;
|
||||
unsigned int RestRegenEndurance;
|
||||
bool ooc_regen;
|
||||
float AreaHPRegen;
|
||||
float AreaManaRegen;
|
||||
float AreaEndRegen;
|
||||
|
||||
bool EngagedRaidTarget;
|
||||
uint32 SavedRaidRestTimer;
|
||||
|
||||
@ -22,6 +22,8 @@
|
||||
#include "../common/rulesys.h"
|
||||
#include "../common/spdat.h"
|
||||
|
||||
#include "../common/data_verification.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "mob.h"
|
||||
|
||||
@ -231,16 +233,81 @@ int32 Client::LevelRegen()
|
||||
return hp;
|
||||
}
|
||||
|
||||
int32 Client::CalcHPRegen()
|
||||
int32 Client::CalcHPRegen(bool bCombat)
|
||||
{
|
||||
int32 regen = LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen;
|
||||
regen += aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration();
|
||||
int item_regen = itembonuses.HPRegen; // worn spells and +regen, already capped
|
||||
item_regen += GetHeroicSTA() / 20;
|
||||
|
||||
item_regen += aabonuses.HPRegen;
|
||||
|
||||
int base = 0;
|
||||
auto base_data = database.GetBaseData(GetLevel(), GetClass());
|
||||
if (base_data)
|
||||
base = static_cast<int>(base_data->hp_regen);
|
||||
|
||||
auto level = GetLevel();
|
||||
bool skip_innate = false;
|
||||
|
||||
if (IsSitting()) {
|
||||
if (level >= 50) {
|
||||
base++;
|
||||
if (level >= 65)
|
||||
base++;
|
||||
}
|
||||
|
||||
if ((Timer::GetCurrentTime() - tmSitting) > 60000) {
|
||||
if (!IsAffectedByBuffByGlobalGroup(GlobalGroup::Lich)) {
|
||||
auto tic_diff = std::min((Timer::GetCurrentTime() - tmSitting) / 60000, static_cast<uint32>(9));
|
||||
if (tic_diff != 1) { // starts at 2 mins
|
||||
int tic_bonus = tic_diff * 1.5 * base;
|
||||
if (m_pp.InnateSkills[InnateRegen] != InnateDisabled)
|
||||
tic_bonus = tic_bonus * 1.2;
|
||||
base = tic_bonus;
|
||||
skip_innate = true;
|
||||
} else if (m_pp.InnateSkills[InnateRegen] == InnateDisabled) { // no innate regen gets first tick
|
||||
int tic_bonus = base * 1.5;
|
||||
base = tic_bonus;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip_innate && m_pp.InnateSkills[InnateRegen] != InnateDisabled) {
|
||||
if (level >= 50) {
|
||||
++base;
|
||||
if (level >= 55)
|
||||
++base;
|
||||
}
|
||||
base *= 2;
|
||||
}
|
||||
|
||||
if (IsStarved())
|
||||
base = 0;
|
||||
|
||||
base += GroupLeadershipAAHealthRegeneration();
|
||||
// some IsKnockedOut that sets to -1
|
||||
base = base * 100.0f * AreaHPRegen * 0.01f + 0.5f;
|
||||
// another check for IsClient && !(base + item_regen) && Cur_HP <= 0 do --base; do later
|
||||
|
||||
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
|
||||
auto fast_mod = RuleI(Character, RestRegenHP); // TODO: this is actually zone based
|
||||
auto max_hp = GetMaxHP();
|
||||
int fast_regen = 6 * (max_hp / fast_mod);
|
||||
if (base < fast_regen) // weird, but what the client is doing
|
||||
base = fast_regen;
|
||||
}
|
||||
|
||||
int regen = base + item_regen + spellbonuses.HPRegen; // TODO: client does this in buff tick
|
||||
return (regen * RuleI(Character, HPRegenMultiplier) / 100);
|
||||
}
|
||||
|
||||
int32 Client::CalcHPRegenCap()
|
||||
{
|
||||
int cap = RuleI(Character, ItemHealthRegenCap) + itembonuses.HeroicSTA / 25;
|
||||
int cap = RuleI(Character, ItemHealthRegenCap);
|
||||
if (GetLevel() > 60)
|
||||
cap = std::max(cap, GetLevel() - 30); // if the rule is set greater than normal I guess
|
||||
if (GetLevel() > 65)
|
||||
cap += GetLevel() - 65;
|
||||
cap += aabonuses.ItemHPRegenCap + spellbonuses.ItemHPRegenCap + itembonuses.ItemHPRegenCap;
|
||||
return (cap * RuleI(Character, HPRegenMultiplier) / 100);
|
||||
}
|
||||
@ -1169,43 +1236,72 @@ int32 Client::CalcBaseManaRegen()
|
||||
return regen;
|
||||
}
|
||||
|
||||
int32 Client::CalcManaRegen()
|
||||
int32 Client::CalcManaRegen(bool bCombat)
|
||||
{
|
||||
uint8 clevel = GetLevel();
|
||||
int32 regen = 0;
|
||||
//this should be changed so we dont med while camping, etc...
|
||||
if (IsSitting() || (GetHorseId() != 0)) {
|
||||
BuffFadeBySitModifier();
|
||||
if (HasSkill(EQEmu::skills::SkillMeditate)) {
|
||||
this->medding = true;
|
||||
regen = (((GetSkill(EQEmu::skills::SkillMeditate) / 10) + (clevel - (clevel / 4))) / 4) + 4;
|
||||
regen += spellbonuses.ManaRegen + itembonuses.ManaRegen;
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillMeditate, nullptr, -5);
|
||||
}
|
||||
else {
|
||||
regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen;
|
||||
int regen = 0;
|
||||
auto level = GetLevel();
|
||||
if (!IsStarved()) {
|
||||
// client does some base regen for shrouds here
|
||||
if (IsSitting() || CanMedOnHorse()) {
|
||||
// kind of weird to do it here w/e
|
||||
// client does some base medding regen for shrouds here
|
||||
if (GetClass() != BARD) {
|
||||
auto skill = GetSkill(EQEmu::skills::SkillMeditate);
|
||||
if (skill > 0) {
|
||||
regen++;
|
||||
if (skill > 1)
|
||||
regen++;
|
||||
if (skill >= 15)
|
||||
regen += skill / 15;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->medding = false;
|
||||
regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen;
|
||||
|
||||
if (level > 61) {
|
||||
regen++;
|
||||
if (level > 63)
|
||||
regen++;
|
||||
}
|
||||
//AAs
|
||||
|
||||
regen += aabonuses.ManaRegen;
|
||||
// add in + 1 bonus for SE_CompleteHeal, but we don't do anything for it yet?
|
||||
|
||||
int item_bonus = itembonuses.ManaRegen; // this is capped already
|
||||
int heroic_bonus = 0;
|
||||
|
||||
switch (GetCasterClass()) {
|
||||
case 'W':
|
||||
heroic_bonus = GetHeroicWIS();
|
||||
break;
|
||||
default:
|
||||
heroic_bonus = GetHeroicINT();
|
||||
break;
|
||||
}
|
||||
|
||||
item_bonus += heroic_bonus / 25;
|
||||
regen += item_bonus;
|
||||
|
||||
if (level <= 70 && regen > 65)
|
||||
regen = 65;
|
||||
|
||||
regen = regen * 100.0f * AreaManaRegen * 0.01f + 0.5f;
|
||||
|
||||
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
|
||||
auto fast_mod = RuleI(Character, RestRegenMana); // TODO: this is actually zone based
|
||||
auto max_mana = GetMaxMana();
|
||||
int fast_regen = 6 * (max_mana / fast_mod);
|
||||
if (regen < fast_regen) // weird, but what the client is doing
|
||||
regen = fast_regen;
|
||||
}
|
||||
|
||||
regen += spellbonuses.ManaRegen; // TODO: live does this in buff tick
|
||||
return (regen * RuleI(Character, ManaRegenMultiplier) / 100);
|
||||
}
|
||||
|
||||
int32 Client::CalcManaRegenCap()
|
||||
{
|
||||
int32 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap;
|
||||
switch (GetCasterClass()) {
|
||||
case 'I':
|
||||
cap += (itembonuses.HeroicINT / 25);
|
||||
break;
|
||||
case 'W':
|
||||
cap += (itembonuses.HeroicWIS / 25);
|
||||
break;
|
||||
}
|
||||
return (cap * RuleI(Character, ManaRegenMultiplier) / 100);
|
||||
}
|
||||
|
||||
@ -2091,16 +2187,91 @@ int32 Client::CalcBaseEndurance()
|
||||
return base_end;
|
||||
}
|
||||
|
||||
int32 Client::CalcEnduranceRegen()
|
||||
int32 Client::CalcEnduranceRegen(bool bCombat)
|
||||
{
|
||||
int32 regen = int32(GetLevel() * 4 / 10) + 2;
|
||||
regen += aabonuses.EnduranceRegen + spellbonuses.EnduranceRegen + itembonuses.EnduranceRegen;
|
||||
int base = 0;
|
||||
if (!IsStarved()) {
|
||||
auto base_data = database.GetBaseData(GetLevel(), GetClass());
|
||||
if (base_data) {
|
||||
base = static_cast<int>(base_data->end_regen);
|
||||
if (!auto_attack && base > 0)
|
||||
base += base / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// so when we are mounted, our local client SpeedRun is always 0, so this is always false, but the packets we process it to our own shit :P
|
||||
bool is_running = runmode && animation != 0 && GetHorseId() == 0; // TODO: animation is really what MQ2 calls SpeedRun
|
||||
|
||||
int weight_limit = GetSTR();
|
||||
auto level = GetLevel();
|
||||
if (GetClass() == MONK) {
|
||||
if (level > 99)
|
||||
weight_limit = 58;
|
||||
else if (level > 94)
|
||||
weight_limit = 57;
|
||||
else if (level > 89)
|
||||
weight_limit = 56;
|
||||
else if (level > 84)
|
||||
weight_limit = 55;
|
||||
else if (level > 79)
|
||||
weight_limit = 54;
|
||||
else if (level > 64)
|
||||
weight_limit = 53;
|
||||
else if (level > 63)
|
||||
weight_limit = 50;
|
||||
else if (level > 61)
|
||||
weight_limit = 47;
|
||||
else if (level > 59)
|
||||
weight_limit = 45;
|
||||
else if (level > 54)
|
||||
weight_limit = 40;
|
||||
else if (level > 50)
|
||||
weight_limit = 38;
|
||||
else if (level > 44)
|
||||
weight_limit = 36;
|
||||
else if (level > 29)
|
||||
weight_limit = 34;
|
||||
else if (level > 14)
|
||||
weight_limit = 32;
|
||||
else
|
||||
weight_limit = 30;
|
||||
}
|
||||
|
||||
bool encumbered = (CalcCurrentWeight() / 10) >= weight_limit;
|
||||
|
||||
if (is_running)
|
||||
base += level / -15;
|
||||
|
||||
if (encumbered)
|
||||
base += level / -15;
|
||||
|
||||
auto item_bonus = GetHeroicAGI() + GetHeroicDEX() + GetHeroicSTA() + GetHeroicSTR();
|
||||
item_bonus = item_bonus / 4 / 50;
|
||||
item_bonus += itembonuses.EnduranceRegen; // this is capped already
|
||||
base += item_bonus;
|
||||
|
||||
base = base * AreaEndRegen + 0.5f;
|
||||
|
||||
auto aa_regen = aabonuses.EnduranceRegen;
|
||||
|
||||
int regen = base;
|
||||
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
|
||||
auto fast_mod = RuleI(Character, RestRegenEnd); // TODO: this is actually zone based
|
||||
auto max_end = GetMaxEndurance();
|
||||
int fast_regen = 6 * (max_end / fast_mod);
|
||||
if (aa_regen < fast_regen) // weird, but what the client is doing
|
||||
aa_regen = fast_regen;
|
||||
}
|
||||
|
||||
regen += aa_regen;
|
||||
regen += spellbonuses.EnduranceRegen; // TODO: client does this in buff tick
|
||||
|
||||
return (regen * RuleI(Character, EnduranceRegenMultiplier) / 100);
|
||||
}
|
||||
|
||||
int32 Client::CalcEnduranceRegenCap()
|
||||
{
|
||||
int cap = (RuleI(Character, ItemEnduranceRegenCap) + itembonuses.HeroicSTR / 25 + itembonuses.HeroicDEX / 25 + itembonuses.HeroicAGI / 25 + itembonuses.HeroicSTA / 25);
|
||||
int cap = RuleI(Character, ItemEnduranceRegenCap);
|
||||
return (cap * RuleI(Character, EnduranceRegenMultiplier) / 100);
|
||||
}
|
||||
|
||||
|
||||
@ -1414,6 +1414,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
||||
if (class_ == MONK)
|
||||
consume_food_timer.SetTimer(CONSUMPTION_MNK_TIMER);
|
||||
|
||||
InitInnates();
|
||||
|
||||
/* If GM not set in DB, and does not meet min status to be GM, reset */
|
||||
if (m_pp.gm && admin < minStatusToBeGM)
|
||||
m_pp.gm = 0;
|
||||
@ -13054,6 +13056,8 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
|
||||
InterruptSpell();
|
||||
SetFeigned(false);
|
||||
BindWound(this, false, true);
|
||||
tmSitting = Timer::GetCurrentTime();
|
||||
BuffFadeBySitModifier();
|
||||
}
|
||||
else if (sa->parameter == ANIM_CROUCH) {
|
||||
if (!UseBardSpellLogic())
|
||||
|
||||
@ -1820,7 +1820,7 @@ void Client::OPGMSummon(const EQApplicationPacket *app)
|
||||
}
|
||||
|
||||
void Client::DoHPRegen() {
|
||||
SetHP(GetHP() + CalcHPRegen() + RestRegenHP);
|
||||
SetHP(GetHP() + CalcHPRegen());
|
||||
SendHPUpdate();
|
||||
}
|
||||
|
||||
@ -1828,7 +1828,10 @@ void Client::DoManaRegen() {
|
||||
if (GetMana() >= max_mana && spellbonuses.ManaRegen >= 0)
|
||||
return;
|
||||
|
||||
SetMana(GetMana() + CalcManaRegen() + RestRegenMana);
|
||||
if (GetMana() < max_mana && (IsSitting() || CanMedOnHorse()) && HasSkill(EQEmu::skills::SkillMeditate))
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillMeditate, nullptr, -5);
|
||||
|
||||
SetMana(GetMana() + CalcManaRegen());
|
||||
CheckManaEndUpdate();
|
||||
}
|
||||
|
||||
@ -1869,10 +1872,11 @@ void Client::DoStaminaHungerUpdate()
|
||||
|
||||
void Client::DoEnduranceRegen()
|
||||
{
|
||||
if(GetEndurance() >= GetMaxEndurance())
|
||||
return;
|
||||
// endurance has some negative mods that could result in a negative regen when starved
|
||||
int regen = CalcEnduranceRegen();
|
||||
|
||||
SetEndurance(GetEndurance() + CalcEnduranceRegen() + RestRegenEndurance);
|
||||
if (regen < 0 || (regen > 0 && GetEndurance() < GetMaxEndurance()))
|
||||
SetEndurance(GetEndurance() + regen);
|
||||
}
|
||||
|
||||
void Client::DoEnduranceUpkeep() {
|
||||
@ -1921,12 +1925,12 @@ void Client::CalcRestState() {
|
||||
// The client must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds,
|
||||
// must be sitting down, and must not have any detrimental spells affecting them.
|
||||
//
|
||||
if(!RuleI(Character, RestRegenPercent))
|
||||
if(!RuleB(Character, RestRegenEnabled))
|
||||
return;
|
||||
|
||||
RestRegenHP = RestRegenMana = RestRegenEndurance = 0;
|
||||
ooc_regen = false;
|
||||
|
||||
if(AggroCount || !IsSitting())
|
||||
if(AggroCount || !(IsSitting() || CanMedOnHorse()))
|
||||
return;
|
||||
|
||||
if(!rest_timer.Check(false))
|
||||
@ -1941,12 +1945,8 @@ void Client::CalcRestState() {
|
||||
}
|
||||
}
|
||||
|
||||
RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100);
|
||||
ooc_regen = true;
|
||||
|
||||
RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100);
|
||||
|
||||
if(RuleB(Character, RestRegenEndurance))
|
||||
RestRegenEndurance = (GetMaxEndurance() * RuleI(Character, RestRegenPercent) / 100);
|
||||
}
|
||||
|
||||
void Client::DoTracking()
|
||||
|
||||
@ -151,6 +151,7 @@ void Client::SummonHorse(uint16 spell_id) {
|
||||
|
||||
uint16 tmpID = horse->GetID();
|
||||
SetHorseId(tmpID);
|
||||
BuffFadeBySitModifier();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1154,7 +1154,7 @@ void Merc::CalcRestState() {
|
||||
// The bot must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds,
|
||||
// must be sitting down, and must not have any detrimental spells affecting them.
|
||||
//
|
||||
if(!RuleI(Character, RestRegenPercent))
|
||||
if(!RuleB(Character, RestRegenEnabled))
|
||||
return;
|
||||
|
||||
RestRegenHP = RestRegenMana = RestRegenEndurance = 0;
|
||||
@ -1174,12 +1174,11 @@ void Merc::CalcRestState() {
|
||||
}
|
||||
}
|
||||
|
||||
RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100);
|
||||
RestRegenHP = 6 * (GetMaxHP() / RuleI(Character, RestRegenHP));
|
||||
|
||||
RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100);
|
||||
RestRegenMana = 6 * (GetMaxMana() / RuleI(Character, RestRegenMana));
|
||||
|
||||
if(RuleB(Character, RestRegenEndurance))
|
||||
RestRegenEndurance = (GetMaxEndurance() * RuleI(Character, RestRegenPercent) / 100);
|
||||
RestRegenEndurance = 6 * (GetMaxEndurance() / RuleI(Character, RestRegenEnd));
|
||||
}
|
||||
|
||||
bool Merc::HasSkill(EQEmu::skills::SkillType skill_id) const {
|
||||
|
||||
@ -336,6 +336,7 @@ public:
|
||||
void BuffFadeDetrimentalByCaster(Mob *caster);
|
||||
void BuffFadeBySitModifier();
|
||||
bool IsAffectedByBuff(uint16 spell_id);
|
||||
bool IsAffectedByBuffByGlobalGroup(GlobalGroup group);
|
||||
void BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration);
|
||||
int AddBuff(Mob *caster, const uint16 spell_id, int duration = 0, int32 level_override = -1);
|
||||
int CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite = false);
|
||||
|
||||
@ -60,6 +60,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
|
||||
const SPDat_Spell_Struct &spell = spells[spell_id];
|
||||
|
||||
if (spell.disallow_sit && IsBuffSpell(spell_id) && IsClient() && (CastToClient()->IsSitting() || CastToClient()->GetHorseId() != 0))
|
||||
return false;
|
||||
|
||||
bool c_override = false;
|
||||
if (caster && caster->IsClient() && GetCastedSpellInvSlot() > 0) {
|
||||
const EQEmu::ItemInstance *inst = caster->CastToClient()->GetInv().GetItem(GetCastedSpellInvSlot());
|
||||
|
||||
@ -4234,6 +4234,19 @@ bool Mob::IsAffectedByBuff(uint16 spell_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::IsAffectedByBuffByGlobalGroup(GlobalGroup group)
|
||||
{
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
for (int i = 0; i < buff_count; ++i) {
|
||||
if (buffs[i].spellid == SPELL_UNKNOWN)
|
||||
continue;
|
||||
if (spells[buffs[i].spellid].spell_category == static_cast<int>(group))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// checks if 'this' can be affected by spell_id from caster
|
||||
// returns true if the spell should fail, false otherwise
|
||||
bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user