mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-23 04:52:29 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 37e66b5c01 | |||
| bf798c0561 | |||
| 6ec577f241 | |||
| 672e6766c7 | |||
| b3eb11d068 | |||
| 55c61ddc78 | |||
| c131eca57f | |||
| a6dbba5380 | |||
| d2f1a3097b | |||
| 6c522e0e76 | |||
| cba068bfe8 | |||
| 28c8af96e3 | |||
| c684709712 | |||
| 225b3fffbd | |||
| 0b790342e7 | |||
| 690621268b | |||
| 725d080cac | |||
| a3ffa7e262 | |||
| 344682e6ff | |||
| c927fec803 | |||
| 3b23477139 | |||
| ea49422ff7 | |||
| 9ccd9b80bb |
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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));
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
@@ -1,3 +0,0 @@
|
||||
.input-row {
|
||||
padding: 8px;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user