Merge branch 'master' into eqstream

This commit is contained in:
KimLS
2017-02-06 21:46:34 -08:00
57 changed files with 6422 additions and 2572 deletions
+1134 -755
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -35,9 +35,9 @@ public:
//abstract virtual function implementations requird by base abstract class
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill) { return true; }
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, int special = 0) { return; }
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) { return; }
virtual bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false,
ExtraAttackOptions *opts = nullptr, int special = 0) { return false; }
ExtraAttackOptions *opts = nullptr) { return false; }
virtual bool HasRaid() { return false; }
virtual bool HasGroup() { return false; }
virtual Raid* GetRaid() { return 0; }
+35 -20
View File
@@ -46,6 +46,7 @@ void Mob::CalcBonuses()
CalcMaxHP();
CalcMaxMana();
SetAttackTimer();
CalcAC();
rooted = FindType(SE_Root);
}
@@ -669,6 +670,10 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
}
switch (effect) {
case SE_ACv2:
case SE_ArmorClass:
newbon->AC += base1;
break;
// Note: AA effects that use accuracy are skill limited, while spell effect is not.
case SE_Accuracy:
// Bad data or unsupported new skill
@@ -1308,8 +1313,9 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
}
case SE_HeadShotLevel: {
if (newbon->HSLevel < base1)
newbon->HSLevel = base1;
if (newbon->HSLevel[0] < base1)
newbon->HSLevel[0] = base1;
newbon->HSLevel[1] = base2;
break;
}
@@ -1322,8 +1328,10 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
}
case SE_AssassinateLevel: {
if (newbon->AssassinateLevel < base1)
newbon->AssassinateLevel = base1;
if (newbon->AssassinateLevel[0] < base1) {
newbon->AssassinateLevel[0] = base1;
newbon->AssassinateLevel[1] = base2;
}
break;
}
@@ -1381,7 +1389,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
}
case SE_MeleeMitigation:
newbon->MeleeMitigationEffect -= base1;
newbon->MeleeMitigationEffect += base1;
break;
case SE_ATK:
@@ -1527,9 +1535,6 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
}
}
// THIS IS WRONG, leaving for now
//this prolly suffer from roundoff error slightly...
newbon->AC = newbon->AC * 10 / 34; //ratio determined impirically from client.
if (GetClass() == BARD)
newbon->ManaRegen = 0; // Bards do not get mana regen from spells.
}
@@ -1927,8 +1932,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
break;
case SE_MeleeMitigation:
//for some reason... this value is negative for increased mitigation
new_bonus->MeleeMitigationEffect -= effect_value;
// This value is negative because it counteracts another SPA :P
new_bonus->MeleeMitigationEffect += effect_value;
break;
case SE_CriticalHitChance:
@@ -3024,8 +3029,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
case SE_HeadShotLevel:
{
if(new_bonus->HSLevel < effect_value)
new_bonus->HSLevel = effect_value;
if(new_bonus->HSLevel[0] < effect_value) {
new_bonus->HSLevel[0] = effect_value;
new_bonus->HSLevel[1] = base2;
}
break;
}
@@ -3040,8 +3047,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
case SE_AssassinateLevel:
{
if(new_bonus->AssassinateLevel < effect_value)
new_bonus->AssassinateLevel = effect_value;
if(new_bonus->AssassinateLevel[0] < effect_value) {
new_bonus->AssassinateLevel[0] = effect_value;
new_bonus->AssassinateLevel[1] = base2;
}
break;
}
@@ -4647,9 +4656,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
break;
case SE_HeadShotLevel:
spellbonuses.HSLevel = effect_value;
aabonuses.HSLevel = effect_value;
itembonuses.HSLevel = effect_value;
spellbonuses.HSLevel[0] = effect_value;
aabonuses.HSLevel[0] = effect_value;
itembonuses.HSLevel[0] = effect_value;
spellbonuses.HSLevel[1] = effect_value;
aabonuses.HSLevel[1] = effect_value;
itembonuses.HSLevel[1] = effect_value;
break;
case SE_Assassinate:
@@ -4662,9 +4674,12 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
break;
case SE_AssassinateLevel:
spellbonuses.AssassinateLevel = effect_value;
aabonuses.AssassinateLevel = effect_value;
itembonuses.AssassinateLevel = effect_value;
spellbonuses.AssassinateLevel[0] = effect_value;
aabonuses.AssassinateLevel[0] = effect_value;
itembonuses.AssassinateLevel[0] = effect_value;
spellbonuses.AssassinateLevel[1] = effect_value;
aabonuses.AssassinateLevel[1] = effect_value;
itembonuses.AssassinateLevel[1] = effect_value;
break;
case SE_FinishingBlow:
+320 -463
View File
File diff suppressed because it is too large Load Diff
+7 -6
View File
@@ -206,9 +206,9 @@ public:
//abstract virtual function implementations requird by base abstract class
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill);
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, int special = 0);
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None);
virtual bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false,
ExtraAttackOptions *opts = nullptr, int special = 0);
ExtraAttackOptions *opts = nullptr);
virtual bool HasRaid() { return (GetRaid() ? true : false); }
virtual bool HasGroup() { return (GetGroup() ? true : false); }
virtual Raid* GetRaid() { return entity_list.GetRaidByMob(this); }
@@ -239,7 +239,7 @@ public:
uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; }
virtual float GetProcChances(float ProcBonus, uint16 hand);
virtual int GetHandToHandDamage(void);
virtual bool TryFinishingBlow(Mob *defender, EQEmu::skills::SkillType skillinuse);
virtual bool TryFinishingBlow(Mob *defender, int &damage);
virtual void DoRiposte(Mob* defender);
inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(EQEmu::skills::SkillOffense)) * 9 / 10); }
inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; }
@@ -248,13 +248,12 @@ public:
uint16 GetPrimarySkillValue();
uint16 MaxSkill(EQEmu::skills::SkillType skillid, uint16 class_, uint16 level) const;
inline uint16 MaxSkill(EQEmu::skills::SkillType skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); }
virtual int GetBaseSkillDamage(EQEmu::skills::SkillType skill, Mob *target = nullptr);
virtual void DoSpecialAttackDamage(Mob *who, EQEmu::skills::SkillType skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance = false);
virtual void TryBackstab(Mob *other,int ReuseTime = 10);
virtual void RogueBackstab(Mob* other, bool min_damage = false, int ReuseTime = 10);
virtual void RogueAssassinate(Mob* other);
virtual void DoClassAttacks(Mob *target, bool IsRiposte=false);
virtual bool TryHeadShot(Mob* defender, EQEmu::skills::SkillType skillInUse);
virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, EQEmu::skills::SkillType skillinuse, int16 chance_mod = 0, int16 focus = 0, bool CanRiposte = false, int ReuseTime = 0);
virtual void ApplySpecialAttackMod(EQEmu::skills::SkillType skill, int32 &dmg, int32 &mindmg);
bool CanDoSpecialAttack(Mob *other);
virtual int32 CheckAggroAmount(uint16 spellid);
@@ -294,6 +293,7 @@ public:
bool UseDiscipline(uint32 spell_id, uint32 target);
uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets);
bool GetNeedsCured(Mob *tar);
bool GetNeedsHateRedux(Mob *tar);
bool HasOrMayGetAggro();
void SetDefaultBotStance();
void CalcChanceToCast();
@@ -503,7 +503,6 @@ public:
bool GetAltOutOfCombatBehavior() { return _altoutofcombatbehavior;}
bool GetShowHelm() { return _showhelm; }
inline virtual int32 GetAC() const { return AC; }
inline virtual int32 GetSTR() const { return STR; }
inline virtual int32 GetSTA() const { return STA; }
inline virtual int32 GetDEX() const { return DEX; }
@@ -672,6 +671,8 @@ private:
glm::vec3 m_PreSummonLocation;
uint8 _spellCastingChances[MaxStances][MaxSpellTypes];
Timer evade_timer;
std::shared_ptr<HealRotation> m_member_of_heal_rotation;
std::map<uint32, BotAA> botAAs;
+2
View File
@@ -436,6 +436,8 @@ public:
case 10:
if (spells[spell_id].effectdescnum != 65)
break;
if (IsEffectInSpell(spell_id, SE_NegateIfCombat))
break;
entry_prototype = new STMovementSpeedEntry();
entry_prototype->SafeCastToMovementSpeed()->group = BCSpells::IsGroupType(target_type);
break;
+3 -1
View File
@@ -617,7 +617,8 @@ bool BotDatabase::LoadBuffs(Bot* bot_inst)
" `caston_x`,"
" `caston_y`,"
" `caston_z`,"
" `extra_di_chance`"
" `extra_di_chance`,"
" `instrument_mod`"
" FROM `bot_buffs`"
" WHERE `bot_id` = '%u'",
bot_inst->GetBotID()
@@ -657,6 +658,7 @@ bool BotDatabase::LoadBuffs(Bot* bot_inst)
bot_buffs[buff_count].caston_y = atoi(row[14]);
bot_buffs[buff_count].caston_z = atoi(row[15]);
bot_buffs[buff_count].ExtraDIChance = atoi(row[16]);
bot_buffs[buff_count].instrument_mod = atoi(row[17]);
bot_buffs[buff_count].casterid = 0;
++buff_count;
}
+71 -11
View File
@@ -33,6 +33,7 @@ extern volatile bool RunLoops;
#include "../common/eqemu_logsys.h"
#include "../common/features.h"
#include "../common/emu_legacy.h"
#include "../common/spdat.h"
#include "../common/guilds.h"
#include "../common/rulesys.h"
@@ -1997,6 +1998,7 @@ void Client::SetGM(bool toggle) {
}
void Client::ReadBook(BookRequest_Struct *book) {
int16 book_language=0;
char *txtfile = book->txtfile;
if(txtfile[0] == '0' && txtfile[1] == '\0') {
@@ -2004,7 +2006,7 @@ void Client::ReadBook(BookRequest_Struct *book) {
return;
}
std::string booktxt2 = database.GetBook(txtfile);
std::string booktxt2 = database.GetBook(txtfile, &book_language);
int length = booktxt2.length();
if (booktxt2[0] != '\0') {
@@ -2015,21 +2017,47 @@ void Client::ReadBook(BookRequest_Struct *book) {
BookText_Struct *out = (BookText_Struct *) outapp->pBuffer;
out->window = book->window;
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF)
{
const EQEmu::ItemInstance *inst = m_inv[book->invslot];
if (ClientVersion() >= EQEmu::versions::ClientVersion::SoF) {
// Find out what slot the book was read from.
// SoF+ need to look up book type for the output message.
int16 read_from_slot;
if (book->subslot >= 0) {
uint16 offset;
offset = (book->invslot-23) * 10; // How many packs to skip.
read_from_slot = 251 + offset + book->subslot;
}
else {
read_from_slot = book->invslot -1;
}
const EQEmu::ItemInstance *inst = 0;
if (read_from_slot <= EQEmu::legacy::SLOT_PERSONAL_BAGS_END)
{
inst = m_inv[read_from_slot];
}
if(inst)
out->type = inst->GetItem()->Book;
else
out->type = book->type;
}
else
{
else {
out->type = book->type;
}
out->invslot = book->invslot;
memcpy(out->booktext, booktxt2.c_str(), length);
if (book_language > 0 && book_language < MAX_PP_LANGUAGE) {
if (m_pp.languages[book_language] < 100) {
GarbleMessage(out->booktext, (100 - m_pp.languages[book_language]));
}
}
QueuePacket(outapp);
safe_delete(outapp);
}
@@ -6861,16 +6889,46 @@ void Client::SendStatsWindow(Client* client, bool use_window)
indP + "Wind: " + itoa(GetWindMod()) + "<br>";
}
EQEmu::skills::SkillType skill = EQEmu::skills::SkillHandtoHand;
auto *inst = GetInv().GetItem(EQEmu::inventory::slotPrimary);
if (inst && inst->IsClassCommon()) {
switch (inst->GetItem()->ItemType) {
case EQEmu::item::ItemType1HSlash:
skill = EQEmu::skills::Skill1HSlashing;
break;
case EQEmu::item::ItemType2HSlash:
skill = EQEmu::skills::Skill2HSlashing;
break;
case EQEmu::item::ItemType1HPiercing:
skill = EQEmu::skills::Skill1HPiercing;
break;
case EQEmu::item::ItemType1HBlunt:
skill = EQEmu::skills::Skill1HBlunt;
break;
case EQEmu::item::ItemType2HBlunt:
skill = EQEmu::skills::Skill2HBlunt;
break;
case EQEmu::item::ItemType2HPiercing:
if (ClientVersion() < EQEmu::versions::ClientVersion::RoF2)
skill = EQEmu::skills::Skill1HPiercing;
else
skill = EQEmu::skills::Skill2HPiercing;
break;
default:
break;
}
}
std::ostringstream final_string;
final_string <<
/* C/L/R */ indP << "Class: " << class_Name << indS << "Level: " << static_cast<int>(GetLevel()) << indS << "Race: " << race_Name << "<br>" <<
/* Runes */ indP << "Rune: " << rune_number << indL << indS << "Spell Rune: " << magic_rune_number << "<br>" <<
/* HP/M/E */ HME_row <<
/* DS */ indP << "DS: " << (itembonuses.DamageShield + spellbonuses.DamageShield*-1) << " (Spell: " << (spellbonuses.DamageShield*-1) << " + Item: " << itembonuses.DamageShield << " / " << RuleI(Character, ItemDamageShieldCap) << ")<br>" <<
/* Atk */ indP << "<c \"#CCFF00\">ATK: " << GetTotalATK() << "</c><br>" <<
/* Atk2 */ indP << "- Base: " << GetATKRating() << " | Item: " << itembonuses.ATK << " (" << RuleI(Character, ItemATKCap) << ")~Used: " << (itembonuses.ATK * 1.342) << " | Spell: " << spellbonuses.ATK << "<br>" <<
/* AC */ indP << "<c \"#CCFF00\">AC: " << CalcAC() << "</c><br>" <<
/* AC2 */ indP << "- Mit: " << GetACMit() << " | Avoid: " << GetACAvoid() << " | Spell: " << spellbonuses.AC << " | Shield: " << shield_ac << "<br>" <<
/* Atk */ indP << "<c \"#CCFF00\">tohit: " << compute_tohit(skill) << " / " << GetTotalToHit(skill, 0) << "</c><br>" <<
/* Atk2 */ indP << "- Offense: " << offense(skill) << " | Item: " << itembonuses.ATK << " (" << RuleI(Character, ItemATKCap) << ")~Used: " << (itembonuses.ATK * 1.342) << " | Spell: " << spellbonuses.ATK << "<br>" <<
/* AC */ indP << "<c \"#CCFF00\">mitigation AC: " << GetMitigationAC() << "</c><br>" <<
/* AC2 */ indP << "- defense: " << compute_defense() << " / " << GetTotalDefense() << " | Spell: " << spellbonuses.AC << " | Shield: " << shield_ac << "<br>" <<
/* Haste */ indP << "<c \"#CCFF00\">Haste: " << GetHaste() << "</c><br>" <<
/* Haste2 */ indP << " - Item: " << itembonuses.haste << " + Spell: " << (spellbonuses.haste + spellbonuses.hastetype2) << " (Cap: " << RuleI(Character, HasteCap) << ") | Over: " << (spellbonuses.hastetype3 + ExtraHaste) << "<br>" <<
/* RunSpeed*/ indP << "<c \"#CCFF00\">Runspeed: " << GetRunspeed() << "</c><br>" <<
@@ -6908,7 +6966,9 @@ void Client::SendStatsWindow(Client* client, bool use_window)
client->Message(15, "~~~~~ %s %s ~~~~~", GetCleanName(), GetLastName());
client->Message(0, " Level: %i Class: %i Race: %i DS: %i/%i Size: %1.1f Weight: %.1f/%d ", GetLevel(), GetClass(), GetRace(), GetDS(), RuleI(Character, ItemDamageShieldCap), GetSize(), (float)CalcCurrentWeight() / 10.0f, GetSTR());
client->Message(0, " HP: %i/%i HP Regen: %i/%i",GetHP(), GetMaxHP(), CalcHPRegen(), CalcHPRegenCap());
client->Message(0, " AC: %i ( Mit.: %i + Avoid.: %i + Spell: %i ) | Shield AC: %i", CalcAC(), GetACMit(), GetACAvoid(), spellbonuses.AC, shield_ac);
client->Message(0, " compute_tohit: %i TotalToHit: %i", compute_tohit(skill), GetTotalToHit(skill, 0));
client->Message(0, " compute_defense: %i TotalDefense: %i", compute_defense(), GetTotalDefense());
client->Message(0, " offense: %i mitigation ac: %i", offense(skill), GetMitigationAC());
if(CalcMaxMana() > 0)
client->Message(0, " Mana: %i/%i Mana Regen: %i/%i", GetMana(), GetMaxMana(), CalcManaRegen(), CalcManaRegenCap());
client->Message(0, " End.: %i/%i End. Regen: %i/%i",GetEndurance(), GetMaxEndurance(), CalcEnduranceRegen(), CalcEnduranceRegenCap());
+4 -9
View File
@@ -221,18 +221,18 @@ public:
//abstract virtual function implementations required by base abstract class
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill);
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, int special = 0);
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None);
virtual bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false,
ExtraAttackOptions *opts = nullptr, int special = 0);
ExtraAttackOptions *opts = nullptr);
virtual bool HasRaid() { return (GetRaid() ? true : false); }
virtual bool HasGroup() { return (GetGroup() ? true : false); }
virtual Raid* GetRaid() { return entity_list.GetRaidByClient(this); }
virtual Group* GetGroup() { return entity_list.GetGroupByClient(this); }
virtual inline bool IsBerserk() { return berserk; }
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
virtual void SetAttackTimer();
int GetQuiverHaste(int delay);
void DoAttackRounds(Mob *target, int hand, bool IsFromSpell = false);
int DoDamageCaps(int base_damage);
void AI_Init();
void AI_Start(uint32 iMoveDelay = 0);
@@ -418,8 +418,6 @@ public:
virtual void CalcBonuses();
//these are all precalculated now
inline virtual int32 GetAC() const { return AC; }
inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(EQEmu::skills::SkillOffense)) * 9 / 10); }
inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; }
inline virtual int GetHaste() const { return Haste; }
int GetRawACNoShield(int &shield_ac) const;
@@ -1292,7 +1290,7 @@ private:
void OPGMEndTraining(const EQApplicationPacket *app);
void OPGMTrainSkill(const EQApplicationPacket *app);
void OPGMSummon(const EQApplicationPacket *app);
void OPCombatAbility(const EQApplicationPacket *app);
void OPCombatAbility(const CombatAbility_Struct *ca_atk);
// Bandolier Methods
void CreateBandolier(const EQApplicationPacket *app);
@@ -1301,9 +1299,6 @@ private:
void HandleTraderPriceUpdate(const EQApplicationPacket *app);
int32 CalcAC();
int32 GetACMit();
int32 GetACAvoid();
int32 CalcATK();
int32 CalcItemATKCap();
int32 CalcHaste();
-105
View File
@@ -1025,111 +1025,6 @@ int32 Client::acmod()
return 0;
};
// This is a testing formula for AC, the value this returns should be the same value as the one the client shows...
// ac1 and ac2 are probably the damage migitation and damage avoidance numbers, not sure which is which.
// I forgot to include the iksar defense bonus and i cant find my notes now...
// AC from spells are not included (cant even cast spells yet..)
int32 Client::CalcAC()
{
// new formula
int avoidance = (acmod() + ((GetSkill(EQEmu::skills::SkillDefense) + itembonuses.HeroicAGI / 10) * 16) / 9);
if (avoidance < 0) {
avoidance = 0;
}
if (RuleB(Character, EnableAvoidanceCap)) {
if (avoidance > RuleI(Character, AvoidanceCap)) {
avoidance = RuleI(Character, AvoidanceCap);
}
}
int mitigation = 0;
if (m_pp.class_ == WIZARD || m_pp.class_ == MAGICIAN || m_pp.class_ == NECROMANCER || m_pp.class_ == ENCHANTER) {
//something is wrong with this, naked casters have the wrong natural AC
// mitigation = (spellbonuses.AC/3) + (GetSkill(DEFENSE)/2) + (itembonuses.AC+1);
mitigation = (GetSkill(EQEmu::skills::SkillDefense) + itembonuses.HeroicAGI / 10) / 4 + (itembonuses.AC + 1);
//this might be off by 4..
mitigation -= 4;
}
else {
// mitigation = (spellbonuses.AC/4) + (GetSkill(DEFENSE)/3) + ((itembonuses.AC*4)/3);
mitigation = (GetSkill(EQEmu::skills::SkillDefense) + itembonuses.HeroicAGI / 10) / 3 + ((itembonuses.AC * 4) / 3);
if (m_pp.class_ == MONK) {
mitigation += GetLevel() * 13 / 10; //the 13/10 might be wrong, but it is close...
}
}
int displayed = 0;
displayed += ((avoidance + mitigation) * 1000) / 847; //natural AC
//Iksar AC, untested
if (GetRace() == IKSAR) {
displayed += 12;
int iksarlevel = GetLevel();
iksarlevel -= 10;
if (iksarlevel > 25) {
iksarlevel = 25;
}
if (iksarlevel > 0) {
displayed += iksarlevel * 12 / 10;
}
}
// Shield AC bonus for HeroicSTR
if (itembonuses.HeroicSTR) {
bool equiped = CastToClient()->m_inv.GetItem(EQEmu::inventory::slotSecondary);
if (equiped) {
uint8 shield = CastToClient()->m_inv.GetItem(EQEmu::inventory::slotSecondary)->GetItem()->ItemType;
if (shield == EQEmu::item::ItemTypeShield) {
displayed += itembonuses.HeroicSTR / 2;
}
}
}
//spell AC bonuses are added directly to natural total
displayed += spellbonuses.AC;
AC = displayed;
return (AC);
}
int32 Client::GetACMit()
{
int mitigation = 0;
if (m_pp.class_ == WIZARD || m_pp.class_ == MAGICIAN || m_pp.class_ == NECROMANCER || m_pp.class_ == ENCHANTER) {
mitigation = (GetSkill(EQEmu::skills::SkillDefense) + itembonuses.HeroicAGI / 10) / 4 + (itembonuses.AC + 1);
mitigation -= 4;
}
else {
mitigation = (GetSkill(EQEmu::skills::SkillDefense) + itembonuses.HeroicAGI / 10) / 3 + ((itembonuses.AC * 4) / 3);
if (m_pp.class_ == MONK) {
mitigation += GetLevel() * 13 / 10; //the 13/10 might be wrong, but it is close...
}
}
// Shield AC bonus for HeroicSTR
if (itembonuses.HeroicSTR) {
bool equiped = CastToClient()->m_inv.GetItem(EQEmu::inventory::slotSecondary);
if (equiped) {
uint8 shield = CastToClient()->m_inv.GetItem(EQEmu::inventory::slotSecondary)->GetItem()->ItemType;
if (shield == EQEmu::item::ItemTypeShield) {
mitigation += itembonuses.HeroicSTR / 2;
}
}
}
return (mitigation * 1000 / 847);
}
int32 Client::GetACAvoid()
{
int32 avoidance = (acmod() + ((GetSkill(EQEmu::skills::SkillDefense) + itembonuses.HeroicAGI / 10) * 16) / 9);
if (avoidance < 0) {
avoidance = 0;
}
if (RuleB(Character, EnableAvoidanceCap)) {
if ((avoidance * 1000 / 847) > RuleI(Character, AvoidanceCap)) {
return RuleI(Character, AvoidanceCap);
}
}
return (avoidance * 1000 / 847);
}
int32 Client::CalcMaxMana()
{
switch (GetCasterClass()) {
+2 -1
View File
@@ -4621,7 +4621,8 @@ void Client::Handle_OP_CombatAbility(const EQApplicationPacket *app)
std::cout << "Wrong size on OP_CombatAbility. Got: " << app->size << ", Expected: " << sizeof(CombatAbility_Struct) << std::endl;
return;
}
OPCombatAbility(app);
auto ca_atk = (CombatAbility_Struct *)app->pBuffer;
OPCombatAbility(ca_atk);
return;
}
+27 -2
View File
@@ -35,6 +35,13 @@
#define CON_YELLOW 15
#define CON_RED 13
#define DMG_BLOCKED -1
#define DMG_PARRIED -2
#define DMG_RIPOSTED -3
#define DMG_DODGED -4
#define DMG_INVULNERABLE -5
#define DMG_RUNE -6
//Spell specialization parameters, not sure of a better place for them
#define SPECIALIZE_FIZZLE 11 //% fizzle chance reduce at 200 specialized
#define SPECIALIZE_MANA_REDUCE 12 //% mana cost reduction at 200 specialized
@@ -464,9 +471,9 @@ struct StatBonuses {
int8 CriticalMend; // chance critical monk mend
int32 ImprovedReclaimEnergy; // Modifies amount of mana returned from reclaim energy
uint32 HeadShot[2]; // Headshot AA (Massive dmg vs humaniod w/ archery) 0= ? 1= Dmg
uint8 HSLevel; // Max Level Headshot will be effective at.
uint8 HSLevel[2]; // Max Level Headshot will be effective at. and chance mod
uint32 Assassinate[2]; // Assassinate AA (Massive dmg vs humaniod w/ assassinate) 0= ? 1= Dmg
uint8 AssassinateLevel; // Max Level Assassinate will be effective at.
uint8 AssassinateLevel[2]; // Max Level Assassinate will be effective at.
int32 PetMeleeMitigation; // Add AC to owner's pet.
bool IllusionPersistence; // Causes illusions not to fade.
uint16 extra_xtargets; // extra xtarget entries
@@ -638,5 +645,23 @@ struct ExtraAttackOptions {
};
struct DamageTable {
int32 max_extra; // max extra damage
int32 chance; // chance not to apply?
int32 minusfactor; // difficulty of rolling
};
struct DamageHitInfo {
//uint16 attacker; // id
//uint16 defender; // id
int base_damage;
int min_damage;
int damage_done;
int offense;
int tohit;
int hand;
EQEmu::skills::SkillType skill;
};
#endif
+2 -2
View File
@@ -52,8 +52,8 @@ class Corpse : public Mob {
/* Corpse: General */
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill) { return true; }
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, int special = 0) { return; }
virtual bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = true, bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr, int special = 0) { return false; }
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) { return; }
virtual bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = true, bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) { return false; }
virtual bool HasRaid() { return false; }
virtual bool HasGroup() { return false; }
virtual Raid* GetRaid() { return 0; }
+2 -2
View File
@@ -35,9 +35,9 @@ public:
//abstract virtual function implementations required by base abstract class
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill) { return true; }
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, int special = 0) { return; }
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) { return; }
virtual bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false,
ExtraAttackOptions *opts = nullptr, int special = 0) {
ExtraAttackOptions *opts = nullptr) {
return false;
}
virtual bool HasRaid() { return false; }
+1 -1
View File
@@ -553,7 +553,7 @@ int HateList::AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOption
auto mob = entity_list.GetMobID(id);
if (mob) {
++hit_count;
caster->ProcessAttackRounds(mob, opts, 1);
caster->ProcessAttackRounds(mob, opts);
}
}
+3 -1
View File
@@ -332,8 +332,10 @@ void NPC::AddLootDrop(const EQEmu::ItemData *item2, ItemList* itemlist, int16 ch
CastToMob()->AddProcToWeapon(item2->Proc.Effect, true);
eslot = EQEmu::textures::weaponPrimary;
if (item2->Damage > 0)
if (item2->Damage > 0) {
SendAddPlayerState(PlayerState::PrimaryWeaponEquipped);
SetFacestab(true);
}
if (item2->IsType2HWeapon())
SetTwoHanderEquipped(true);
}
-7
View File
@@ -1270,12 +1270,6 @@ void Lua_Mob::DoSpecialAttackDamage(Lua_Mob other, int skill, int max_damage, in
self->DoSpecialAttackDamage(other, static_cast<EQEmu::skills::SkillType>(skill), max_damage, min_damage, hate_override, reuse_time);
}
void Lua_Mob::DoSpecialAttackDamage(Lua_Mob other, int skill, int max_damage, int min_damage, int hate_override, int reuse_time,
bool hit_chance) {
Lua_Safe_Call_Void();
self->DoSpecialAttackDamage(other, static_cast<EQEmu::skills::SkillType>(skill), max_damage, min_damage, hate_override, reuse_time, hit_chance);
}
void Lua_Mob::DoThrowingAttackDmg(Lua_Mob other) {
Lua_Safe_Call_Void();
self->DoThrowingAttackDmg(other);
@@ -2218,7 +2212,6 @@ luabind::scope lua_register_mob() {
.def("DoSpecialAttackDamage", (void(Lua_Mob::*)(Lua_Mob,int,int,int))&Lua_Mob::DoSpecialAttackDamage)
.def("DoSpecialAttackDamage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,int))&Lua_Mob::DoSpecialAttackDamage)
.def("DoSpecialAttackDamage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,int,int))&Lua_Mob::DoSpecialAttackDamage)
.def("DoSpecialAttackDamage", (void(Lua_Mob::*)(Lua_Mob,int,int,int,int,int,bool))&Lua_Mob::DoSpecialAttackDamage)
.def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::DoThrowingAttackDmg)
.def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst))&Lua_Mob::DoThrowingAttackDmg)
.def("DoThrowingAttackDmg", (void(Lua_Mob::*)(Lua_Mob,Lua_ItemInst,Lua_Item))&Lua_Mob::DoThrowingAttackDmg)
-1
View File
@@ -267,7 +267,6 @@ public:
void DoSpecialAttackDamage(Lua_Mob other, int skill, int max_damage, int min_damage);
void DoSpecialAttackDamage(Lua_Mob other, int skill, int max_damage, int min_damage, int hate_override);
void DoSpecialAttackDamage(Lua_Mob other, int skill, int max_damage, int min_damage, int hate_override, int reuse_time);
void DoSpecialAttackDamage(Lua_Mob other, int skill, int max_damage, int min_damage, int hate_override, int reuse_time, bool hit_chance);
void DoThrowingAttackDmg(Lua_Mob other);
void DoThrowingAttackDmg(Lua_Mob other, Lua_ItemInst range_weapon);
void DoThrowingAttackDmg(Lua_Mob other, Lua_ItemInst range_weapon, Lua_Item item);
+8 -27
View File
@@ -4426,20 +4426,10 @@ void Merc::DoClassAttacks(Mob *target) {
if(zone->random.Int(0, 100) > 25) //tested on live, warrior mobs both kick and bash, kick about 75% of the time, casting doesn't seem to make a difference.
{
DoAnim(animKick);
int32 dmg = 0;
int32 dmg = GetBaseSkillDamage(EQEmu::skills::SkillKick);
if (GetWeaponDamage(target, (const EQEmu::ItemData*)nullptr) <= 0){
dmg = -5;
}
else{
if (target->CheckHitChance(this, EQEmu::skills::SkillKick, 0)) {
if(RuleB(Combat, UseIntervalAC))
dmg = GetKickDamage();
else
dmg = zone->random.Int(1, GetKickDamage());
}
}
if (GetWeaponDamage(target, (const EQEmu::ItemData*)nullptr) <= 0)
dmg = DMG_INVULNERABLE;
reuse = KickReuseTime * 1000;
DoSpecialAttackDamage(target, EQEmu::skills::SkillKick, dmg, 1, -1, reuse);
@@ -4448,19 +4438,10 @@ void Merc::DoClassAttacks(Mob *target) {
else
{
DoAnim(animTailRake);
int32 dmg = 0;
int32 dmg = GetBaseSkillDamage(EQEmu::skills::SkillBash);
if (GetWeaponDamage(target, (const EQEmu::ItemData*)nullptr) <= 0){
dmg = -5;
}
else{
if (target->CheckHitChance(this, EQEmu::skills::SkillBash, 0)) {
if(RuleB(Combat, UseIntervalAC))
dmg = GetBashDamage();
else
dmg = zone->random.Int(1, GetBashDamage());
}
}
if (GetWeaponDamage(target, (const EQEmu::ItemData*)nullptr) <= 0)
dmg = DMG_INVULNERABLE;
reuse = BashReuseTime * 1000;
DoSpecialAttackDamage(target, EQEmu::skills::SkillBash, dmg, 1, -1, reuse);
@@ -4474,7 +4455,7 @@ void Merc::DoClassAttacks(Mob *target) {
classattack_timer.Start(reuse / HasteModifier);
}
bool Merc::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts, int special)
bool Merc::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts)
{
if (!other) {
SetTarget(nullptr);
@@ -4485,7 +4466,7 @@ bool Merc::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, boo
return NPC::Attack(other, Hand, bRiposte, IsStrikethrough, IsFromSpell, opts);
}
void Merc::Damage(Mob* other, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic, int special)
void Merc::Damage(Mob* other, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic, eSpecialAttacks special)
{
if(IsDead() || IsCorpse())
return;
+2 -3
View File
@@ -65,9 +65,9 @@ public:
//abstract virtual function implementations requird by base abstract class
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill);
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, int special = 0);
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None);
virtual bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false,
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr, int special = 0);
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr);
virtual bool HasRaid() { return false; }
virtual bool HasGroup() { return (GetGroup() ? true : false); }
virtual Raid* GetRaid() { return 0; }
@@ -197,7 +197,6 @@ public:
virtual void CalcBonuses();
int32 GetEndurance() const {return cur_end;} //This gets our current endurance
inline uint8 GetEndurancePercent() { return (uint8)((float)cur_end / (float)max_end * 100.0f); }
inline virtual int32 GetAC() const { return AC; }
inline virtual int32 GetATK() const { return ATK; }
inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; }
int32 GetRawACNoShield(int &shield_ac) const;
+26 -2
View File
@@ -110,7 +110,9 @@ Mob::Mob(const char* in_name,
m_TargetV(glm::vec3()),
flee_timer(FLEE_CHECK_TIMER),
m_Position(position),
tmHidden(-1)
tmHidden(-1),
mitigation_ac(0),
m_specialattacks(eSpecialAttacks::None)
{
targeted = 0;
tar_ndx=0;
@@ -212,6 +214,7 @@ Mob::Mob(const char* in_name,
has_shieldequiped = false;
has_twohandbluntequiped = false;
has_twohanderequipped = false;
can_facestab = false;
has_numhits = false;
has_MGB = false;
has_ProjectIllusion = false;
@@ -285,6 +288,8 @@ Mob::Mob(const char* in_name,
armor_tint.Slot[i].Color = in_armor_tint.Slot[i].Color;
}
std::fill(std::begin(m_spellHitsLeft), std::end(m_spellHitsLeft), 0);
m_Delta = glm::vec4();
animation = 0;
@@ -1497,6 +1502,10 @@ void Mob::ShowStats(Client* client)
if (IsAIControlled()) {
client->Message(0, " AggroRange: %1.0f AssistRange: %1.0f", GetAggroRange(), GetAssistRange());
}
client->Message(0, " compute_tohit: %i TotalToHit: %i", compute_tohit(EQEmu::skills::SkillHandtoHand), GetTotalToHit(EQEmu::skills::SkillHandtoHand, 0));
client->Message(0, " compute_defense: %i TotalDefense: %i", compute_defense(), GetTotalDefense());
client->Message(0, " offense: %i mitigation ac: %i", offense(EQEmu::skills::SkillHandtoHand), GetMitigationAC());
}
}
@@ -2773,7 +2782,22 @@ void Mob::SendWearChange(uint8 material_slot, Client *one_client)
wc->material = GetEquipmentMaterial(material_slot);
wc->elite_material = IsEliteMaterialItem(material_slot);
wc->hero_forge_model = GetHerosForgeModel(material_slot);
#ifdef BOTS
if (IsBot()) {
auto item_inst = CastToBot()->GetBotItem(EQEmu::InventoryProfile::CalcSlotFromMaterial(material_slot));
if (item_inst)
wc->color.Color = item_inst->GetColor();
else
wc->color.Color = 0;
}
else {
wc->color.Color = GetEquipmentColor(material_slot);
}
#else
wc->color.Color = GetEquipmentColor(material_slot);
#endif
wc->wear_slot_id = material_slot;
if (!one_client)
@@ -4644,7 +4668,7 @@ void Mob::SetRaidGrouped(bool v)
}
}
int16 Mob::GetCriticalChanceBonus(uint16 skill)
int Mob::GetCriticalChanceBonus(uint16 skill)
{
int critical_chance = 0;
+56 -32
View File
@@ -54,6 +54,13 @@ namespace EQEmu
class ItemInstance;
}
enum class eSpecialAttacks : int {
None,
Rampage,
AERampage,
ChaoticStab
};
class Mob : public Entity {
public:
enum CLIENT_CONN_STATUS { CLIENT_CONNECTING, CLIENT_CONNECTED, CLIENT_LINKDEAD,
@@ -152,45 +159,54 @@ public:
float HeadingAngleToMob(Mob *other); // to keep consistent with client generated messages
virtual void RangedAttack(Mob* other) { }
virtual void ThrowingAttack(Mob* other) { }
uint16 GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg);
// 13 = Primary (default), 14 = secondary
virtual bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false,
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr, int special = 0) = 0;
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) = 0;
void DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts = nullptr);
int MonkSpecialAttack(Mob* other, uint8 skill_used);
virtual void TryBackstab(Mob *other,int ReuseTime = 10);
bool AvoidDamage(Mob* attacker, int32 &damage, int hand);
bool AvoidDamage(Mob *attacker, DamageHitInfo &hit);
int compute_tohit(EQEmu::skills::SkillType skillinuse);
int GetTotalToHit(EQEmu::skills::SkillType skill, int chance_mod); // compute_tohit + spell bonuses
int compute_defense();
virtual bool CheckHitChance(Mob* attacker, EQEmu::skills::SkillType skillinuse, int chance_mod = 0);
virtual void TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttackOptions *opts = nullptr);
void TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage);
virtual bool TryFinishingBlow(Mob *defender, EQEmu::skills::SkillType skillinuse);
uint32 TryHeadShot(Mob* defender, EQEmu::skills::SkillType skillInUse);
uint32 TryAssassinate(Mob* defender, EQEmu::skills::SkillType skillInUse, uint16 ReuseTime);
int GetTotalDefense(); // compute_defense + spell bonuses
bool CheckHitChance(Mob* attacker, DamageHitInfo &hit);
void TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts = nullptr);
void TryPetCriticalHit(Mob *defender, DamageHitInfo &hit);
virtual bool TryFinishingBlow(Mob *defender, int &damage);
int TryHeadShot(Mob* defender, EQEmu::skills::SkillType skillInUse);
int TryAssassinate(Mob* defender, EQEmu::skills::SkillType skillInUse);
virtual void DoRiposte(Mob* defender);
void ApplyMeleeDamageBonus(uint16 skill, int32 &damage,ExtraAttackOptions *opts = nullptr);
virtual void MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttackOptions *opts = nullptr);
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
void ApplyMeleeDamageMods(uint16 skill, int &damage, Mob * defender = nullptr, ExtraAttackOptions *opts = nullptr);
int ACSum();
int offense(EQEmu::skills::SkillType skill);
void CalcAC() { mitigation_ac = ACSum(); }
int GetACSoftcap();
double GetSoftcapReturns();
int GetClassRaceACBonus();
inline int GetMitigationAC() { return mitigation_ac; }
void MeleeMitigation(Mob *attacker, DamageHitInfo &hit, ExtraAttackOptions *opts = nullptr);
double RollD20(int offense, int mitigation); // CALL THIS FROM THE DEFENDER
bool CombatRange(Mob* other);
virtual inline bool IsBerserk() { return false; } // only clients
void RogueEvade(Mob *other);
void CommonOutgoingHitSuccess(Mob* defender, int32 &damage, EQEmu::skills::SkillType skillInUse, ExtraAttackOptions *opts = nullptr);
void CommonOutgoingHitSuccess(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts = nullptr);
void BreakInvisibleSpells();
virtual void CancelSneakHide();
void CommonBreakInvisible();
void CommonBreakInvisibleFromCombat();
bool HasDied();
virtual bool CheckDualWield();
void DoMainHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr, int special = 0);
void DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr, int special = 0);
void DoMainHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr);
void DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr);
virtual bool CheckDoubleAttack();
// inline process for places where we need to do them outside of the AI_Process
void ProcessAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr, int special = 0)
void ProcessAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr)
{
if (target) {
DoMainHandAttackRounds(target, opts, special);
DoMainHandAttackRounds(target, opts);
if (CanThisClassDualWield())
DoOffHandAttackRounds(target, opts, special);
DoOffHandAttackRounds(target, opts);
}
return;
}
@@ -357,6 +373,8 @@ public:
inline void SetTwoHandBluntEquiped(bool val) { has_twohandbluntequiped = val; }
bool HasTwoHanderEquipped() { return has_twohanderequipped; }
void SetTwoHanderEquipped(bool val) { has_twohanderequipped = val; }
bool CanFacestab() { return can_facestab; }
void SetFacestab(bool val) { can_facestab = val; }
virtual uint16 GetSkill(EQEmu::skills::SkillType skill_num) const { return 0; }
virtual uint32 GetEquipment(uint8 material_slot) const { return(0); }
virtual int32 GetEquipmentMaterial(uint8 material_slot) const;
@@ -367,7 +385,7 @@ public:
bool AffectedBySpellExcludingSlot(int slot, int effect);
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill) = 0;
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill,
bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, int special = 0) = 0;
bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) = 0;
inline virtual void SetHP(int32 hp) { if (hp >= max_hp) cur_hp = max_hp; else cur_hp = hp;}
bool ChangeHP(Mob* other, int32 amount, uint16 spell_id = 0, int8 buffslot = -1, bool iBuffTic = false);
inline void SetOOCRegen(int32 newoocregen) {oocregen = newoocregen;}
@@ -404,9 +422,10 @@ public:
strn0cpy(name, GetName(), 64); return; };
inline Mob* GetTarget() const { return target; }
virtual void SetTarget(Mob* mob);
inline bool HasTargetReflection() const { return (target && target != this && target->target == this); }
virtual inline float GetHPRatio() const { return max_hp == 0 ? 0 : ((float)cur_hp/max_hp*100); }
virtual inline int GetIntHPRatio() const { return max_hp == 0 ? 0 : static_cast<int>(cur_hp * 100 / max_hp); }
inline virtual int32 GetAC() const { return AC + itembonuses.AC + spellbonuses.AC; }
inline int32 GetAC() const { return AC; }
inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK; }
inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; }
inline virtual int32 GetSTR() const { return STR + itembonuses.STR + spellbonuses.STR; }
@@ -673,7 +692,7 @@ public:
int16 GetMeleeMinDamageMod_SE(uint16 skill);
int16 GetCrippBlowChance();
int16 GetSkillReuseTime(uint16 skill);
int16 GetCriticalChanceBonus(uint16 skill);
int GetCriticalChanceBonus(uint16 skill);
int16 GetSkillDmgAmt(uint16 skill);
bool TryReflectSpell(uint32 spell_id);
bool CanBlockSpell() const { return(spellbonuses.BlockNextSpell); }
@@ -778,7 +797,8 @@ public:
int32 GetMeleeMitigation();
uint8 GetWeaponDamageBonus(const EQEmu::ItemData* weapon, bool offhand = false);
uint16 GetDamageTable(EQEmu::skills::SkillType skillinuse);
const DamageTable &GetDamageTable() const;
void ApplyDamageTable(DamageHitInfo &hit);
virtual int GetHandToHandDamage(void);
bool CanThisClassDoubleAttack(void) const;
@@ -801,9 +821,9 @@ public:
int32 AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTic, Mob* attacker);
int32 ReduceAllDamage(int32 damage);
virtual void DoSpecialAttackDamage(Mob *who, EQEmu::skills::SkillType skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool CheckHitChance = false, bool CanAvoid = true);
void DoSpecialAttackDamage(Mob *who, EQEmu::skills::SkillType skill, int base_damage, int min_damage = 0, int32 hate_override = -1, int ReuseTime = 10);
virtual void DoThrowingAttackDmg(Mob* other, const EQEmu::ItemInstance* RangeWeapon = nullptr, const EQEmu::ItemData* AmmoItem = nullptr, uint16 weapon_damage = 0, int16 chance_mod = 0, int16 focus = 0, int ReuseTime = 0, uint32 range_id = 0, int AmmoSlot = 0, float speed = 4.0f);
virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, EQEmu::skills::SkillType skillinuse, int16 chance_mod = 0, int16 focus = 0, bool CanRiposte = false, int ReuseTime = 0);
void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, EQEmu::skills::SkillType skillinuse, int16 chance_mod = 0, int16 focus = 0, bool CanRiposte = false, int ReuseTime = 0);
virtual void DoArcheryAttackDmg(Mob* other, const EQEmu::ItemInstance* RangeWeapon = nullptr, const EQEmu::ItemInstance* Ammo = nullptr, uint16 weapon_damage = 0, int16 chance_mod = 0, int16 focus = 0, int ReuseTime = 0, uint32 range_id = 0, uint32 ammo_id = 0, const EQEmu::ItemData *AmmoItem = nullptr, int AmmoSlot = 0, float speed = 4.0f);
bool TryProjectileAttack(Mob* other, const EQEmu::ItemData *item, EQEmu::skills::SkillType skillInUse, uint16 weapon_dmg, const EQEmu::ItemInstance* RangeWeapon, const EQEmu::ItemInstance* Ammo, int AmmoSlot, float speed);
void ProjectileAttack();
@@ -816,6 +836,7 @@ public:
bool AddRampage(Mob*);
void ClearRampage();
void AreaRampage(ExtraAttackOptions *opts);
inline bool IsSpecialAttack(eSpecialAttacks in) { return m_specialattacks == in; }
void StartEnrage();
void ProcessEnrage();
@@ -1042,7 +1063,7 @@ public:
#endif
protected:
void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const EQEmu::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, int special = 0);
void CommonDamage(Mob* other, int &damage, const uint16 spell_id, const EQEmu::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None);
static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
float _GetMovementSpeed(int mod) const;
int _GetWalkSpeed() const;
@@ -1079,6 +1100,7 @@ protected:
bool multitexture;
int AC;
int mitigation_ac; // cached Mob::ACSum
int32 ATK;
int32 STR;
int32 STA;
@@ -1150,6 +1172,7 @@ protected:
int base_walkspeed;
int base_fearspeed;
int current_speed;
eSpecialAttacks m_specialattacks;
uint32 pLastChange;
bool held;
@@ -1168,15 +1191,14 @@ protected:
void ExecWeaponProc(const EQEmu::ItemInstance* weapon, uint16 spell_id, Mob *on, int level_override = -1);
virtual float GetProcChances(float ProcBonus, uint16 hand = EQEmu::inventory::slotPrimary);
virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 hand = EQEmu::inventory::slotPrimary, Mob *on = nullptr);
virtual float GetSpecialProcChances(uint16 hand);
virtual float GetAssassinateProcChances(uint16 ReuseTime);
virtual float GetSkillProcChances(uint16 ReuseTime, uint16 hand = 0); // hand = MainCharm?
uint16 GetWeaponSpeedbyHand(uint16 hand);
int GetWeaponDamage(Mob *against, const EQEmu::ItemData *weapon_item);
int GetWeaponDamage(Mob *against, const EQEmu::ItemInstance *weapon_item, uint32 *hate = nullptr);
int GetKickDamage();
int GetBashDamage();
virtual void ApplySpecialAttackMod(EQEmu::skills::SkillType skill, int32 &dmg, int32 &mindmg);
#ifdef BOTS
virtual
#endif
int GetBaseSkillDamage(EQEmu::skills::SkillType skill, Mob *target = nullptr);
virtual int16 GetFocusEffect(focusType type, uint16 spell_id) { return 0; }
void CalculateNewFearpoint();
float FindGroundZ(float new_x, float new_y, float z_offset=0.0);
@@ -1211,7 +1233,7 @@ protected:
Timer attack_dw_timer;
Timer ranged_timer;
float attack_speed; //% increase/decrease in attack speed (not haste)
int8 attack_delay; //delay between attacks in 10ths of seconds
int attack_delay; //delay between attacks in 10ths of seconds
int16 slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%)
Timer tic_timer;
Timer mana_timer;
@@ -1278,6 +1300,7 @@ protected:
bool has_shieldequiped;
bool has_twohandbluntequiped;
bool has_twohanderequipped;
bool can_facestab;
bool has_numhits;
bool has_MGB;
bool has_ProjectIllusion;
@@ -1394,7 +1417,8 @@ protected:
glm::vec3 m_TargetRing;
uint32 m_spellHitsLeft[38]; // Used to track which spells will have their numhits incremented when spell finishes casting, 38 Buffslots
// we might want to do this differently, we gotta do max NPC buffs ... which is 97
uint32 m_spellHitsLeft[EQEmu::constants::TotalBuffs]; // Used to track which spells will have their numhits incremented when spell finishes casting
int flymode;
bool m_targetable;
int QGVarDuration(const char *fmt);
+12 -19
View File
@@ -1160,11 +1160,8 @@ void Mob::AI_Process() {
if ((IsPet() || IsTempPet()) && IsPetOwnerClient()){
if (spellbonuses.PC_Pet_Rampage[0] || itembonuses.PC_Pet_Rampage[0] || aabonuses.PC_Pet_Rampage[0]){
int chance = spellbonuses.PC_Pet_Rampage[0] + itembonuses.PC_Pet_Rampage[0] + aabonuses.PC_Pet_Rampage[0];
int dmg_mod = spellbonuses.PC_Pet_Rampage[1] + itembonuses.PC_Pet_Rampage[1] + aabonuses.PC_Pet_Rampage[1];
if(zone->random.Roll(chance)) {
ExtraAttackOptions opts;
opts.damage_percent = dmg_mod / 100.0f;
Rampage(&opts);
Rampage(nullptr);
}
}
}
@@ -1175,12 +1172,7 @@ void Mob::AI_Process() {
rampage_chance = rampage_chance > 0 ? rampage_chance : 20;
if(zone->random.Roll(rampage_chance)) {
ExtraAttackOptions opts;
int cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 2);
if(cur > 0) {
opts.damage_percent = cur / 100.0f;
}
cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 3);
int cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 3);
if(cur > 0) {
opts.damage_flat = cur;
}
@@ -1215,12 +1207,7 @@ void Mob::AI_Process() {
rampage_chance = rampage_chance > 0 ? rampage_chance : 20;
if(zone->random.Roll(rampage_chance)) {
ExtraAttackOptions opts;
int cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 2);
if(cur > 0) {
opts.damage_percent = cur / 100.0f;
}
cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 3);
int cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 3);
if(cur > 0) {
opts.damage_flat = cur;
}
@@ -1992,6 +1979,8 @@ bool Mob::Rampage(ExtraAttackOptions *opts)
rampage_targets = RuleI(Combat, DefaultRampageTargets);
if (rampage_targets > RuleI(Combat, MaxRampageTargets))
rampage_targets = RuleI(Combat, MaxRampageTargets);
m_specialattacks = eSpecialAttacks::Rampage;
for (int i = 0; i < RampageArray.size(); i++) {
if (index_hit >= rampage_targets)
break;
@@ -2001,14 +1990,16 @@ bool Mob::Rampage(ExtraAttackOptions *opts)
if (m_target == GetTarget())
continue;
if (CombatRange(m_target)) {
ProcessAttackRounds(m_target, opts, 2);
ProcessAttackRounds(m_target, opts);
index_hit++;
}
}
}
if (RuleB(Combat, RampageHitsTarget) && index_hit < rampage_targets)
ProcessAttackRounds(GetTarget(), opts, 2);
ProcessAttackRounds(GetTarget(), opts);
m_specialattacks = eSpecialAttacks::None;
return true;
}
@@ -2024,10 +2015,12 @@ void Mob::AreaRampage(ExtraAttackOptions *opts)
int rampage_targets = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 1);
rampage_targets = rampage_targets > 0 ? rampage_targets : -1;
m_specialattacks = eSpecialAttacks::AERampage;
index_hit = hate_list.AreaRampage(this, GetTarget(), rampage_targets, opts);
if(index_hit == 0)
ProcessAttackRounds(GetTarget(), opts, 1);
ProcessAttackRounds(GetTarget(), opts);
m_specialattacks = eSpecialAttacks::None;
}
uint32 Mob::GetLevelCon(uint8 mylevel, uint8 iOtherLevel) {
+21 -6
View File
@@ -201,6 +201,9 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
CalcNPCDamage();
}
base_damage = round((max_dmg - min_dmg) / 1.9);
min_damage = min_dmg - round(base_damage / 10.0);
accuracy_rating = d->accuracy_rating;
avoidance_rating = d->avoidance_rating;
ATK = d->ATK;
@@ -1054,7 +1057,7 @@ NPC* NPC::SpawnNPC(const char* spawncommand, const glm::vec4& position, Client*
npc_type->WIS = 150;
npc_type->CHA = 150;
npc_type->attack_delay = 30;
npc_type->attack_delay = 3000;
npc_type->prim_melee_type = 28;
npc_type->sec_melee_type = 28;
@@ -1956,10 +1959,10 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
id[i] = std::tolower(id[i]);
}
if(id == "ac") { AC = atoi(val.c_str()); return; }
if(id == "ac") { AC = atoi(val.c_str()); CalcAC(); return; }
else if(id == "str") { STR = atoi(val.c_str()); return; }
else if(id == "sta") { STA = atoi(val.c_str()); return; }
else if(id == "agi") { AGI = atoi(val.c_str()); return; }
else if(id == "agi") { AGI = atoi(val.c_str()); CalcAC(); return; }
else if(id == "dex") { DEX = atoi(val.c_str()); return; }
else if(id == "wis") { WIS = atoi(val.c_str()); CalcMaxMana(); return; }
else if(id == "int" || id == "_int") { INT = atoi(val.c_str()); CalcMaxMana(); return; }
@@ -1984,13 +1987,25 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
else if(id == "special_attacks") { NPCSpecialAttacks(val.c_str(), 0, 1); return; }
else if(id == "special_abilities") { ProcessSpecialAbilities(val.c_str()); return; }
else if(id == "attack_speed") { attack_speed = (float)atof(val.c_str()); CalcBonuses(); return; }
else if(id == "attack_delay") { attack_delay = atoi(val.c_str()); CalcBonuses(); return; }
else if(id == "attack_delay") { /* TODO: fix DB */attack_delay = atoi(val.c_str()) * 100; CalcBonuses(); return; }
else if(id == "atk") { ATK = atoi(val.c_str()); return; }
else if(id == "accuracy") { accuracy_rating = atoi(val.c_str()); return; }
else if(id == "avoidance") { avoidance_rating = atoi(val.c_str()); return; }
else if(id == "trackable") { trackable = atoi(val.c_str()); return; }
else if(id == "min_hit") { min_dmg = atoi(val.c_str()); return; }
else if(id == "max_hit") { max_dmg = atoi(val.c_str()); return; }
else if(id == "min_hit") {
min_dmg = atoi(val.c_str());
// TODO: fix DB
base_damage = round((max_dmg - min_dmg) / 1.9);
min_damage = min_dmg - round(base_damage / 10.0);
return;
}
else if(id == "max_hit") {
max_dmg = atoi(val.c_str());
// TODO: fix DB
base_damage = round((max_dmg - min_dmg) / 1.9);
min_damage = min_dmg - round(base_damage / 10.0);
return;
}
else if(id == "attack_count") { attack_count = atoi(val.c_str()); return; }
else if(id == "see_invis") { see_invis = atoi(val.c_str()); return; }
else if(id == "see_invis_undead") { see_invis_undead = atoi(val.c_str()); return; }
+7 -3
View File
@@ -109,9 +109,9 @@ public:
//abstract virtual function implementations requird by base abstract class
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill);
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, int special = 0);
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None);
virtual bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false,
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr, int special = 0);
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr);
virtual bool HasRaid() { return false; }
virtual bool HasGroup() { return false; }
virtual Raid* GetRaid() { return 0; }
@@ -259,9 +259,11 @@ public:
uint32 GetMaxDMG() const {return max_dmg;}
uint32 GetMinDMG() const {return min_dmg;}
int GetBaseDamage() const { return base_damage; }
int GetMinDamage() const { return min_damage; }
float GetSlowMitigation() const { return slow_mitigation; }
float GetAttackSpeed() const {return attack_speed;}
uint8 GetAttackDelay() const {return attack_delay;}
int GetAttackDelay() const {return attack_delay;}
bool IsAnimal() const { return(bodytype == BT_Animal); }
uint16 GetPetSpellID() const {return pet_spell_id;}
void SetPetSpellID(uint16 amt) {pet_spell_id = amt;}
@@ -463,6 +465,8 @@ protected:
uint32 max_dmg;
uint32 min_dmg;
int base_damage;
int min_damage;
int32 accuracy_rating;
int32 avoidance_rating;
int16 attack_count;
+3 -2
View File
@@ -225,8 +225,7 @@ Object::Object(const char *model, float x, float y, float z, float heading, uint
{
user = nullptr;
last_user = nullptr;
EQEmu::ItemInstance* inst = nullptr;
inst = new EQEmu::ItemInstance(ItemInstWorldContainer);
EQEmu::ItemInstance* inst = new EQEmu::ItemInstance(ItemInstWorldContainer);
// Initialize members
m_id = 0;
@@ -257,6 +256,8 @@ Object::Object(const char *model, float x, float y, float z, float heading, uint
strcpy(m_data.object_name, model);
else
strcpy(m_data.object_name, "IT64_ACTORDEF"); //default object name if model isn't specified for some unknown reason
safe_delete(inst);
}
Object::~Object()
+712 -1004
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -5566,6 +5566,7 @@ void Client::InitializeBuffSlots()
for(int x = 0; x < max_slots; ++x)
{
buffs[x].spellid = SPELL_UNKNOWN;
buffs[x].UpdateClient = false;
}
current_buff_count = 0;
}
@@ -5582,6 +5583,7 @@ void NPC::InitializeBuffSlots()
for(int x = 0; x < max_slots; ++x)
{
buffs[x].spellid = SPELL_UNKNOWN;
buffs[x].UpdateClient = false;
}
current_buff_count = 0;
}
+4 -4
View File
@@ -486,7 +486,7 @@ int32 Mob::Tune_MeleeMitigation(Mob* GM, Mob *attacker, int32 damage, int32 minh
}
damage = GetMeleeMitDmg(attacker, damage, minhit, mitigation_rating, attack_rating);
//damage = GetMeleeMitDmg(attacker, damage, minhit, mitigation_rating, attack_rating);
}
if (damage < 0)
@@ -539,7 +539,7 @@ int32 Client::Tune_GetMeleeMitDmg(Mob* GM, Mob *attacker, int32 damage, int32 mi
float mit_rating, float atk_rating)
{
if (!attacker->IsNPC() || RuleB(Combat, UseOldDamageIntervalRules))
return Mob::GetMeleeMitDmg(attacker, damage, minhit, mit_rating, atk_rating);
return 0; //Mob::GetMeleeMitDmg(attacker, damage, minhit, mit_rating, atk_rating);
int d = 10;
// floats for the rounding issues
float dmg_interval = (damage - minhit) / 19.0;
@@ -613,7 +613,7 @@ int32 Client::GetMeleeDamage(Mob* other, bool GetMinDamage)
}
int min_hit = 1;
int max_hit = (2*weapon_damage*GetDamageTable(skillinuse)) / 100;
int max_hit = 2;//(2*weapon_damage*GetDamageTable(skillinuse)) / 100;
if(GetLevel() < 10 && max_hit > RuleI(Combat, HitCapPre10))
max_hit = (RuleI(Combat, HitCapPre10));
@@ -1086,4 +1086,4 @@ float Mob::Tune_CheckHitChance(Mob* defender, Mob* attacker, EQEmu::skills::Skil
}
return(chancetohit);
}
}
+1 -1
View File
@@ -2133,7 +2133,7 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
temp_npctype_data->healscale = atoi(row[87]);
temp_npctype_data->no_target_hotkey = atoi(row[88]) == 1 ? true: false;
temp_npctype_data->raid_target = atoi(row[89]) == 0 ? false: true;
temp_npctype_data->attack_delay = atoi(row[90]);
temp_npctype_data->attack_delay = atoi(row[90]) * 100; // TODO: fix DB
temp_npctype_data->light = (atoi(row[91]) & 0x0F);
temp_npctype_data->armtexture = atoi(row[92]);
+1 -1
View File
@@ -110,7 +110,7 @@ struct NPCType
uint8 spawn_limit; //only this many may be in zone at a time (0=no limit)
uint8 mount_color; //only used by horse class
float attack_speed; //%+- on attack delay of the mob.
uint8 attack_delay; //delay between attacks in 10ths of a second
int attack_delay; //delay between attacks in ms
int accuracy_rating; // flat bonus before mods
int avoidance_rating; // flat bonus before mods
bool findable; //can be found with find command