Compare commits

..

23 Commits

Author SHA1 Message Date
Akkadius 37e66b5c01 Swap quest debug for combat logging [skip ci] 2019-10-12 17:25:13 -05:00
Akkadius bf798c0561 Pet critical logging [skip ci] 2019-10-12 17:24:07 -05:00
Akkadius 6ec577f241 More logging [skip ci] 2019-10-08 02:25:02 -05:00
Akkadius 672e6766c7 More logging [skip ci] 2019-10-08 02:10:49 -05:00
Akkadius b3eb11d068 More logging changes [skip ci] 2019-10-08 01:56:08 -05:00
Akkadius 55c61ddc78 Update some of the logging [skip ci] 2019-10-06 04:56:19 -05:00
Akkadius c131eca57f More temp logging changes [skip ci] 2019-10-06 03:42:34 -05:00
Akkadius a6dbba5380 Revert message changes [skip ci] 2019-10-06 02:39:37 -05:00
Akkadius d2f1a3097b More log tweaks [skip ci] 2019-10-06 02:33:00 -05:00
Akkadius 6c522e0e76 Fix logging [skip ci] 2019-10-06 02:27:15 -05:00
Akkadius cba068bfe8 Lower logging status for GM 2019-10-06 02:22:43 -05:00
Akkadius 28c8af96e3 Logging Mob::TryCriticalHit [skip ci] 2019-10-06 02:12:15 -05:00
Akkadius c684709712 Logging to Mob::CommonOutgoingHitSuccess [skip ci[ 2019-10-06 02:05:39 -05:00
Akkadius 225b3fffbd Logging to Mob::ApplyMeleeDamageBonus [skip ci] 2019-10-06 01:57:48 -05:00
Akkadius 0b790342e7 Add logging to Mob::CheckHitChance [skip ci] 2019-10-06 01:53:03 -05:00
Akkadius 690621268b Change GetDamageTable to use e.self [skip ci] 2019-10-06 01:41:10 -05:00
Akkadius 725d080cac Damage Table logging [skip ci] 2019-10-06 01:31:28 -05:00
Akkadius a3ffa7e262 Logging to GetRawACNoShield [skip ci] 2019-10-06 00:54:46 -05:00
Akkadius 344682e6ff Add logging to Mob::GetMeleeMitDmg [skip ci] 2019-10-06 00:38:08 -05:00
Akkadius c927fec803 Logging to MeleeMitigation [skip ci] 2019-10-06 00:06:59 -05:00
Akkadius 3b23477139 Comment each method with source function calls [skip ci] 2019-10-05 22:36:00 -05:00
Akkadius ea49422ff7 Add legacy_combat.lua merely for logging reference 2019-10-05 22:09:48 -05:00
Akkadius 9ccd9b80bb Old combat code beginng in plus linux fixes 2019-10-05 19:50:11 -05:00
38 changed files with 3066 additions and 2041 deletions
+1 -1
View File
@@ -144,7 +144,7 @@ void EQEmuLogSys::ProcessGMSay(uint16 debug_level, uint16 log_category, const st
return;
/* Check to see if the process that actually ran this is zone */
if (EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformZone)
if (GetExecutablePlatformInt() == EQEmuExePlatform::ExePlatformZone)
on_log_gmsay_hook(log_category, message);
}
-9
View File
@@ -65,15 +65,6 @@ namespace EQEmu {
return Real(0.0, 1.0) <= required;
}
// same range as client's roll0
// This is their main high level RNG function
int Roll0(int max)
{
if (max - 1 > 0)
return Int(0, max - 1);
return 0;
}
// std::shuffle requires a RNG engine passed to it, so lets provide a wrapper to use our engine
template<typename RandomAccessIterator>
void Shuffle(RandomAccessIterator first, RandomAccessIterator last)
-1
View File
@@ -492,7 +492,6 @@ RULE_INT(Combat, NPCAssistCap, 5) // Maxiumium number of NPCs that will assist a
RULE_INT(Combat, NPCAssistCapTimer, 6000) // Time in milliseconds a NPC will take to clear assist aggro cap space
RULE_BOOL(Combat, UseRevampHandToHand, false) // use h2h revamped dmg/delays I believe this was implemented during SoF
RULE_BOOL(Combat, ClassicMasterWu, false) // classic master wu uses a random special, modern doesn't
RULE_INT(Combat, LevelToStopDamageCaps, 0) // 1 will effectively disable them, 20 should give basically same results as old incorrect system
RULE_CATEGORY_END()
RULE_CATEGORY(NPC)
-24
View File
@@ -124,30 +124,6 @@ bool EQEmu::skills::IsCastingSkill(SkillType skill)
}
}
int32 EQEmu::skills::GetBaseDamage(SkillType skill)
{
switch (skill) {
case SkillBash:
return 2;
case SkillDragonPunch:
return 12;
case SkillEagleStrike:
return 7;
case SkillFlyingKick:
return 25;
case SkillKick:
return 3;
case SkillRoundKick:
return 5;
case SkillTigerClaw:
return 4;
case SkillFrenzy:
return 10;
default:
return 0;
}
}
const std::map<EQEmu::skills::SkillType, std::string>& EQEmu::skills::GetSkillTypeMap()
{
/* VS2013 code
-1
View File
@@ -166,7 +166,6 @@ namespace EQEmu
float GetSkillMeleePushForce(SkillType skill);
bool IsBardInstrumentSkill(SkillType skill);
bool IsCastingSkill(SkillType skill);
int32 GetBaseDamage(SkillType skill);
extern const std::map<SkillType, std::string>& GetSkillTypeMap();
-138
View File
@@ -1,138 +0,0 @@
var app = angular.module('app', ['nvd3']);
app.controller('MainCtrl', function($scope, $interval) {
$scope.options = {
chart: {
type: 'discreteBarChart',
height: 450,
margin: {
top: 20,
right: 20,
bottom: 50,
left: 55
},
x: function(d) {
return d.label;
},
y: function(d) {
return d.value + (1e-10);
},
showValues: true,
valueFormat: function(d) {
return d3.format(',.2r')(d);
},
duration: 500,
xAxis: {
axisLabel: 'D1-D20'
},
yAxis: {
axisLabel: 'Count'
}
}
};
$scope.offense = 100;
$scope.mitigation = 100;
$scope.data = [{
key: "Cumulative Return",
values: []
}];
for (var i = 0; i < 20; ++i) {
var value = {
"label": i + 1,
"value": 0
};
$scope.data[0].values.push(value);
}
function addRoll(interval) {
$scope.data[0].values[interval - 1].value += 1;
}
var stop;
$scope.clearData = function() {
console.log('Clearing data');
for (var i = 0; i < 20; ++i) {
$scope.data[0].values[i].value = 0;
}
};
$scope.start = function() {
if (angular.isDefined(stop))
return;
stop = $interval(doCombatRound, 100);
};
$scope.stop = function() {
if (angular.isDefined(stop)) {
$interval.cancel(stop);
stop = undefined;
}
};
$scope.$on('$destroy', function() {
$scope.stop();
});
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
function getRandom(min, max) {
return Math.random() * (max - min) + min;
}
function addChance(bucket, chance, value) {
for (var i = 0; i < chance; ++i) {
bucket.push(value);
}
}
function doCombatRound() {
var offense = $scope.offense;
var mitigation = $scope.mitigation;
mitigation = mitigation - ((mitigation - offense) / 2.0);
var diff = offense - mitigation;
var mean = 0.0;
var mult1 = 0.0;
var mult2 = 0.0;
if (offense > 30.0) {
mult1 = offense / 200.0 + 25.75;
if ((mitigation / offense) < 0.35) {
mult1 = mult1 + 1.0;
} else if ((mitigation / offense) > 0.65) {
mult1 = mult1 - 1.0;
}
mult2 = offense / 140 + 18.5;
} else {
mult1 = 11.5 + offense / 2.0;
mult2 = 14.0 + offense / 6.0;
}
if (offense > mitigation) {
mean = diff / offense * mult1;
} else if (mitigation > offense) {
mean = diff / mitigation * mult2;
}
var stddev = 8.8;
var theta = 2 * Math.PI * getRandom(0.0, 1.0);
var rho = Math.sqrt(-2 * Math.log(1 - getRandom(0.0, 1.0)));
var d = mean + stddev * rho * Math.cos(theta);
if (d < -9.5) {
d = -9.5;
} else if (d > 9.5) {
d = 9.5;
}
d = d + 11;
addRoll(parseInt(d));
};
});
-30
View File
@@ -1,30 +0,0 @@
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>Combat Visualization</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.min.css"/>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-nvd3/1.0.5/angular-nvd3.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<nvd3 options="options" data="data"></nvd3>
<button ng-click=clearData()>Clear Data</button>
<button ng-click=start()>Start</button>
<button ng-click=stop()>Stop</button>
<div class="input-row">
<label>Offense:</label>
<input type="number" name="Offense" ng-model="offense"></input>
</div>
<div class="input-row">
<label>Mitigation:</label>
<input type="number" name="mitigation" ng-model="mitigation"></input>
</div>
</body>
</html>
-3
View File
@@ -1,3 +0,0 @@
.input-row {
padding: 8px;
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -150,7 +150,7 @@ OP_GMZoneRequest=0x62ac
OP_GMZoneRequest2=0x7e1a
OP_GMGoto=0x7d8e
OP_GMSearchCorpse=0x357c
OP_GMHideMe=0x2fab
OP_GMHideMe=0x79c5
OP_GMDelCorpse=0x607e
OP_GMApproval=0x6db5
OP_GMToggle=0x2097
@@ -1,25 +0,0 @@
update npc_types set attack_speed=0, atk=ceil(1.7*level), accuracy=ceil(1.7*level) where (name like 'Swarm%' or name like '%skel%' or name like 'BLpet%' or name like 'Sum%')
and id >510 and id <860;
update npc_types set attack_speed=0, atk=ceil(0.5*level), accuracy=ceil(0.5*level) where name like 'SumFire%';
update npc_types set attack_speed=0, atk=ceil(1.775*level), accuracy=ceil(1.775*level) where name like 'SumAir%';
update npc_types set attack_speed=0, atk=ceil(1.775*level), accuracy=ceil(1.775*level) where name like 'SumEarth%';
update npc_types set attack_speed=0, atk=ceil(2.26*level), accuracy=ceil(2.26*level),gender=2,size=3 where name like 'BestialAid%';
update npc_types set attack_speed=0, atk=ceil(2.26*level), accuracy=ceil(2.26*level),gender=2,size=3 where name like 'RagingServant%';
update npc_types n
set ac=
ceil(case
when level < 3 then level*2+2
when level < 15 and level >=3 then level*3
when id >= 200000 and id < 224000 then 200*.5+level
else level * 4.1 end
* (case when raid_target=1 then 1.4 else 1 end) + (case when raid_target=1 then level*1.4 else 0 end))
+ 4*ifnull((select min(expansion)from zone where zoneidnumber=floor(n.id/1000)),5)
,str=ceil((level * 4.1)*.75) + case when raid_target=1 then level else 0 end + 4*ifnull((select min(expansion)from zone where zoneidnumber=floor(n.id/1000)),5)
,sta=ceil((level * 4.1)*.75) + case when raid_target=1 then level else 0 end + 4*ifnull((select min(expansion)from zone where zoneidnumber=floor(n.id/1000)),5)
,agi=ceil((level * 4.1)*.75) + case when raid_target=1 then level else 0 end + 4*ifnull((select min(expansion)from zone where zoneidnumber=floor(n.id/1000)),5)
,_int=ceil((level * 4.1)*.75) + case when raid_target=1 then level else 0 end + 4*ifnull((select min(expansion)from zone where zoneidnumber=floor(n.id/1000)),5)
,dex=ceil((level * 4.1)*.75) + case when raid_target=1 then level else 0 end + 4*ifnull((select min(expansion)from zone where zoneidnumber=floor(n.id/1000)),5)
,wis=ceil((level * 4.1)*.75) + case when raid_target=1 then level else 0 end + 4*ifnull((select min(expansion)from zone where zoneidnumber=floor(n.id/1000)),5)
,cha=ceil((level * 4.1)*.75) + case when raid_target=1 then level else 0 end + 4*ifnull((select min(expansion)from zone where zoneidnumber=floor(n.id/1000)),5);
+897 -1068
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, eSpecialAttacks special = eSpecialAttacks::None) { 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, int special = 0) { return; }
virtual bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false,
ExtraAttackOptions *opts = nullptr) { return false; }
ExtraAttackOptions *opts = nullptr, int special = 0) { return false; }
virtual bool HasRaid() { return false; }
virtual bool HasGroup() { return false; }
virtual Raid* GetRaid() { return 0; }
+3 -5
View File
@@ -46,7 +46,6 @@ void Mob::CalcBonuses()
CalcMaxHP();
CalcMaxMana();
SetAttackTimer();
CalcAC();
rooted = FindType(SE_Root);
}
@@ -670,10 +669,6 @@ 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
@@ -1532,6 +1527,9 @@ 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.
}
+301 -154
View File
@@ -1992,6 +1992,110 @@ bool Bot::CheckBotDoubleAttack(bool tripleAttack) {
return false;
}
void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, EQEmu::skills::SkillType skillinuse, int16 chance_mod, int16 focus, bool CanRiposte, int ReuseTime) {
if (!CanDoSpecialAttack(other))
return;
//For spells using skill value 98 (feral swipe ect) server sets this to 67 automatically.
if (skillinuse == EQEmu::skills::SkillBegging)
skillinuse = EQEmu::skills::SkillOffense;
int damage = 0;
uint32 hate = 0;
int Hand = EQEmu::inventory::slotPrimary;
if (hate == 0 && weapon_damage > 1)
hate = weapon_damage;
if(weapon_damage > 0) {
if(GetClass() == BERSERKER) {
int bonus = (3 + GetLevel( )/ 10);
weapon_damage = (weapon_damage * (100 + bonus) / 100);
}
int32 min_hit = 1;
int32 max_hit = ((2 * weapon_damage * GetDamageTable(skillinuse)) / 100);
if(GetLevel() >= 28 && IsWarriorClass()) {
int ucDamageBonus = GetWeaponDamageBonus((const EQEmu::ItemData*) nullptr);
min_hit += (int) ucDamageBonus;
max_hit += (int) ucDamageBonus;
hate += ucDamageBonus;
}
ApplySpecialAttackMod(skillinuse, max_hit, min_hit);
min_hit += (min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100);
if(max_hit < min_hit)
max_hit = min_hit;
if(RuleB(Combat, UseIntervalAC))
damage = max_hit;
else
damage = zone->random.Int(min_hit, max_hit);
if (other->AvoidDamage(this, damage, CanRiposte ? EQEmu::inventory::slotRange : EQEmu::inventory::slotPrimary)) { // MainRange excludes ripo, primary doesn't have any extra behavior
if (damage == -3) {
DoRiposte(other);
if (HasDied())
return;
}
} else {
if (other->CheckHitChance(this, skillinuse, Hand, chance_mod)) {
other->MeleeMitigation(this, damage, min_hit);
if (damage > 0) {
damage += damage*focus/100;
ApplyMeleeDamageBonus(skillinuse, damage);
damage += other->GetFcDamageAmtIncoming(this, 0, true, skillinuse);
damage += ((itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse));
TryCriticalHit(other, skillinuse, damage, nullptr);
}
} else {
damage = 0;
}
}
}
else
damage = -5;
if (skillinuse == EQEmu::skills::SkillBash){
const EQEmu::ItemInstance* inst = GetBotItem(EQEmu::inventory::slotSecondary);
const EQEmu::ItemData* botweapon = 0;
if(inst)
botweapon = inst->GetItem();
if(botweapon) {
if (botweapon->ItemType == EQEmu::item::ItemTypeShield)
hate += botweapon->AC;
hate = (hate * (100 + GetFuriousBash(botweapon->Focus.Effect)) / 100);
}
}
other->AddToHateList(this, hate);
bool CanSkillProc = true;
if (skillinuse == EQEmu::skills::SkillOffense){ //Hack to allow damage to display.
skillinuse = EQEmu::skills::SkillTigerClaw; //'strike' your opponent - Arbitrary choice for message.
CanSkillProc = false; //Disable skill procs
}
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse);
if (HasDied())
return;
if (damage > 0)
CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
if ((skillinuse == EQEmu::skills::SkillDragonPunch) && GetAA(aaDragonPunch) && zone->random.Int(0, 99) < 25){
SpellFinished(904, other, EQEmu::CastingSlot::Item, 0, -1, spells[904].ResistDiff);
other->Stun(100);
}
if (CanSkillProc && HasSkillProcs())
TrySkillProc(other, skillinuse, ReuseTime);
if (CanSkillProc && (damage > 0) && HasSkillProcSuccess())
TrySkillProc(other, skillinuse, ReuseTime, true);
}
void Bot::ApplySpecialAttackMod(EQEmu::skills::SkillType skill, int32 &dmg, int32 &mindmg) {
int item_slot = -1;
//1: Apply bonus from AC (BOOT/SHIELD/HANDS) est. 40AC=6dmg
@@ -3560,7 +3664,7 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, EQEmu::skills::Sk
return true;
}
void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic, eSpecialAttacks special) {
void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic, int special) {
if(spell_id == 0)
spell_id = SPELL_UNKNOWN;
@@ -3606,7 +3710,7 @@ void Bot::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
Mob::AddToHateList(other, hate, damage, iYellForHelp, bFrenzy, iBuffTic);
}
bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) {
bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts, int special) {
if (!other) {
SetTarget(nullptr);
Log.Out(Logs::General, Logs::Error, "A null Mob object was passed to Bot::Attack for evaluation!");
@@ -3674,7 +3778,25 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b
//if weapon damage > 0 then we know we can hit the target with this weapon
//otherwise we cannot and we set the damage to -5 later on
if(weapon_damage > 0) {
int min_damage = 0;
//Berserker Berserk damage bonus
if(berserk && (GetClass() == BERSERKER)){
int bonus = (3 + GetLevel() / 10); //unverified
weapon_damage = (weapon_damage * (100 + bonus) / 100);
Log.Out(Logs::Detail, Logs::Combat, "Berserker damage bonus increases DMG to %d", weapon_damage);
}
//try a finishing blow.. if successful end the attack
if(TryFinishingBlow(other, skillinuse))
return true;
//damage formula needs some work
int min_hit = 1;
int max_hit = ((2 * weapon_damage * GetDamageTable(skillinuse)) / 100);
if(GetLevel() < 10 && max_hit > RuleI(Combat, HitCapPre10))
max_hit = (RuleI(Combat, HitCapPre10));
else if(GetLevel() < 20 && max_hit > RuleI(Combat, HitCapPre20))
max_hit = (RuleI(Combat, HitCapPre20));
// ***************************************************************
// *** Calculate the damage bonus, if applicable, for this hit ***
@@ -3692,7 +3814,8 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b
// Damage bonuses apply only to hits from the main hand (Hand == MainPrimary) by characters level 28 and above
// who belong to a melee class. If we're here, then all of these conditions apply.
ucDamageBonus = GetWeaponDamageBonus(weapon ? weapon->GetItem() : (const EQEmu::ItemData*) nullptr);
min_damage = ucDamageBonus;
min_hit += (int) ucDamageBonus;
max_hit += (int) ucDamageBonus;
hate += ucDamageBonus;
}
#endif
@@ -3700,21 +3823,28 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b
if (Hand == EQEmu::inventory::slotSecondary) {
if (aabonuses.SecondaryDmgInc || itembonuses.SecondaryDmgInc || spellbonuses.SecondaryDmgInc){
ucDamageBonus = GetWeaponDamageBonus(weapon ? weapon->GetItem() : (const EQEmu::ItemData*) nullptr);
min_damage = ucDamageBonus;
min_hit += (int) ucDamageBonus;
max_hit += (int) ucDamageBonus;
hate += ucDamageBonus;
}
}
int min_cap = (base_damage * GetMeleeMinDamageMod_SE(skillinuse) / 100);
min_hit = (min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100);
Log.Out(Logs::Detail, Logs::Combat, "Damage calculated to %d (bonus %d, base %d, str %d, skill %d, DMG %d, lv %d)",
damage, min_damage, base_damage, GetSTR(), GetSkill(skillinuse), weapon_damage, mylevel);
if(max_hit < min_hit)
max_hit = min_hit;
auto offense = this->offense(skillinuse);
if(RuleB(Combat, UseIntervalAC))
damage = max_hit;
else
damage = zone->random.Int(min_hit, max_hit);
Log.Out(Logs::Detail, Logs::Combat, "Damage calculated to %d (min %d, max %d, str %d, skill %d, DMG %d, lv %d)",
damage, min_hit, max_hit, GetSTR(), GetSkill(skillinuse), weapon_damage, GetLevel());
if(opts) {
base_damage *= opts->damage_percent;
base_damage += opts->damage_flat;
damage *= opts->damage_percent;
damage += opts->damage_flat;
hate *= opts->hate_percent;
hate += opts->hate_flat;
}
@@ -3735,12 +3865,11 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b
}
}
} else {
if (other->CheckHitChance(this, skillinuse)) {
other->MeleeMitigation(this, damage, base_damage, offense, skillinuse, opts);
if (damage > 0) {
ApplyDamageTable(damage, offense);
CommonOutgoingHitSuccess(other, damage, min_damage, min_cap, skillinuse, opts);
}
if (other->CheckHitChance(this, skillinuse, Hand)) {
other->MeleeMitigation(this, damage, min_hit, opts);
ApplyMeleeDamageBonus(skillinuse, damage);
damage += ((itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse));
TryCriticalHit(other, skillinuse, damage, opts);
} else {
damage = 0;
}
@@ -3767,7 +3896,39 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b
if (damage > 0)
CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
CommonBreakInvisibleFromCombat();
//break invis when you attack
if(invisible) {
Log.Out(Logs::Detail, Logs::Combat, "Removing invisibility due to melee attack.");
BuffFadeByEffect(SE_Invisibility);
BuffFadeByEffect(SE_Invisibility2);
invisible = false;
}
if(invisible_undead) {
Log.Out(Logs::Detail, Logs::Combat, "Removing invisibility vs. undead due to melee attack.");
BuffFadeByEffect(SE_InvisVsUndead);
BuffFadeByEffect(SE_InvisVsUndead2);
invisible_undead = false;
}
if(invisible_animals){
Log.Out(Logs::Detail, Logs::Combat, "Removing invisibility vs. animals due to melee attack.");
BuffFadeByEffect(SE_InvisVsAnimals);
invisible_animals = false;
}
if(hidden || improved_hidden){
hidden = false;
improved_hidden = false;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer;
sa_out->spawn_id = GetID();
sa_out->type = 0x03;
sa_out->parameter = 0;
entity_list.QueueClients(this, outapp, true);
safe_delete(outapp);
}
if (spellbonuses.NegateIfCombat)
BuffFadeByEffect(SE_NegateIfCombat);
@@ -4655,24 +4816,21 @@ int Bot::GetHandToHandDamage(void) {
return 2;
}
bool Bot::TryFinishingBlow(Mob *defender, EQEmu::skills::SkillType skillinuse, int &damage)
{
bool Bot::TryFinishingBlow(Mob *defender, EQEmu::skills::SkillType skillinuse) {
if (!defender)
return false;
if (aabonuses.FinishingBlow[1] && !defender->IsClient() && defender->GetHPRatio() < 10) {
int chance = (aabonuses.FinishingBlow[0] / 10);
int fb_damage = aabonuses.FinishingBlow[1];
int levelreq = aabonuses.FinishingBlowLvl[0];
if (defender->GetLevel() <= levelreq && (chance >= zone->random.Int(0, 1000))) {
Log.Out(Logs::Detail, Logs::Combat, "Landed a finishing blow: levelreq at %d, other level %d",
levelreq, defender->GetLevel());
uint32 chance = (aabonuses.FinishingBlow[0] / 10);
uint32 damage = aabonuses.FinishingBlow[1];
uint16 levelreq = aabonuses.FinishingBlowLvl[0];
if(defender->GetLevel() <= levelreq && (chance >= zone->random.Int(0, 1000))){
Log.Out(Logs::Detail, Logs::Combat, "Landed a finishing blow: levelreq at %d, other level %d", levelreq , defender->GetLevel());
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FINISHING_BLOW, GetName());
damage = fb_damage;
defender->Damage(this, damage, SPELL_UNKNOWN, skillinuse);
return true;
} else {
Log.Out(Logs::Detail, Logs::Combat, "FAILED a finishing blow: levelreq at %d, other level %d",
levelreq, defender->GetLevel());
Log.Out(Logs::Detail, Logs::Combat, "FAILED a finishing blow: levelreq at %d, other level %d", levelreq , defender->GetLevel());
return false;
}
}
@@ -4700,92 +4858,6 @@ void Bot::DoRiposte(Mob* defender) {
}
}
int Bot::GetBaseSkillDamage(EQEmu::skills::SkillType skill, Mob *target)
{
int base = EQEmu::skills::GetBaseDamage(skill);
auto skill_level = GetSkill(skill);
switch (skill) {
case EQEmu::skills::SkillDragonPunch:
case EQEmu::skills::SkillEagleStrike:
case EQEmu::skills::SkillTigerClaw:
if (skill_level >= 25)
base++;
if (skill_level >= 75)
base++;
if (skill_level >= 125)
base++;
if (skill_level >= 175)
base++;
return base;
case EQEmu::skills::SkillFrenzy:
if (GetBotItem(EQEmu::inventory::slotSecondary)) {
if (GetLevel() > 15)
base += GetLevel() - 15;
if (base > 23)
base = 23;
if (GetLevel() > 50)
base += 2;
if (GetLevel() > 54)
base++;
if (GetLevel() > 59)
base++;
}
return base;
case EQEmu::skills::SkillFlyingKick: {
float skill_bonus = skill_level / 9.0f;
float ac_bonus = 0.0f;
auto inst = GetBotItem(EQEmu::inventory::slotFeet);
if (inst)
ac_bonus = inst->GetItemArmorClass(true) / 25.0f;
if (ac_bonus > skill_bonus)
ac_bonus = skill_bonus;
return static_cast<int>(ac_bonus + skill_bonus);
}
case EQEmu::skills::SkillKick: {
float skill_bonus = skill_level / 10.0f;
float ac_bonus = 0.0f;
auto inst = GetBotItem(EQEmu::inventory::slotFeet);
if (inst)
ac_bonus = inst->GetItemArmorClass(true) / 25.0f;
if (ac_bonus > skill_bonus)
ac_bonus = skill_bonus;
return static_cast<int>(ac_bonus + skill_bonus);
}
case EQEmu::skills::SkillBash: {
float skill_bonus = skill_level / 10.0f;
float ac_bonus = 0.0f;
const EQEmu::ItemInstance *inst = nullptr;
if (HasShieldEquiped())
inst = GetBotItem(EQEmu::inventory::slotSecondary);
else if (HasTwoHanderEquipped())
inst = GetBotItem(EQEmu::inventory::slotPrimary);
if (inst)
ac_bonus = inst->GetItemArmorClass(true) / 25.0f;
if (ac_bonus > skill_bonus)
ac_bonus = skill_bonus;
return static_cast<int>(ac_bonus + skill_bonus);
}
case EQEmu::skills::SkillBackstab: {
float skill_bonus = static_cast<float>(skill_level) * 0.02f;
auto inst = GetBotItem(EQEmu::inventory::slotPrimary);
if (inst && inst->GetItem() && inst->GetItem()->ItemType == EQEmu::item::ItemType1HPiercing) {
base = inst->GetItemBackstabDamage(true);
if (!inst->GetItemBackstabDamage())
base += inst->GetItemWeaponDamage(true);
if (target) {
if (inst->GetItemElementalFlag(true) && inst->GetItemElementalDamage(true))
base += target->ResistElementalWeaponDmg(inst);
if (inst->GetItemBaneDamageBody(true) || inst->GetItemBaneDamageRace(true))
base += target->CheckBaneDamage(inst);
}
}
return static_cast<int>(static_cast<float>(base) * (skill_bonus + 2.0f));
}
default:
return 0;
}
}
void Bot::DoSpecialAttackDamage(Mob *who, EQEmu::skills::SkillType skill, int32 max_damage, int32 min_damage, int32 hate_override, int ReuseTime, bool HitChance) {
int32 hate = max_damage;
if(hate_override > -1)
@@ -4805,36 +4877,33 @@ void Bot::DoSpecialAttackDamage(Mob *who, EQEmu::skills::SkillType skill, int32
}
}
int min_cap = max_damage * GetMeleeMinDamageMod_SE(skill) / 100;
min_damage += (min_damage * GetMeleeMinDamageMod_SE(skill) / 100);
int hand = EQEmu::inventory::slotPrimary;
int damage = 0;
auto offense = this->offense(skill);
if (skill == EQEmu::skills::SkillThrowing || skill == EQEmu::skills::SkillArchery)
hand = EQEmu::inventory::slotRange;
if (who->AvoidDamage(this, damage, hand)) {
if (damage == -3)
if (who->AvoidDamage(this, max_damage, hand)) {
if (max_damage == -3)
DoRiposte(who);
} else {
if (HitChance || who->CheckHitChance(this, skill)) {
if (max_damage > 0)
who->MeleeMitigation(this, damage, max_damage, offense, skill);
if (damage > 0) {
ApplyDamageTable(damage, offense);
CommonOutgoingHitSuccess(who, damage, min_damage, min_cap, skill);
}
if (HitChance || who->CheckHitChance(this, skill, EQEmu::inventory::slotPrimary)) {
who->MeleeMitigation(this, max_damage, min_damage);
ApplyMeleeDamageBonus(skill, max_damage);
max_damage += who->GetFcDamageAmtIncoming(this, 0, true, skill);
max_damage += ((itembonuses.HeroicSTR / 10) + (max_damage * who->GetSkillDmgTaken(skill) / 100) + GetSkillDmgAmt(skill));
TryCriticalHit(who, skill, max_damage);
} else {
damage = 0;
max_damage = 0;
}
}
who->AddToHateList(this, hate);
who->Damage(this, damage, SPELL_UNKNOWN, skill, false);
who->Damage(this, max_damage, SPELL_UNKNOWN, skill, false);
if(!GetTarget() || HasDied())
return;
if (damage > 0)
if (max_damage > 0)
CheckNumHitsRemaining(NumHit::OutgoingHitSuccess);
//[AA Dragon Punch] value[0] = 100 for 25%, chance value[1] = skill
@@ -4850,7 +4919,7 @@ void Bot::DoSpecialAttackDamage(Mob *who, EQEmu::skills::SkillType skill, int32
if (HasSkillProcs())
TrySkillProc(who, skill, (ReuseTime * 1000));
if (damage > 0 && HasSkillProcSuccess())
if (max_damage > 0 && HasSkillProcSuccess())
TrySkillProc(who, skill, (ReuseTime * 1000), true);
}
@@ -4898,33 +4967,72 @@ void Bot::TryBackstab(Mob *other, int ReuseTime) {
}
}
} else if(aabonuses.FrontalBackstabMinDmg || itembonuses.FrontalBackstabMinDmg || spellbonuses.FrontalBackstabMinDmg) {
m_specialattacks = eSpecialAttacks::ChaoticStab;
RogueBackstab(other, true);
m_specialattacks = eSpecialAttacks::None;
if (level > 54) {
float DoubleAttackProbability = ((GetSkill(EQEmu::skills::SkillDoubleAttack) + GetLevel()) / 500.0f);
if(zone->random.Real(0, 1) < DoubleAttackProbability)
if(other->GetHP() > 0)
RogueBackstab(other,true, ReuseTime);
if (tripleChance && other->GetHP() > 0 && tripleChance > zone->random.Int(0, 100))
RogueBackstab(other,false,ReuseTime);
}
}
else
Attack(other, EQEmu::inventory::slotPrimary);
}
void Bot::RogueBackstab(Mob *other, bool min_damage, int ReuseTime)
{
if (!other)
return;
EQEmu::ItemInstance *botweaponInst = GetBotItem(EQEmu::inventory::slotPrimary);
if (botweaponInst) {
if (!GetWeaponDamage(other, botweaponInst))
return;
} else if (!GetWeaponDamage(other, (const EQEmu::ItemData *)nullptr)) {
return;
void Bot::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) {
int32 ndamage = 0;
int32 max_hit = 0;
int32 min_hit = 0;
int32 hate = 0;
int32 primaryweapondamage = 0;
int32 backstab_dmg = 0;
EQEmu::ItemInstance* botweaponInst = GetBotItem(EQEmu::inventory::slotPrimary);
if(botweaponInst) {
primaryweapondamage = GetWeaponDamage(other, botweaponInst);
backstab_dmg = botweaponInst->GetItem()->BackstabDmg;
for (int i = EQEmu::inventory::socketBegin; i < EQEmu::inventory::SocketCount; ++i) {
EQEmu::ItemInstance *aug = botweaponInst->GetAugment(i);
if(aug)
backstab_dmg += aug->GetItem()->BackstabDmg;
}
} else {
primaryweapondamage = ((GetLevel() / 7) + 1);
backstab_dmg = primaryweapondamage;
}
uint32 hate = 0;
if(primaryweapondamage > 0) {
if(level > 25) {
max_hit = (((((2 * backstab_dmg) * GetDamageTable(EQEmu::skills::SkillBackstab) / 100) * 10 * GetSkill(EQEmu::skills::SkillBackstab) / 355) + ((level - 25) / 3) + 1) * ((100 + RuleI(Combat, BackstabBonus)) / 100));
hate = (20 * backstab_dmg * GetSkill(EQEmu::skills::SkillBackstab) / 355);
} else {
max_hit = (((((2 * backstab_dmg) * GetDamageTable(EQEmu::skills::SkillBackstab) / 100) * 10 * GetSkill(EQEmu::skills::SkillBackstab) / 355) + 1) * ((100 + RuleI(Combat, BackstabBonus)) / 100));
hate = (20 * backstab_dmg * GetSkill(EQEmu::skills::SkillBackstab) / 355);
}
int base_damage = GetBaseSkillDamage(EQEmu::skills::SkillBackstab, other);
hate = base_damage;
if (level < 51)
min_hit = (level * 15 / 10);
else
min_hit = ((level * ( level * 5 - 105)) / 100);
DoSpecialAttackDamage(other, EQEmu::skills::SkillBackstab, base_damage, 0, hate, ReuseTime);
if (!other->CheckHitChance(this, EQEmu::skills::SkillBackstab, 0))
ndamage = 0;
else {
if (min_damage) {
ndamage = min_hit;
} else {
if (max_hit < min_hit)
max_hit = min_hit;
ndamage = (RuleB(Combat, UseIntervalAC) ? max_hit : zone->random.Int(min_hit, max_hit));
}
}
} else
ndamage = -5;
DoSpecialAttackDamage(other, EQEmu::skills::SkillBackstab, ndamage, min_hit, hate, ReuseTime);
DoAnim(anim1HPiercing);
}
@@ -5049,25 +5157,41 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
if (!target->CheckHitChance(this, EQEmu::skills::SkillBash, 0))
dmg = 0;
else {
dmg = GetBaseSkillDamage(EQEmu::skills::SkillBash);
if(RuleB(Combat, UseIntervalAC))
dmg = GetBashDamage();
else
dmg = zone->random.Int(1, GetBashDamage());
}
}
reuse = (BashReuseTime * 1000);
DoSpecialAttackDamage(target, EQEmu::skills::SkillBash, dmg, 0, -1, reuse);
DoSpecialAttackDamage(target, EQEmu::skills::SkillBash, dmg, 1, -1, reuse);
did_attack = true;
}
}
if (skill_to_use == EQEmu::skills::SkillFrenzy) {
int AtkRounds = 3;
int32 max_dmg = GetBaseSkillDamage(EQEmu::skills::SkillFrenzy);
int skillmod = 0;
if (MaxSkill(EQEmu::skills::SkillFrenzy) > 0)
skillmod = (100 * GetSkill(EQEmu::skills::SkillFrenzy) / MaxSkill(EQEmu::skills::SkillFrenzy));
int32 max_dmg = (26 + ((((GetLevel() - 6) * 2) * skillmod) / 100)) * ((100 + RuleI(Combat, FrenzyBonus)) / 100);
int32 min_dmg = 0;
DoAnim(anim2HSlashing);
if (GetLevel() < 51)
min_dmg = 1;
else
min_dmg = (GetLevel() * 8 / 10);
if (min_dmg > max_dmg)
max_dmg = min_dmg;
reuse = (FrenzyReuseTime * 1000);
did_attack = true;
while(AtkRounds > 0) {
if (GetTarget() && (AtkRounds == 1 || zone->random.Int(0, 100) < 75)) {
DoSpecialAttackDamage(GetTarget(), EQEmu::skills::SkillFrenzy, max_dmg, 0, max_dmg, reuse, true);
DoSpecialAttackDamage(GetTarget(), EQEmu::skills::SkillFrenzy, max_dmg, min_dmg, max_dmg, reuse, true);
}
AtkRounds--;
@@ -5083,11 +5207,14 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
if (!target->CheckHitChance(this, EQEmu::skills::SkillKick, 0))
dmg = 0;
else {
dmg = GetBaseSkillDamage(EQEmu::skills::SkillKick);
if(RuleB(Combat, UseIntervalAC))
dmg = GetKickDamage();
else
dmg = zone->random.Int(1, GetKickDamage());
}
}
reuse = (KickReuseTime * 1000);
DoSpecialAttackDamage(target, EQEmu::skills::SkillKick, dmg, 0, -1, reuse);
DoSpecialAttackDamage(target, EQEmu::skills::SkillKick, dmg, 1, -1, reuse);
did_attack = true;
}
}
@@ -5122,6 +5249,26 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
classattack_timer.Start(reuse / HasteModifier);
}
bool Bot::TryHeadShot(Mob* defender, EQEmu::skills::SkillType skillInUse) {
bool Result = false;
if (defender && (defender->GetBodyType() == BT_Humanoid) && (skillInUse == EQEmu::skills::SkillArchery) && (GetClass() == RANGER) && (GetLevel() >= 62)) {
int defenderLevel = defender->GetLevel();
int rangerLevel = GetLevel();
if(GetAA(aaHeadshot) && ((defenderLevel - 46) <= GetAA(aaHeadshot) * 2)) {
float AttackerChance = 0.20f + ((float)(rangerLevel - 51) * 0.005f);
float DefenderChance = (float)zone->random.Real(0.00f, 1.00f);
if(AttackerChance > DefenderChance) {
Log.Out(Logs::Detail, Logs::Combat, "Landed a headshot: Attacker chance was %f and Defender chance was %f.", AttackerChance, DefenderChance);
entity_list.MessageClose(this, false, 200, MT_CritMelee, "%s has scored a leathal HEADSHOT!", GetName());
defender->Damage(this, (defender->GetMaxHP()+50), SPELL_UNKNOWN, skillInUse);
Result = true;
} else
Log.Out(Logs::Detail, Logs::Combat, "FAILED a headshot: Attacker chance was %f and Defender chance was %f.", AttackerChance, DefenderChance);
}
}
return Result;
}
int32 Bot::CheckAggroAmount(uint16 spellid) {
int32 AggroAmount = Mob::CheckAggroAmount(spellid, nullptr);
int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid);
+6 -4
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, eSpecialAttacks special = eSpecialAttacks::None);
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 bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false,
ExtraAttackOptions *opts = nullptr);
ExtraAttackOptions *opts = nullptr, int special = 0);
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, int &damage);
virtual bool TryFinishingBlow(Mob *defender, EQEmu::skills::SkillType skillinuse);
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,12 +248,13 @@ 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);
@@ -502,6 +503,7 @@ 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; }
+5 -37
View File
@@ -6863,46 +6863,16 @@ 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\">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>" <<
/* 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>" <<
/* 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>" <<
@@ -6940,9 +6910,7 @@ 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, " 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());
client->Message(0, " AC: %i ( Mit.: %i + Avoid.: %i + Spell: %i ) | Shield AC: %i", CalcAC(), GetACMit(), GetACAvoid(), spellbonuses.AC, shield_ac);
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());
+9 -4
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, eSpecialAttacks special = eSpecialAttacks::None);
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 bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false,
ExtraAttackOptions *opts = nullptr);
ExtraAttackOptions *opts = nullptr, int special = 0);
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,6 +418,8 @@ 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;
@@ -1290,7 +1292,7 @@ private:
void OPGMEndTraining(const EQApplicationPacket *app);
void OPGMTrainSkill(const EQApplicationPacket *app);
void OPGMSummon(const EQApplicationPacket *app);
void OPCombatAbility(const CombatAbility_Struct *ca_atk);
void OPCombatAbility(const EQApplicationPacket *app);
// Bandolier Methods
void CreateBandolier(const EQApplicationPacket *app);
@@ -1299,6 +1301,9 @@ private:
void HandleTraderPriceUpdate(const EQApplicationPacket *app);
int32 CalcAC();
int32 GetACMit();
int32 GetACAvoid();
int32 CalcATK();
int32 CalcItemATKCap();
int32 CalcHaste();
+117
View File
@@ -1025,6 +1025,111 @@ 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()) {
@@ -2127,5 +2232,17 @@ int Client::GetRawACNoShield(int &shield_ac) const
}
}
}
Log.Out(
Logs::General,
Logs::Combat,
"[%s] [Client::GetRawACNoShield] AC [%i] ItemAC [%i] SpellAC [%i] AAAC [%i]",
GetName(),
ac,
itembonuses.AC,
spellbonuses.AC,
aabonuses.AC
);
return ac;
}
+1 -2
View File
@@ -4622,8 +4622,7 @@ 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;
}
auto ca_atk = (CombatAbility_Struct *)app->pBuffer;
OPCombatAbility(ca_atk);
OPCombatAbility(app);
return;
}
-6
View File
@@ -638,11 +638,5 @@ struct ExtraAttackOptions {
};
struct DamageTable {
int32 max_extra; // max extra damage
int32 chance; // chance not to apply?
int32 minusfactor; // difficulty of rolling
};
#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, 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 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 bool HasRaid() { return false; }
virtual bool HasGroup() { return false; }
virtual Raid* GetRaid() { return 0; }
+1 -1
View File
@@ -248,7 +248,7 @@ void Embperl::init_eval_file(void)
"} else {"
//we 'my' $filename,$mtime,$package,$sub to prevent them from changing our state up here.
" eval(\"package $package; my(\\$filename,\\$mtime,\\$package,\\$sub); \\$isloaded = 1; require './$filename'; \");"
/* "local *FH;open FH, $filename or die \"open '$filename' $!\";"
/* "local *FH;open FH, $filename or die \"open '$filename' $!\";"
"local($/) = undef;my $sub = <FH>;close FH;"
"my $eval = qq{package $package; sub handler { $sub; }};"
"{ my($filename,$mtime,$package,$sub); eval $eval; }"
+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, eSpecialAttacks special = eSpecialAttacks::None) { 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, int special = 0) { return; }
virtual bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false,
ExtraAttackOptions *opts = nullptr) {
ExtraAttackOptions *opts = nullptr, int special = 0) {
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);
caster->ProcessAttackRounds(mob, opts, 1);
}
}
+15 -6
View File
@@ -4432,8 +4432,13 @@ void Merc::DoClassAttacks(Mob *target) {
dmg = -5;
}
else{
if (target->CheckHitChance(this, EQEmu::skills::SkillKick, 0))
dmg = GetBaseSkillDamage(EQEmu::skills::SkillKick, GetTarget());
if (target->CheckHitChance(this, EQEmu::skills::SkillKick, 0)) {
if(RuleB(Combat, UseIntervalAC))
dmg = GetKickDamage();
else
dmg = zone->random.Int(1, GetKickDamage());
}
}
reuse = KickReuseTime * 1000;
@@ -4449,8 +4454,12 @@ void Merc::DoClassAttacks(Mob *target) {
dmg = -5;
}
else{
if (target->CheckHitChance(this, EQEmu::skills::SkillBash, 0))
dmg = GetBaseSkillDamage(EQEmu::skills::SkillBash, GetTarget());
if (target->CheckHitChance(this, EQEmu::skills::SkillBash, 0)) {
if(RuleB(Combat, UseIntervalAC))
dmg = GetBashDamage();
else
dmg = zone->random.Int(1, GetBashDamage());
}
}
reuse = BashReuseTime * 1000;
@@ -4465,7 +4474,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)
bool Merc::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts, int special)
{
if (!other) {
SetTarget(nullptr);
@@ -4476,7 +4485,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, eSpecialAttacks special)
void Merc::Damage(Mob* other, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic, int special)
{
if(IsDead() || IsCorpse())
return;
+3 -2
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, eSpecialAttacks special = eSpecialAttacks::None);
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 bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false,
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr);
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr, int special = 0);
virtual bool HasRaid() { return false; }
virtual bool HasGroup() { return (GetGroup() ? true : false); }
virtual Raid* GetRaid() { return 0; }
@@ -197,6 +197,7 @@ 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;
+32 -13
View File
@@ -109,9 +109,7 @@ Mob::Mob(const char* in_name,
m_TargetV(glm::vec3()),
flee_timer(FLEE_CHECK_TIMER),
m_Position(position),
tmHidden(-1),
mitigation_ac(0),
m_specialattacks(eSpecialAttacks::None)
tmHidden(-1)
{
targeted = 0;
tar_ndx=0;
@@ -1493,9 +1491,6 @@ void Mob::ShowStats(Client* client)
spawngroupid = n->respawn2->SpawnGroupID();
client->Message(0, " NPCID: %u SpawnGroupID: %u Grid: %i LootTable: %u FactionID: %i SpellsID: %u ", GetNPCTypeID(),spawngroupid, n->GetGrid(), n->GetLoottableID(), n->GetNPCFactionID(), n->GetNPCSpellsID());
client->Message(0, " Accuracy: %i MerchantID: %i EmoteID: %i Runspeed: %.3f Walkspeed: %.3f", n->GetAccuracyRating(), n->MerchantType, n->GetEmoteID(), static_cast<float>(0.025f * n->GetRunspeed()), static_cast<float>(0.025f * n->GetWalkspeed()));
client->Message(0, " compute_tohit: %i TotalToHit: %i", n->compute_tohit(EQEmu::skills::SkillHandtoHand), n->GetTotalToHit(EQEmu::skills::SkillHandtoHand, 0));
client->Message(0, " compute_defense: %i TotalDefense: %i", n->compute_defense(), n->GetTotalDefense());
client->Message(0, " offense: %i mitigation ac: %i", n->offense(EQEmu::skills::SkillHandtoHand), n->GetMitigationAC());
n->QueryLoot(client);
}
if (IsAIControlled()) {
@@ -4608,11 +4603,16 @@ int16 Mob::GetCritDmgMob(uint16 skill)
int critDmg_mod = 0;
// All skill dmg mod + Skill specific
critDmg_mod += itembonuses.CritDmgMob[EQEmu::skills::HIGHEST_SKILL + 1] + spellbonuses.CritDmgMob[EQEmu::skills::HIGHEST_SKILL + 1] + aabonuses.CritDmgMob[EQEmu::skills::HIGHEST_SKILL + 1] +
itembonuses.CritDmgMob[skill] + spellbonuses.CritDmgMob[skill] + aabonuses.CritDmgMob[skill];
critDmg_mod += itembonuses.CritDmgMob[EQEmu::skills::HIGHEST_SKILL + 1] +
spellbonuses.CritDmgMob[EQEmu::skills::HIGHEST_SKILL + 1] +
aabonuses.CritDmgMob[EQEmu::skills::HIGHEST_SKILL + 1] +
itembonuses.CritDmgMob[skill] +
spellbonuses.CritDmgMob[skill] +
aabonuses.CritDmgMob[skill];
if(critDmg_mod < -100)
if (critDmg_mod < -100) {
critDmg_mod = -100;
}
return critDmg_mod;
}
@@ -4648,16 +4648,35 @@ void Mob::SetRaidGrouped(bool v)
}
}
int Mob::GetCriticalChanceBonus(uint16 skill)
int16 Mob::GetCriticalChanceBonus(uint16 skill)
{
int critical_chance = 0;
// All skills + Skill specific
critical_chance += itembonuses.CriticalHitChance[EQEmu::skills::HIGHEST_SKILL + 1] + spellbonuses.CriticalHitChance[EQEmu::skills::HIGHEST_SKILL + 1] + aabonuses.CriticalHitChance[EQEmu::skills::HIGHEST_SKILL + 1] +
itembonuses.CriticalHitChance[skill] + spellbonuses.CriticalHitChance[skill] + aabonuses.CriticalHitChance[skill];
critical_chance += itembonuses.CriticalHitChance[EQEmu::skills::HIGHEST_SKILL + 1] +
spellbonuses.CriticalHitChance[EQEmu::skills::HIGHEST_SKILL + 1] +
aabonuses.CriticalHitChance[EQEmu::skills::HIGHEST_SKILL + 1] +
itembonuses.CriticalHitChance[skill] +
spellbonuses.CriticalHitChance[skill] +
aabonuses.CriticalHitChance[skill];
if(critical_chance < -100)
Log.Out(
Logs::General,
Logs::Combat,
"[%s] [Mob::GetCriticalChanceBonus] Bonuses | Item [%i] Spell [%i] AA [%i] | 2nd Item [%i] Spell [%i] AA [%i] Final Chance [%i]",
GetCleanName(),
itembonuses.CriticalHitChance[EQEmu::skills::HIGHEST_SKILL + 1],
spellbonuses.CriticalHitChance[EQEmu::skills::HIGHEST_SKILL + 1],
aabonuses.CriticalHitChance[EQEmu::skills::HIGHEST_SKILL + 1],
itembonuses.CriticalHitChance[skill],
spellbonuses.CriticalHitChance[skill],
aabonuses.CriticalHitChance[skill],
critical_chance
);
if (critical_chance < -100) {
critical_chance = -100;
}
return critical_chance;
}
+29 -51
View File
@@ -54,13 +54,6 @@ 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,
@@ -159,53 +152,43 @@ 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) = 0;
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr, int special = 0) = 0;
int MonkSpecialAttack(Mob* other, uint8 skill_used);
virtual void TryBackstab(Mob *other,int ReuseTime = 10);
bool AvoidDamage(Mob* attacker, int &damage, int hand);
int compute_tohit(EQEmu::skills::SkillType skillinuse);
int GetTotalToHit(EQEmu::skills::SkillType skill, int chance_mod); // compute_tohit + spell bonuses
int compute_defense();
int GetTotalDefense(); // compute_defense + spell bonuses
bool CheckHitChance(Mob* attacker, EQEmu::skills::SkillType skillinuse, int chance_mod = 0);
virtual void TryCriticalHit(Mob *defender, uint16 skill, int &damage, int min_damage, ExtraAttackOptions *opts = nullptr);
void TryPetCriticalHit(Mob *defender, uint16 skill, int &damage);
virtual bool TryFinishingBlow(Mob *defender, EQEmu::skills::SkillType skillinuse, int &damage);
int TryHeadShot(Mob* defender, EQEmu::skills::SkillType skillInUse);
int TryAssassinate(Mob* defender, EQEmu::skills::SkillType skillInUse, uint16 ReuseTime);
bool AvoidDamage(Mob* attacker, int32 &damage, int hand);
virtual bool CheckHitChance(Mob* attacker, EQEmu::skills::SkillType skillinuse, int Hand, int16 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);
virtual void DoRiposte(Mob* defender);
void ApplyMeleeDamageBonus(uint16 skill, int &damage,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, int &damage, int base_damage, int offense, EQEmu::skills::SkillType, ExtraAttackOptions *opts = nullptr);
double RollD20(double offense, double mitigation); // CALL THIS FROM THE 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);
bool CombatRange(Mob* other);
virtual inline bool IsBerserk() { return false; } // only clients
void RogueEvade(Mob *other);
void CommonOutgoingHitSuccess(Mob* defender, int &damage, int min_damage, int min_mod, EQEmu::skills::SkillType skillInUse, ExtraAttackOptions *opts = nullptr);
void CommonOutgoingHitSuccess(Mob* defender, int32 &damage, EQEmu::skills::SkillType skillInUse, ExtraAttackOptions *opts = nullptr);
void BreakInvisibleSpells();
virtual void CancelSneakHide();
void CommonBreakInvisible();
void CommonBreakInvisibleFromCombat();
bool HasDied();
virtual bool CheckDualWield();
void DoMainHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr);
void DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr);
void DoMainHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr, int special = 0);
void DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr, int special = 0);
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)
void ProcessAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr, int special = 0)
{
if (target) {
DoMainHandAttackRounds(target, opts);
DoMainHandAttackRounds(target, opts, special);
if (CanThisClassDualWield())
DoOffHandAttackRounds(target, opts);
DoOffHandAttackRounds(target, opts, special);
}
return;
}
@@ -382,7 +365,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, eSpecialAttacks special = eSpecialAttacks::None) = 0;
bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, int special = 0) = 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;}
@@ -421,7 +404,7 @@ public:
virtual void SetTarget(Mob* mob);
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 int32 GetAC() const { return AC; }
inline virtual int32 GetAC() const { return AC + itembonuses.AC + spellbonuses.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; }
@@ -688,7 +671,7 @@ public:
int16 GetMeleeMinDamageMod_SE(uint16 skill);
int16 GetCrippBlowChance();
int16 GetSkillReuseTime(uint16 skill);
int GetCriticalChanceBonus(uint16 skill);
int16 GetCriticalChanceBonus(uint16 skill);
int16 GetSkillDmgAmt(uint16 skill);
bool TryReflectSpell(uint32 spell_id);
bool CanBlockSpell() const { return(spellbonuses.BlockNextSpell); }
@@ -793,8 +776,7 @@ public:
int32 GetMeleeMitigation();
uint8 GetWeaponDamageBonus(const EQEmu::ItemData* weapon, bool offhand = false);
const DamageTable &GetDamageTable() const;
void ApplyDamageTable(int &damage, int offense);
uint16 GetDamageTable(EQEmu::skills::SkillType skillinuse);
virtual int GetHandToHandDamage(void);
bool CanThisClassDoubleAttack(void) const;
@@ -817,9 +799,9 @@ public:
int32 AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTic, Mob* attacker);
int32 ReduceAllDamage(int32 damage);
void DoSpecialAttackDamage(Mob *who, EQEmu::skills::SkillType skill, int base_damage, int min_damage = 0, int32 hate_override = -1, int ReuseTime = 10, bool CheckHitChance = false, bool CanAvoid = true);
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);
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);
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 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();
@@ -832,7 +814,6 @@ public:
bool AddRampage(Mob*);
void ClearRampage();
void AreaRampage(ExtraAttackOptions *opts);
inline bool IsSpecialAttack(eSpecialAttacks in) { return m_specialattacks == in; }
void StartEnrage();
void ProcessEnrage();
@@ -1059,7 +1040,7 @@ public:
#endif
protected:
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);
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);
static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
float _GetMovementSpeed(int mod) const;
int _GetWalkSpeed() const;
@@ -1096,7 +1077,6 @@ protected:
bool multitexture;
int AC;
int mitigation_ac; // cached Mob::ACSum
int32 ATK;
int32 STR;
int32 STA;
@@ -1168,7 +1148,6 @@ protected:
int base_walkspeed;
int base_fearspeed;
int current_speed;
eSpecialAttacks m_specialattacks;
uint32 pLastChange;
bool held;
@@ -1193,10 +1172,9 @@ protected:
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);
#ifdef BOTS
virtual
#endif
int GetBaseSkillDamage(EQEmu::skills::SkillType skill, Mob *target = nullptr);
int GetKickDamage();
int GetBashDamage();
virtual void ApplySpecialAttackMod(EQEmu::skills::SkillType skill, int32 &dmg, int32 &mindmg);
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);
@@ -1231,7 +1209,7 @@ protected:
Timer attack_dw_timer;
Timer ranged_timer;
float attack_speed; //% increase/decrease in attack speed (not haste)
int attack_delay; //delay between attacks in 10ths of seconds
int8 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;
+19 -12
View File
@@ -1160,8 +1160,11 @@ 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)) {
Rampage(nullptr);
ExtraAttackOptions opts;
opts.damage_percent = dmg_mod / 100.0f;
Rampage(&opts);
}
}
}
@@ -1172,7 +1175,12 @@ 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, 3);
int cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 2);
if(cur > 0) {
opts.damage_percent = cur / 100.0f;
}
cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 3);
if(cur > 0) {
opts.damage_flat = cur;
}
@@ -1207,7 +1215,12 @@ 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, 3);
int cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 2);
if(cur > 0) {
opts.damage_percent = cur / 100.0f;
}
cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 3);
if(cur > 0) {
opts.damage_flat = cur;
}
@@ -1979,8 +1992,6 @@ 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;
@@ -1990,16 +2001,14 @@ bool Mob::Rampage(ExtraAttackOptions *opts)
if (m_target == GetTarget())
continue;
if (CombatRange(m_target)) {
ProcessAttackRounds(m_target, opts);
ProcessAttackRounds(m_target, opts, 2);
index_hit++;
}
}
}
if (RuleB(Combat, RampageHitsTarget) && index_hit < rampage_targets)
ProcessAttackRounds(GetTarget(), opts);
m_specialattacks = eSpecialAttacks::None;
ProcessAttackRounds(GetTarget(), opts, 2);
return true;
}
@@ -2015,12 +2024,10 @@ 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);
m_specialattacks = eSpecialAttacks::None;
ProcessAttackRounds(GetTarget(), opts, 1);
}
uint32 Mob::GetLevelCon(uint8 mylevel, uint8 iOtherLevel) {
+4 -19
View File
@@ -201,9 +201,6 @@ 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;
@@ -1057,7 +1054,7 @@ NPC* NPC::SpawnNPC(const char* spawncommand, const glm::vec4& position, Client*
npc_type->WIS = 150;
npc_type->CHA = 150;
npc_type->attack_delay = 3000;
npc_type->attack_delay = 30;
npc_type->prim_melee_type = 28;
npc_type->sec_melee_type = 28;
@@ -1987,25 +1984,13 @@ 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") { /* TODO: fix DB */attack_delay = atoi(val.c_str()) * 100; CalcBonuses(); return; }
else if(id == "attack_delay") { attack_delay = atoi(val.c_str()); 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());
// 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 == "min_hit") { min_dmg = atoi(val.c_str()); return; }
else if(id == "max_hit") { max_dmg = atoi(val.c_str()); 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; }
+3 -7
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, eSpecialAttacks special = eSpecialAttacks::None);
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 bool Attack(Mob* other, int Hand = EQEmu::inventory::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false,
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr);
bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr, int special = 0);
virtual bool HasRaid() { return false; }
virtual bool HasGroup() { return false; }
virtual Raid* GetRaid() { return 0; }
@@ -259,11 +259,9 @@ 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;}
int GetAttackDelay() const {return attack_delay;}
uint8 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;}
@@ -465,8 +463,6 @@ protected:
uint32 max_dmg;
uint32 min_dmg;
int base_damage;
int min_damage;
int32 accuracy_rating;
int32 avoidance_rating;
int16 attack_count;
+600 -398
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -3636,7 +3636,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
}
if((!IsAllianceSpellLine(spell_id) && !IsBeneficialAllowed(spelltar)) ||
if(!IsBeneficialAllowed(spelltar) ||
(IsGroupOnlySpell(spell_id) &&
!(
(pBasicGroup && ((pBasicGroup == pBasicGroupTarget) || (pBasicGroup == pBasicGroupTargetPet))) || //Basic Group
+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 0; //Mob::GetMeleeMitDmg(attacker, damage, minhit, mit_rating, atk_rating);
return 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;//(2*weapon_damage*GetDamageTable(skillinuse)) / 100;
int max_hit = (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);
}
}
+3 -3
View File
@@ -280,13 +280,13 @@ public:
if (message.find("\n") != std::string::npos){
auto message_split = SplitString(message, '\n');
entity_list.MessageStatus(0, 80, Log.GetGMSayColorFromCategory(log_category), "%s", message_split[0].c_str());
entity_list.MessageStatus(0, 0, Log.GetGMSayColorFromCategory(log_category), "%s", message_split[0].c_str());
for (size_t iter = 1; iter < message_split.size(); ++iter) {
entity_list.MessageStatus(0, 80, Log.GetGMSayColorFromCategory(log_category), "--- %s", message_split[iter].c_str());
entity_list.MessageStatus(0, 0, Log.GetGMSayColorFromCategory(log_category), "--- %s", message_split[iter].c_str());
}
}
else{
entity_list.MessageStatus(0, 80, Log.GetGMSayColorFromCategory(log_category), "%s", message.c_str());
entity_list.MessageStatus(0, 0, Log.GetGMSayColorFromCategory(log_category), "%s", message.c_str());
}
}
+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]) * 100; // TODO: fix DB
temp_npctype_data->attack_delay = atoi(row[90]);
temp_npctype_data->light = (atoi(row[91]) & 0x0F);
temp_npctype_data->armtexture = atoi(row[92]);
+3 -3
View File
@@ -110,9 +110,9 @@ 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.
int attack_delay; //delay between attacks in ms
int accuracy_rating; // flat bonus before mods
int avoidance_rating; // flat bonus before mods
uint8 attack_delay; //delay between attacks in 10ths of a second
int accuracy_rating; //10 = 1% accuracy
int avoidance_rating; //10 = 1% avoidance
bool findable; //can be found with find command
bool trackable;
int16 slow_mitigation;