Merge branch 'master' into raycast

This commit is contained in:
KimLS 2014-03-01 17:49:21 -08:00
commit 629f9863ae
44 changed files with 747 additions and 385 deletions

View File

@ -1,7 +1,27 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 02/27/2014 ==
cavedude: Exported TrainDisc to Lua.
== 02/26/2014 ==
Kayen: Implemented SE_FrenziedDevestation - increase critical spell chacnce and 2x mana cost for DD spells
Kayen: Fixed SE_SpellProcChance - Now works on spell dervived procs
cavedude: Added two new NPC special_abilities. ALWAYS_FLEE, which forces the NPC to always flee ignoring FleeIfNotAlone and FLEE_PERCENT which allows you to change the HP an individual NPC will flee at. If no value is set, the rule is used as normal.
cavedude: Fixed an issue where rectangular roamboxes could cause an NPC to get stuck on a single coord.
cavedude: Added a new roambox column, mindelay allowing you to have more control over the roambox delay.
Uleat: Fix for 'sqrt' failure on vs2010 clients
image: Added idle zone timer to save CPU cycles.
Required SQL: utils/sql/git/required/2014_02_26_roambox_update.sql
== 02/24/2014 ==
cavedude: Better flee runspeed calculation. Added rule Combat:FleeMultiplier to alter this behavior.
Sorvani: Updated GetUnusedInstanceID to not recycle instance ID's unless it has reached max (65535)
== 02/23/2014 ==
Secrets: Exported the client object SendTargetCommand to Perl.
cavedude: Merchants will now keep better track of charges.
== 02/20/2014 ==
Kayen: Implemented SE_MitigateDotDamage - dot spell mitigation rune with max value

View File

@ -2516,30 +2516,53 @@ bool Database::GetUnusedInstanceID(uint16 &instance_id)
MYSQL_RES *result;
MYSQL_ROW row;
uint32 count = RuleI(Zone, ReservedInstances) + 1;
uint32 count = RuleI(Zone, ReservedInstances);
uint32 max = 65535;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM instance_list where id >= %i ORDER BY id", count), errbuf, &result)) {
if (RunQuery(query, MakeAnyLenString(&query, "SELECT IFNULL(MAX(id),%u)+1 FROM instance_list WHERE id > %u", count,count), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) != 0) {
while((row = mysql_fetch_row(result))) {
if(count < atoi(row[0])) {
instance_id = count;
mysql_free_result(result);
return true;
} else if(count > max) {
instance_id = 0;
mysql_free_result(result);
return false;
row = mysql_fetch_row(result);
mysql_free_result(result);
if(atoi(row[0]) <= max) {
count = atoi(row[0]);
} else {
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM instance_list where id > %u ORDER BY id", count), errbuf, &result)) {
safe_delete_array(query);
if (mysql_num_rows(result) != 0) {
count++;
while((row = mysql_fetch_row(result))) {
if(count < atoi(row[0])) {
instance_id = count;
mysql_free_result(result);
return true;
} else if(count > max) {
instance_id = 0;
mysql_free_result(result);
return false;
} else {
count++;
}
}
} else {
instance_id = 0;
mysql_free_result(result);
return false;
}
} else {
count++;
safe_delete_array(query);
instance_id = 0;
return false;
}
}
} else {
instance_id = 0;
mysql_free_result(result);
return false;
}
} else {
safe_delete_array(query);
instance_id = 0;
return false;
}
instance_id = count;
return true;

View File

@ -310,8 +310,8 @@ RULE_INT ( Combat, ClientBaseCritChance, 0 ) //The base crit chance for all clie
RULE_BOOL ( Combat, UseIntervalAC, true)
RULE_INT ( Combat, PetAttackMagicLevel, 30)
RULE_BOOL ( Combat, EnableFearPathing, true)
RULE_INT ( Combat, FleeHPRatio, 25)
RULE_INT ( Combat, FleeSnareHPRatio, 11) // HP at which snare will halt movement of a fleeing NPC.
RULE_REAL ( Combat, FleeMultiplier, 2.0) // Determines how quickly a NPC will slow down while fleeing. Decrease multiplier to slow NPC down quicker.
RULE_INT ( Combat, FleeHPRatio, 25) //HP % when a NPC begins to flee.
RULE_BOOL ( Combat, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it.
RULE_BOOL ( Combat, AdjustProcPerMinute, true)
RULE_REAL ( Combat, AvgProcsPerMinute, 2.0)
@ -466,6 +466,8 @@ RULE_INT ( Merchant, PriceBonusPct, 4) // Determines maximum price bonus from ha
RULE_INT ( Merchant, PricePenaltyPct, 4) // Determines maximum price penalty from having bad faction/CHA. Value is a percent.
RULE_REAL( Merchant, ChaBonusMod, 3.45) // Determines CHA cap, from 104 CHA. 3.45 is 132 CHA at apprehensive. 0.34 is 400 CHA at apprehensive.
RULE_REAL ( Merchant, ChaPenaltyMod, 1.52) // Determines CHA bottom, up to 102 CHA. 1.52 is 37 CHA at apprehensive. 0.98 is 0 CHA at apprehensive.
RULE_BOOL ( Merchant, EnableAltCurrencySell, true) // Enables the ability to resell items to alternate currency merchants
RULE_CATEGORY_END()
RULE_CATEGORY ( Bazaar )

View File

@ -359,7 +359,7 @@ typedef enum {
#define SE_DispelBeneficial 209 // implemented
//#define SE_PetShield 210 // *not implemented
#define SE_AEMelee 211 // implemented
//#define SE_CastingSkills 212 // *not implemented -Include/Exclude Casting Skill type. (*no longer used on live)
#define SE_FrenziedDevastation 212 // implemented - increase spell criticals + all DD spells cast 2x mana.
#define SE_PetMaxHP 213 // implemented[AA] - increases the maximum hit points of your pet
#define SE_MaxHPChange 214 // implemented
#define SE_PetAvoidance 215 // implemented[AA] - increases pet ability to avoid melee damage
@ -397,7 +397,7 @@ typedef enum {
#define SE_RaiseSkillCap 247 // *not implemented[AA] - adds skill over the skill cap.
//#define SE_SecondaryForte 248 // not implemented as bonus(gives you a 2nd specialize skill that can go past 50 to 100)
#define SE_SecondaryDmgInc 249 // implemented[AA] Allows off hand weapon to recieve a damage bonus (Sinister Strikes)
#define SE_SpellProcChance 250 // implemented - Increase chance to sympathetic proc by %
#define SE_SpellProcChance 250 // implemented - Increase chance to proc from melee proc spells (ie Spirit of Panther)
#define SE_ConsumeProjectile 251 // implemented[AA] - chance to not consume an arrow (ConsumeProjectile = 100)
#define SE_FrontalBackstabChance 252 // implemented[AA] - chance to perform a full damage backstab from front.
#define SE_FrontalBackstabMinDmg 253 // implemented[AA] - allow a frontal backstab for mininum damage.

View File

@ -0,0 +1,2 @@
alter table `spawngroup` add column `mindelay` int(11) not null default 15000 AFTER delay;
alter table `spawngroup` change `delay` `delay` int(11) not null default 45000;

View File

@ -0,0 +1,8 @@
-- Virulent Venom
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('888', '1', '250', '10', '0');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('889', '1', '250', '20', '0');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('890', '1', '250', '30', '0');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('891', '1', '250', '40', '0');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('892', '1', '250', '50', '0');

View File

@ -94,7 +94,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
dist2 <= spells[AIspells[i].spellid].range*spells[AIspells[i].spellid].range
)
&& (mana_cost <= GetMana() || GetMana() == GetMaxMana())
&& (AIspells[i].time_cancast+(MakeRandomInt(0, 4))) <= Timer::GetCurrentTime() //break up the spelling casting over a period of time.
&& (AIspells[i].time_cancast + (MakeRandomInt(0, 4) * 1000)) <= Timer::GetCurrentTime() //break up the spelling casting over a period of time.
) {
#if MobAI_DEBUG_Spells >= 21
@ -125,21 +125,19 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
break;
}
case SpellType_Root: {
if (
!tar->IsRooted()
&& dist2 >= 900
&& MakeRandomInt(0, 99) < 50
&& tar->DontRootMeBefore() < Timer::GetCurrentTime()
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
Mob *rootee = GetHateRandom();
if (rootee && !rootee->IsRooted() && MakeRandomInt(0, 99) < 50
&& rootee->DontRootMeBefore() < Timer::GetCurrentTime()
&& rootee->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
) {
if(!checked_los) {
if(!CheckLosFN(tar))
if(!CheckLosFN(rootee))
return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
checked_los = true;
}
uint32 tempTime = 0;
AIDoSpellCast(i, tar, mana_cost, &tempTime);
tar->SetDontRootMeBefore(tempTime);
AIDoSpellCast(i, rootee, mana_cost, &tempTime);
rootee->SetDontRootMeBefore(tempTime);
return true;
}
break;
@ -167,7 +165,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
}
case SpellType_InCombatBuff: {
if(MakeRandomInt(0,100) < 50)
if(MakeRandomInt(0, 99) < 50)
{
AIDoSpellCast(i, tar, mana_cost);
return true;
@ -184,7 +182,20 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
break;
}
case SpellType_Slow:
case SpellType_Debuff:
case SpellType_Debuff: {
Mob * debuffee = GetHateRandom();
if (debuffee && manaR >= 10 && MakeRandomInt(0, 99 < 70) &&
debuffee->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0) {
if (!checked_los) {
if (!CheckLosFN(debuffee))
return false;
checked_los = true;
}
AIDoSpellCast(i, debuffee, mana_cost);
return true;
}
break;
}
case SpellType_Nuke: {
if (
manaR >= 10 && MakeRandomInt(0, 99) < 70
@ -201,7 +212,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
break;
}
case SpellType_Dispel: {
if(MakeRandomInt(0, 100) < 15)
if(MakeRandomInt(0, 99) < 15)
{
if(!checked_los) {
if(!CheckLosFN(tar))
@ -444,6 +455,7 @@ void NPC::AI_Init() {
roambox_distance = 0;
roambox_movingto_x = 0;
roambox_movingto_y = 0;
roambox_min_delay = 2500;
roambox_delay = 2500;
}
@ -1579,14 +1591,17 @@ void NPC::AI_DoMovement() {
movey *= MakeRandomInt(0, 1) ? 1 : -1;
roambox_movingto_x = GetX() + movex;
roambox_movingto_y = GetY() + movey;
//Try to calculate new coord using distance.
if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x)
roambox_movingto_x -= movex * 2;
if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y)
roambox_movingto_y -= movey * 2;
//New coord is still invalid, ignore distance and just pick a new random coord.
//If we're here we may have a roambox where one side is shorter than the specified distance. Commons, Wkarana, etc.
if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x)
roambox_movingto_x = roambox_max_x;
roambox_movingto_x = MakeRandomFloat(roambox_min_x+1,roambox_max_x-1);
if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y)
roambox_movingto_y = roambox_max_y;
roambox_movingto_y = MakeRandomFloat(roambox_min_y+1,roambox_max_y-1);
}
mlog(AI__WAYPOINTS, "Roam Box: d=%.3f (%.3f->%.3f,%.3f->%.3f): Go To (%.3f,%.3f)",
@ -1594,7 +1609,7 @@ void NPC::AI_DoMovement() {
if (!CalculateNewPosition2(roambox_movingto_x, roambox_movingto_y, GetZ(), walksp, true))
{
roambox_movingto_x = roambox_max_x + 1; // force update
pLastFightingDelayMoving = Timer::GetCurrentTime() + RandomTimer(roambox_delay, roambox_delay + 5000);
pLastFightingDelayMoving = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay);
SetMoving(false);
SendPosition(); // makes mobs stop clientside
}
@ -1870,7 +1885,7 @@ bool NPC::AI_EngagedCastCheck() {
// try casting a heal on nearby
if (!entity_list.AICheckCloseBeneficialSpells(this, 25, MobAISpellRange, SpellType_Heal)) {
//nobody to heal, try some detrimental spells.
if(!AICastSpell(GetTarget(), 20, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm)) {
if(!AICastSpell(GetTarget(), 20, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root)) {
//no spell to cast, try again soon.
AIautocastspell_timer->Start(RandomTimer(500, 1000), false);
}

View File

@ -125,6 +125,8 @@
#define YOU_ARE_PROTECTED 424 //%1 tries to cast a spell on you, but you are protected.
#define TARGET_RESISTED 425 //Your target resisted the %1 spell.
#define YOU_RESIST 426 //You resist the %1 spell!
#define YOU_CRIT_HEAL 427 //You perform an exceptional heal! (%1)
#define YOU_CRIT_BLAST 428 //You deliver a critical blast! (%1)
#define SUMMONING_CORPSE 429 //Summoning your corpse.
#define SUMMONING_CORPSE_OTHER 430 //Summoning %1's corpse.
#define MISSING_SPELL_COMP_ITEM 433 //You are missing %1.
@ -168,6 +170,8 @@
#define OTHER_REGAIN_CAST 1033 //%1 regains concentration and continues casting.
#define GENERIC_SHOUT 1034 //%1 shouts '%2'
#define GENERIC_EMOTE 1036 //%1 %2
#define OTHER_CRIT_HEAL 1039 //%1 performs an exceptional heal! (%2)
#define OTHER_CRIT_BLAST 1040 //%1 delivers a critical blast! (%2)
#define NPC_ENRAGE_START 1042 //%1 has become ENRAGED.
#define NPC_ENRAGE_END 1043 //%1 is no longer enraged.
#define NPC_RAMPAGE 1044 //%1 goes on a RAMPAGE!
@ -222,6 +226,9 @@
#define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.'
#define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets.
#define SUSPEND_MINION_FIGHTING 3270 //Your pet must be at peace, first.
#define ALREADY_SHIELDED 3279 //Either you or your target is already being shielded.
#define START_SHIELDING 3281 //%1 begins to use %2 as a living shield!
#define END_SHIELDING 3282 //%1 ceases protecting %2.
#define TRADESKILL_MISSING_ITEM 3455 //You are missing a %1.
#define TRADESKILL_MISSING_COMPONENTS 3456 //Sorry, but you don't have everything you need for this recipe in your general inventory.
#define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1!

View File

@ -1222,7 +1222,7 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) {
if (caster->IsClient())
{
//3: At maxed ability, Total Domination has a 50% chance of preventing the charm break that otherwise would have occurred.
uint16 TotalDominationBonus = caster->aabonuses.CharmBreakChance + caster->spellbonuses.CharmBreakChance + caster->itembonuses.CharmBreakChance;
int16 TotalDominationBonus = caster->aabonuses.CharmBreakChance + caster->spellbonuses.CharmBreakChance + caster->itembonuses.CharmBreakChance;
if (MakeRandomInt(0, 99) < TotalDominationBonus)
return true;

View File

@ -2004,6 +2004,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
if(!bRiposte && other->GetHP() > 0 ) {
TryWeaponProc(nullptr, weapon, other, Hand); //no weapon
TrySpellProc(nullptr, weapon, other, Hand);
}
TriggerDefensiveProcs(nullptr, other, Hand, damage);
@ -3851,24 +3852,17 @@ void Mob::HealDamage(uint32 amount, Mob* caster) {
}
}
if(amount > (maxhp - curhp))
acthealed = (maxhp - curhp);
else
acthealed = amount;
char *TempString = nullptr;
MakeAnyLenString(&TempString, "%d", acthealed);
if(acthealed > 100){
if(caster){
Message_StringID(MT_NonMelee, YOU_HEALED, caster->GetCleanName(), TempString);
if(caster != this){
caster->Message_StringID(MT_NonMelee, YOU_HEAL, GetCleanName(), TempString);
}
}
else{
if (acthealed > 100) {
if (caster) {
Message_StringID(MT_NonMelee, YOU_HEALED, caster->GetCleanName(), itoa(acthealed));
if (caster != this)
caster->Message_StringID(MT_NonMelee, YOU_HEAL, GetCleanName(), itoa(acthealed));
} else {
Message(MT_NonMelee, "You have been healed for %d points of damage.", acthealed);
}
}
@ -3882,22 +3876,15 @@ void Mob::HealDamage(uint32 amount, Mob* caster) {
SendHPUpdate();
}
safe_delete_array(TempString);
}
//proc chance includes proc bonus
float Mob::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) {
float Mob::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand)
{
int mydex = GetDEX();
float AABonus = 0;
ProcBonus = 0;
ProcChance = 0;
float ProcChance = 0.0f;
if (aabonuses.ProcChance)
AABonus = float(aabonuses.ProcChance) / 100.0f;
switch(hand){
switch (hand) {
case 13:
weapon_speed = attack_timer.GetDuration();
break;
@ -3909,24 +3896,20 @@ float Mob::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_spe
break;
}
//calculate the weapon speed in ms, so we can use the rule to compare against.
if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance
// fast as a client can swing, so should be the floor of the proc chance
if (weapon_speed < RuleI(Combat, MinHastedDelay))
weapon_speed = RuleI(Combat, MinHastedDelay);
ProcBonus += (float(itembonuses.ProcChance + spellbonuses.ProcChance) / 1000.0f + AABonus);
if(RuleB(Combat, AdjustProcPerMinute) == true)
{
ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib) / 100.0f;
ProcChance = ProcChance + (ProcChance * ProcBonus);
}
else
{
ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy);
ProcChance = ProcChance + (ProcChance * ProcBonus);
if (RuleB(Combat, AdjustProcPerMinute)) {
ProcChance = (static_cast<float>(weapon_speed) *
RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
ProcBonus += static_cast<float>(mydex) * RuleR(Combat, ProcPerMinDexContrib);
ProcChance += ProcChance * ProcBonus / 100.0f;
} else {
ProcChance = RuleR(Combat, BaseProcChance) +
static_cast<float>(mydex) / RuleR(Combat, ProcDexDivideBy);
ProcChance += ProcChance * ProcBonus / 100.0f;
}
mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus);
@ -3950,16 +3933,6 @@ float Mob::GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 w
break;
}
/*
float PermaHaste;
if(GetHaste() > 0)
PermaHaste = 1 / (1 + (float)GetHaste()/100);
else if(GetHaste() < 0)
PermaHaste = 1 * (1 - (float)GetHaste()/100);
else
PermaHaste = 1.0f;
*/
//calculate the weapon speed in ms, so we can use the rule to compare against.
//weapon_speed = ((int)(weapon_speed*(100.0f+attack_speed)*PermaHaste));
if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance
@ -4039,159 +4012,180 @@ void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) {
}
if(!weapon_g) {
TryWeaponProc(nullptr, (const Item_Struct*)nullptr, on, hand);
TrySpellProc(nullptr, (const Item_Struct*)nullptr, on);
return;
}
if(!weapon_g->IsType(ItemClassCommon)) {
TryWeaponProc(nullptr, (const Item_Struct*) nullptr, on, hand);
TrySpellProc(nullptr, (const Item_Struct*)nullptr, on);
return;
}
//do main procs
// Innate + aug procs from weapons
// TODO: powersource procs
TryWeaponProc(weapon_g, weapon_g->GetItem(), on, hand);
// Procs from Buffs and AA both melee and range
TrySpellProc(weapon_g, weapon_g->GetItem(), on, hand);
//we have to calculate these again, oh well
int ourlevel = GetLevel();
float ProcChance, ProcBonus;
GetProcChances(ProcBonus, ProcChance, weapon_g->GetItem()->Delay, hand);
if(hand != 13)
{
ProcChance /= 2;
}
//do augment procs
int r;
for(r = 0; r < MAX_AUGMENT_SLOTS; r++) {
const ItemInst* aug_i = weapon_g->GetAugment(r);
if(!aug_i)
continue;
const Item_Struct* aug = aug_i->GetItem();
if(!aug)
continue;
if (aug->Proc.Type == ET_CombatProc) {
ProcChance = ProcChance*(100+aug->ProcRate)/100;
if (MakeRandomFloat(0, 1) < ProcChance) {
if(aug->Proc.Level > ourlevel) {
Mob * own = GetOwner();
if(own != nullptr) {
own->Message_StringID(13,PROC_PETTOOLOW);
} else {
Message_StringID(13,PROC_TOOLOW);
}
} else {
ExecWeaponProc(aug_i, aug->Proc.Effect, on);
}
}
}
}
return;
}
void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct* weapon, Mob *on, uint16 hand) {
void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand)
{
if (!weapon)
return;
uint16 skillinuse = 28;
int ourlevel = GetLevel();
float ProcChance, ProcBonus;
if(weapon!=nullptr)
GetProcChances(ProcBonus, ProcChance, weapon->Delay, hand);
else
GetProcChances(ProcBonus, ProcChance);
float ProcBonus = static_cast<float>(aabonuses.ProcChanceSPA +
spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA);
ProcBonus += static_cast<float>(itembonuses.ProcChance) / 10.0f; // Combat Effects
float ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand);
if(hand != 13) //Is Archery intended to proc at 50% rate?
if (hand != 13) //Is Archery intened to proc at 50% rate?
ProcChance /= 2;
//give weapon a chance to proc first.
if(weapon != nullptr) {
skillinuse = GetSkillByItemType(weapon->ItemType);
if (weapon->Proc.Type == ET_CombatProc) {
float WPC = ProcChance*(100.0f+(float)weapon->ProcRate)/100.0f;
if (MakeRandomFloat(0, 1) <= WPC) { // 255 dex = 0.084 chance of proc. No idea what this number should be really.
if(weapon->Proc.Level > ourlevel) {
mlog(COMBAT__PROCS, "Tried to proc (%s), but our level (%d) is lower than required (%d)", weapon->Name, ourlevel, weapon->Proc.Level);
Mob * own = GetOwner();
if(own != nullptr) {
own->Message_StringID(13,PROC_PETTOOLOW);
} else {
Message_StringID(13,PROC_TOOLOW);
}
// Try innate proc on weapon
// We can proc once here, either weapon or one aug
bool proced = false; // silly bool to prevent augs from going if weapon does
skillinuse = GetSkillByItemType(weapon->ItemType);
if (weapon->Proc.Type == ET_CombatProc) {
float WPC = ProcChance * (100.0f + // Proc chance for this weapon
static_cast<float>(weapon->ProcRate)) / 100.0f;
if (MakeRandomFloat(0, 1) <= WPC) { // 255 dex = 0.084 chance of proc. No idea what this number should be really.
if (weapon->Proc.Level > ourlevel) {
mlog(COMBAT__PROCS,
"Tried to proc (%s), but our level (%d) is lower than required (%d)",
weapon->Name, ourlevel, weapon->Proc.Level);
if (IsPet()) {
Mob *own = GetOwner();
if (own)
own->Message_StringID(13, PROC_PETTOOLOW);
} else {
mlog(COMBAT__PROCS, "Attacking weapon (%s) successfully procing spell %d (%.2f percent chance)", weapon->Name, weapon->Proc.Effect, ProcChance*100);
ExecWeaponProc(inst, weapon->Proc.Effect, on);
Message_StringID(13, PROC_TOOLOW);
}
} else {
mlog(COMBAT__PROCS, "Attacking weapon (%s) did no proc (%.2f percent chance).", weapon->Name, ProcChance*100);
mlog(COMBAT__PROCS,
"Attacking weapon (%s) successfully procing spell %d (%.2f percent chance)",
weapon->Name, weapon->Proc.Effect, WPC * 100);
ExecWeaponProc(inst, weapon->Proc.Effect, on);
proced = true;
}
}
}
if(ProcBonus == -1) {
LogFile->write(EQEMuLog::Error, "ProcBonus was -1 value!");
return;
}
if (!proced && inst) {
for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) {
const ItemInst *aug_i = inst->GetAugment(r);
if (!aug_i) // no aug, try next slot!
continue;
const Item_Struct *aug = aug_i->GetItem();
if (!aug)
continue;
bool bRangedAttack = false;
if (weapon != nullptr) {
if (weapon->ItemType == ItemTypeBow || weapon->ItemType == ItemTypeLargeThrowing || weapon->ItemType == ItemTypeSmallThrowing) {
bRangedAttack = true;
}
}
bool isRanged = false;
if(weapon)
{
if(weapon->ItemType == ItemTypeArrow ||
weapon->ItemType == ItemTypeLargeThrowing ||
weapon->ItemType == ItemTypeSmallThrowing ||
weapon->ItemType == ItemTypeBow)
{
isRanged = true;
}
}
uint32 i;
for(i = 0; i < MAX_PROCS; i++) {
if (PermaProcs[i].spellID != SPELL_UNKNOWN) {
if(MakeRandomInt(0, 100) < PermaProcs[i].chance) {
mlog(COMBAT__PROCS, "Permanent proc %d procing spell %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance);
ExecWeaponProc(nullptr, PermaProcs[i].spellID, on);
} else {
mlog(COMBAT__PROCS, "Permanent proc %d failed to proc %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance);
if (aug->Proc.Type == ET_CombatProc) {
float APC = ProcChance * (100.0f + // Proc chance for this aug
static_cast<float>(aug->ProcRate)) / 100.0f;
if (MakeRandomFloat(0, 1) <= APC) {
if (aug->Proc.Level > ourlevel) {
if (IsPet()) {
Mob *own = GetOwner();
if (own)
own->Message_StringID(13, PROC_PETTOOLOW);
} else {
Message_StringID(13, PROC_TOOLOW);
}
} else {
ExecWeaponProc(aug_i, aug->Proc.Effect, on);
break;
}
}
}
}
}
// TODO: Powersource procs
if (HasSkillProcs())
TrySkillProc(on, skillinuse, ProcChance);
if(!isRanged)
{
if(IsPet() && hand != 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets)
{
//Maybe implement this later if pets are ever given dual procs?
return;
}
void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand)
{
float ProcBonus = static_cast<float>(spellbonuses.SpellProcChance +
itembonuses.SpellProcChance + aabonuses.SpellProcChance);
float ProcChance = 0.0f;
if (weapon)
ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand);
else
ProcChance = GetProcChances(ProcBonus);
if (hand != 13) //Is Archery intened to proc at 50% rate?
ProcChance /= 2;
bool rangedattk = false;
if (weapon && hand == 11) {
if (weapon->ItemType == ItemTypeArrow ||
weapon->ItemType == ItemTypeLargeThrowing ||
weapon->ItemType == ItemTypeSmallThrowing ||
weapon->ItemType == ItemTypeBow)
rangedattk = true;
}
for (uint32 i = 0; i < MAX_PROCS; i++) {
if (IsPet() && hand != 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets)
continue; // If pets ever can proc from off hand, this will need to change
// Not ranged
if (!rangedattk) {
// Perma procs (AAs)
if (PermaProcs[i].spellID != SPELL_UNKNOWN) {
if (MakeRandomInt(0, 99) < PermaProcs[i].chance) { // TODO: Do these get spell bonus?
mlog(COMBAT__PROCS,
"Permanent proc %d procing spell %d (%d percent chance)",
i, PermaProcs[i].spellID, PermaProcs[i].chance);
ExecWeaponProc(nullptr, PermaProcs[i].spellID, on);
} else {
mlog(COMBAT__PROCS,
"Permanent proc %d failed to proc %d (%d percent chance)",
i, PermaProcs[i].spellID, PermaProcs[i].chance);
}
}
else
{
int chance = ProcChance * (SpellProcs[i].chance);
if(MakeRandomInt(0, 100) < chance) {
mlog(COMBAT__PROCS, "Spell proc %d procing spell %d (%d percent chance)", i, SpellProcs[i].spellID, chance);
// Spell procs (buffs)
if (SpellProcs[i].spellID != SPELL_UNKNOWN) {
float chance = ProcChance * (SpellProcs[i].chance / 100.0f);
if (MakeRandomFloat(0, 1) <= chance) {
mlog(COMBAT__PROCS,
"Spell proc %d procing spell %d (%.2f percent chance)",
i, SpellProcs[i].spellID, chance);
ExecWeaponProc(nullptr, SpellProcs[i].spellID, on);
CheckNumHitsRemaining(11, 0, SpellProcs[i].base_spellID);
} else {
mlog(COMBAT__PROCS, "Spell proc %d failed to proc %d (%d percent chance)", i, SpellProcs[i].spellID, chance);
mlog(COMBAT__PROCS,
"Spell proc %d failed to proc %d (%.2f percent chance)",
i, SpellProcs[i].spellID, chance);
}
}
}
if (bRangedAttack) {
int chance = ProcChance * RangedProcs[i].chance;
if(MakeRandomInt(0, 100) < chance) {
mlog(COMBAT__PROCS, "Ranged proc %d procing spell %d", i, RangedProcs[i].spellID, RangedProcs[i].chance);
ExecWeaponProc(nullptr, RangedProcs[i].spellID, on);
CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID);
} else {
mlog(COMBAT__PROCS, "Ranged proc %d failed to proc %d", i, RangedProcs[i].spellID, RangedProcs[i].chance);
} else if (rangedattk) { // ranged only
// ranged spell procs (buffs)
if (RangedProcs[i].spellID != SPELL_UNKNOWN) {
float chance = ProcChance * (RangedProcs[i].chance / 100.0f);
if (MakeRandomFloat(0, 1) <= chance) {
mlog(COMBAT__PROCS,
"Ranged proc %d procing spell %d (%.2f percent chance)",
i, RangedProcs[i].spellID, chance);
ExecWeaponProc(nullptr, RangedProcs[i].spellID, on);
CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID);
} else {
mlog(COMBAT__PROCS,
"Ranged proc %d failed to proc %d (%.2f percent chance)",
i, RangedProcs[i].spellID, chance);
}
}
}
}
if (HasSkillProcs())
TrySkillProc(on, skillinuse, ProcChance);
return;
}
void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage)

View File

@ -963,7 +963,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
newbon->GiveDoubleAttack += base1;
break;
case SE_ProcChance:
newbon->ProcChance += base1;
newbon->ProcChanceSPA += base1;
break;
case SE_RiposteChance:
newbon->RiposteChance += base1;
@ -1228,6 +1228,14 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
break;
}
case SE_FrenziedDevastation:
newbon->FrenziedDevastation += base2;
break;
case SE_SpellProcChance:
newbon->SpellProcChance += base1;
break;
}
}
}
@ -1882,17 +1890,14 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_ProcChance:
{
//multiplier is to be compatible with item effects,watching for overflow too
effect_value = effect_value<3000? effect_value : 3000;
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
newbon->ProcChance += effect_value;
newbon->ProcChanceSPA += effect_value;
else if((effect_value < 0) && (newbon->DoubleAttackChance > effect_value))
newbon->ProcChance = effect_value;
else if((effect_value < 0) && (newbon->ProcChanceSPA > effect_value))
newbon->ProcChanceSPA = effect_value;
if(newbon->ProcChance < effect_value)
newbon->ProcChance = effect_value;
if(newbon->ProcChanceSPA < effect_value)
newbon->ProcChanceSPA = effect_value;
break;
}
@ -2524,6 +2529,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
newbon->DistanceRemoval = true;
break;
case SE_FrenziedDevastation:
newbon->FrenziedDevastation += spells[spell_id].base2[i];
break;
}
}
}
@ -3311,9 +3320,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
break;
case SE_ProcChance:
spellbonuses.ProcChance = effect_value;
aabonuses.ProcChance = effect_value;
itembonuses.ProcChance = effect_value;
spellbonuses.ProcChanceSPA = effect_value;
aabonuses.ProcChanceSPA = effect_value;
itembonuses.ProcChanceSPA = effect_value;
break;
case SE_ExtraAttackChance:
@ -3866,7 +3875,13 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
spellbonuses.ImprovedTaunt[0] = effect_value;
spellbonuses.ImprovedTaunt[1] = effect_value;
spellbonuses.ImprovedTaunt[2] = -1;
break;
case SE_FrenziedDevastation:
spellbonuses.FrenziedDevastation += effect_value;
aabonuses.FrenziedDevastation += effect_value;
itembonuses.FrenziedDevastation += effect_value;
break;
}
}
}

View File

@ -7705,16 +7705,11 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel
}
//proc chance includes proc bonus
float Bot::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) {
float Bot::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand) {
int mydex = GetDEX();
float AABonus = 0;
ProcBonus = 0;
ProcChance = 0;
float ProcChance = 0.0f;
if (aabonuses.ProcChance)
AABonus = float(aabonuses.ProcChance) / 100.0f;
switch(hand){
switch (hand) {
case SLOT_PRIMARY:
weapon_speed = attack_timer.GetDuration();
break;
@ -7726,24 +7721,20 @@ float Bot::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_spe
break;
}
//calculate the weapon speed in ms, so we can use the rule to compare against.
if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance
// fast as a client can swing, so should be the floor of the proc chance
if (weapon_speed < RuleI(Combat, MinHastedDelay))
weapon_speed = RuleI(Combat, MinHastedDelay);
ProcBonus += (float(itembonuses.ProcChance + spellbonuses.ProcChance) / 1000.0f + AABonus);
if(RuleB(Combat, AdjustProcPerMinute) == true)
{
ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib) / 100.0f;
ProcChance = ProcChance + (ProcChance * ProcBonus);
}
else
{
ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy);
ProcChance = ProcChance + (ProcChance * ProcBonus);
if (RuleB(Combat, AdjustProcPerMinute)) {
ProcChance = (static_cast<float>(weapon_speed) *
RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
ProcBonus += static_cast<float>(mydex) * RuleR(Combat, ProcPerMinDexContrib);
ProcChance += ProcChance * ProcBonus / 100.0f;
} else {
ProcChance = RuleR(Combat, BaseProcChance) +
static_cast<float>(mydex) / RuleR(Combat, ProcDexDivideBy);
ProcChance += ProcChance*ProcBonus / 100.0f;
}
mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus);

View File

@ -167,7 +167,7 @@ public:
uint16 BotGetSpells(int spellslot) { return AIspells[spellslot].spellid; }
uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; }
uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; }
virtual float GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand);
virtual float GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand);
virtual bool AvoidDamage(Mob* other, int32 &damage, bool CanRiposte);
virtual int GetMonkHandToHandDamage(void);
virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse);

View File

@ -2963,6 +2963,101 @@ void Client::Message_StringID(uint32 type, uint32 string_id, const char* message
safe_delete(outapp);
}
// helper function, returns true if we should see the message
bool Client::FilteredMessageCheck(Mob *sender, eqFilterType filter)
{
eqFilterMode mode = GetFilter(filter);
// easy ones first
if (mode == FilterShow)
return true;
else if (mode == FilterHide)
return false;
if (!sender && mode == FilterHide) {
return false;
} else if (sender) {
if (this == sender) {
if (mode == FilterHide) // don't need to check others
return false;
} else if (mode == FilterShowSelfOnly) { // we know sender isn't us
return false;
} else if (mode == FilterShowGroupOnly) {
Group *g = GetGroup();
if (!g || !g->IsGroupMember(sender))
return false;
}
}
// we passed our checks
return true;
}
void Client::FilteredMessage_StringID(Mob *sender, uint32 type,
eqFilterType filter, uint32 string_id)
{
if (!FilteredMessageCheck(sender, filter))
return;
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage, 12);
SimpleMessage_Struct *sms = (SimpleMessage_Struct *)outapp->pBuffer;
sms->color = type;
sms->string_id = string_id;
sms->unknown8 = 0;
QueuePacket(outapp);
safe_delete(outapp);
return;
}
void Client::FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id,
const char *message1, const char *message2, const char *message3,
const char *message4, const char *message5, const char *message6,
const char *message7, const char *message8, const char *message9)
{
if (!FilteredMessageCheck(sender, filter))
return;
int i, argcount, length;
char *bufptr;
const char *message_arg[9] = {0};
if (type == MT_Emote)
type = 4;
if (!message1) {
Message_StringID(type, string_id); // use the simple message instead
return;
}
i = 0;
message_arg[i++] = message1;
message_arg[i++] = message2;
message_arg[i++] = message3;
message_arg[i++] = message4;
message_arg[i++] = message5;
message_arg[i++] = message6;
message_arg[i++] = message7;
message_arg[i++] = message8;
message_arg[i++] = message9;
for (argcount = length = 0; message_arg[argcount]; argcount++)
length += strlen(message_arg[argcount]) + 1;
EQApplicationPacket *outapp = new EQApplicationPacket(OP_FormattedMessage, length+13);
FormattedMessage_Struct *fm = (FormattedMessage_Struct *)outapp->pBuffer;
fm->string_id = string_id;
fm->type = type;
bufptr = fm->message;
for (i = 0; i < argcount; i++) {
strcpy(bufptr, message_arg[i]);
bufptr += strlen(message_arg[i]) + 1;
}
QueuePacket(outapp);
safe_delete(outapp);
}
void Client::SetTint(int16 in_slot, uint32 color) {
Color_Struct new_color;

View File

@ -250,6 +250,14 @@ public:
uint8 SlotConvert(uint8 slot,bool bracer=false);
void Message_StringID(uint32 type, uint32 string_id, uint32 distance = 0);
void Message_StringID(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0);
bool FilteredMessageCheck(Mob *sender, eqFilterType filter);
void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id);
void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter,
uint32 string_id, const char *message1, const char *message2 = nullptr,
const char *message3 = nullptr, const char *message4 = nullptr,
const char *message5 = nullptr, const char *message6 = nullptr,
const char *message7 = nullptr, const char *message8 = nullptr,
const char *message9 = nullptr);
void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice);
void SendTraderItem(uint32 item_id,uint16 quantity);
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);

View File

@ -1615,7 +1615,8 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app)
if (shield_target)
{
entity_list.MessageClose(this,false,100,0,"%s ceases shielding %s.",GetName(),shield_target->GetName());
entity_list.MessageClose_StringID(this, false, 100, 0,
END_SHIELDING, GetName(), shield_target->GetName());
for (int y = 0; y < 2; y++)
{
if (shield_target->shielder[y].shielder_id == GetID())
@ -1640,7 +1641,8 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app)
{
if (shield_target->shielder[x].shielder_id == 0)
{
entity_list.MessageClose(this,false,100,0,"%s uses their shield to guard %s.",GetName(),shield_target->GetName());
entity_list.MessageClose_StringID(this ,false, 100, 0,
START_SHIELDING, GetName(), shield_target->GetName());
shield_target->shielder[x].shielder_id = GetID();
int shieldbonus = shield->AC*2;
switch (GetAA(197))
@ -1677,7 +1679,7 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app)
}
if (!ack)
{
Message(0, "No more than two warriors may shield the same being.");
Message_StringID(0, ALREADY_SHIELDED);
shield_target = 0;
return;
}
@ -12618,32 +12620,38 @@ void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app)
uint32 cost = 0;
uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id);
uint32 merchant_id = tar->MerchantType;
bool found = false;
std::list<MerchantList> merlist = zone->merchanttable[merchant_id];
std::list<MerchantList>::const_iterator itr;
for(itr = merlist.begin(); itr != merlist.end(); ++itr) {
MerchantList ml = *itr;
if(GetLevel() < ml.level_required) {
continue;
if (RuleB(Merchant, EnableAltCurrencySell)) {
bool found = false;
std::list<MerchantList> merlist = zone->merchanttable[merchant_id];
std::list<MerchantList>::const_iterator itr;
for (itr = merlist.begin(); itr != merlist.end(); ++itr) {
MerchantList ml = *itr;
if (GetLevel() < ml.level_required) {
continue;
}
int32 fac = tar->GetPrimaryFaction();
if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) {
continue;
}
item = database.GetItem(ml.item);
if (!item)
continue;
if (item->ID == inst->GetItem()->ID) {
cost = ml.alt_currency_cost;
found = true;
break;
}
}
int32 fac = tar->GetPrimaryFaction();
if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) {
continue;
}
item = database.GetItem(ml.item);
if(!item)
continue;
if(item->ID == inst->GetItem()->ID) {
cost = ml.alt_currency_cost;
found = true;
break;
if (!found) {
cost = 0;
}
}
if(!found) {
else {
cost = 0;
}
@ -12793,6 +12801,10 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) {
return;
}
if (!RuleB(Merchant, EnableAltCurrencySell)) {
return;
}
const Item_Struct* item = nullptr;
uint32 cost = 0;
uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id);
@ -12815,7 +12827,7 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) {
if(!item)
continue;
if(item->ID == inst->GetItem()->ID) {
if(item->ID == inst->GetItem()->ID) {
cost = ml.alt_currency_cost;
found = true;
break;

View File

@ -581,7 +581,8 @@ bool Client::Process() {
{
if (!CombatRange(shield_target))
{
entity_list.MessageClose(this,false,100,0,"%s ceases shielding %s.",GetCleanName(),shield_target->GetCleanName());
entity_list.MessageClose_StringID(this, false, 100, 0,
END_SHIELDING, GetCleanName(), shield_target->GetCleanName());
for (int y = 0; y < 2; y++)
{
if (shield_target->shielder[y].shielder_id == GetID())

View File

@ -2085,21 +2085,27 @@ void command_ai(Client *c, const Seperator *sep)
}
else if (strcasecmp(sep->arg[1], "roambox") == 0) {
if (target && target->IsAIControlled() && target->IsNPC()) {
if ((sep->argnum == 6 || sep->argnum == 7) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) {
if ((sep->argnum == 6 || sep->argnum == 7 || sep->argnum == 8) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) {
uint32 tmp = 2500;
uint32 tmp2 = 2500;
if (sep->IsNumber(7))
tmp = atoi(sep->arg[7]);
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atof(sep->arg[6]), tmp);
if (sep->IsNumber(8))
tmp2 = atoi(sep->arg[8]);
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atof(sep->arg[6]), tmp, tmp2);
}
else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) {
uint32 tmp = 2500;
uint32 tmp2 = 2500;
if (sep->IsNumber(4))
tmp = atoi(sep->arg[4]);
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp);
if (sep->IsNumber(5))
tmp2 = atoi(sep->arg[5]);
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp, tmp2);
}
else {
c->Message(0, "Usage: #ai roambox dist max_x min_x max_y min_y [delay]");
c->Message(0, "Usage: #ai roambox dist roamdist [delay]");
c->Message(0, "Usage: #ai roambox dist max_x min_x max_y min_y [delay] [mindelay]");
c->Message(0, "Usage: #ai roambox dist roamdist [delay] [mindelay]");
}
}
else

View File

@ -122,7 +122,10 @@ enum {
TETHER = 33,
DESTRUCTIBLE_OBJECT = 34,
NO_HARM_FROM_CLIENT = 35,
MAX_SPECIAL_ATTACK = 36
ALWAYS_FLEE = 36,
FLEE_PERCENT = 37,
MAX_SPECIAL_ATTACK = 38
};
typedef enum { //fear states
@ -274,6 +277,7 @@ struct StatBonuses {
int16 DamageModifier[HIGHEST_SKILL+2]; //i
int16 MinDamageModifier[HIGHEST_SKILL+2]; //i
int16 ProcChance; // ProcChance/10 == % increase i = CombatEffects
int16 ProcChanceSPA; // ProcChance from spell effects
int16 ExtraAttackChance;
int16 DoTShielding;
int16 DivineSaveChance[2]; // Second Chance (base1 = chance, base2 = spell on trigger)
@ -311,8 +315,8 @@ struct StatBonuses {
//uint16 BlockSpellEffect[EFFECT_COUNT]; // Prevents spells with certain effects from landing on you *no longer used
bool ImmuneToFlee; // Bypass the fleeing flag
uint16 VoiceGraft; // Stores the ID of the mob with which to talk through
uint16 SpellProcChance; // chance to proc from sympathetic spell effects
uint16 CharmBreakChance; // chance to break charm
int16 SpellProcChance; // chance to proc from sympathetic spell effects
int16 CharmBreakChance; // chance to break charm
int16 SongRange; // increases range of beneficial bard songs
uint16 HPToManaConvert; // Uses HP to cast spells at specific conversion
uint16 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have.
@ -335,6 +339,7 @@ struct StatBonuses {
bool DivineAura; // invulnerability
bool DistanceRemoval; // Check if Cancle if Moved effect is present
int16 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid
int16 FrenziedDevastation; // base1= AArank(used) base2= chance increase spell criticals + all DD spells 2x mana.
//bool AbsorbMagicAtt; // Magic Rune *Need to be implemented for NegateEffect
//bool MeleeRune; // Melee Rune *Need to be implemented for NegateEffect

View File

@ -62,7 +62,7 @@ int32 NPC::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
}
int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if (spells[spell_id].targettype == ST_Self)
return value;
@ -74,22 +74,24 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
// Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40)
value -= (GetLevel() - 40) * 20;
//This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch.
if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
int chance = RuleI(Spells, BaseCritChance);
chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
if (chance > 0){
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
//Improved Harm Touch is a guaranteed crit if you have at least one level of SCF.
if (spell_id == SPELL_IMP_HARM_TOUCH && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0))
chance = 100;
if (MakeRandomInt(1,100) <= chance){
Critical = true;
ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease;
@ -102,53 +104,55 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
}
ratio += RuleI(Spells, WizCritRatio); //Default is zero
if (Critical){
value = value_BaseEffect*ratio/100;
value = value_BaseEffect*ratio/100;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
if (target) {
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
value -= target->GetFcDamageAmtIncoming(this, spell_id);
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
value -= target->GetFcDamageAmtIncoming(this, spell_id);
}
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100;
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100;
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100;
entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s delivers a critical blast! (%d)", GetName(), -value);
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits,
OTHER_CRIT_BLAST, GetName(), itoa(-value));
Message_StringID(MT_SpellCrits, YOU_CRIT_BLAST, itoa(-value));
return value;
}
}
value = value_BaseEffect;
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
value = value_BaseEffect;
if (target) {
value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100;
value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
if (target) {
value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100;
value -= target->GetFcDamageAmtIncoming(this, spell_id);
}
value -= target->GetFcDamageAmtIncoming(this, spell_id);
}
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id);
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id);
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
return value;
}
}
int32 Client::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
@ -255,7 +259,7 @@ int32 NPC::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
}
int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
if (target == nullptr)
target = this;
@ -263,54 +267,57 @@ int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
int16 chance = 0;
int8 modifier = 1;
bool Critical = false;
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100);
value = value_BaseEffect;
value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100);
value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100);
// Instant Heals
if(spells[spell_id].buffduration < 1) {
chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance;
chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance;
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
if (spellbonuses.CriticalHealDecay)
chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay);
chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay);
if(chance && (MakeRandomInt(0,99) < chance)) {
Critical = true;
modifier = 2; //At present time no critical heal amount modifier SPA exists.
}
value *= modifier;
value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier;
value += GetFocusEffect(focusFcHealAmt, spell_id);
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier;
value += GetFocusEffect(focusFcHealAmt, spell_id);
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier;
value += value*target->GetHealRate(spell_id, this)/100;
value += value*target->GetHealRate(spell_id, this)/100;
if (Critical)
entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s performs an exceptional heal! (%d)", GetName(), value);
if (Critical) {
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits,
OTHER_CRIT_HEAL, GetName(), itoa(value));
Message_StringID(MT_SpellCrits, YOU_CRIT_HEAL, itoa(value));
}
return value;
}
//Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value]
else {
chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime;
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime;
chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id);
if (spellbonuses.CriticalRegenDecay)
chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay);
if(chance && (MakeRandomInt(0,99) < chance))
return (value * 2);
}
@ -321,6 +328,12 @@ int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
int32 Client::GetActSpellCost(uint16 spell_id, int32 cost)
{
//FrenziedDevastation doubles mana cost of all DD spells
int16 FrenziedDevastation = itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
if (FrenziedDevastation && IsPureNukeSpell(spell_id))
cost *= 2;
// Formula = Unknown exact, based off a random percent chance up to mana cost(after focuses) of the cast spell
if(this->itembonuses.Clairvoyance && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
{
@ -426,7 +439,7 @@ int32 Client::GetActSpellDuration(uint16 spell_id, int32 duration)
// Only need this for clients, since the change was for bard songs, I assume we should keep non bard songs getting +1
// However if its bard or not and is mez, charm or fear, we need to add 1 so that client is in sync
if (!IsShortDurationBuff(spell_id) ||
if (!(IsShortDurationBuff(spell_id) && IsBardSong(spell_id)) ||
IsFearSpell(spell_id) ||
IsCharmSpell(spell_id) ||
IsMezSpell(spell_id) ||

View File

@ -1012,6 +1012,22 @@ Mob *EntityList::GetMobByNpcTypeID(uint32 get_id)
return nullptr;
}
bool EntityList::IsMobSpawnedByNpcTypeID(uint32 get_id)
{
if (get_id == 0 || npc_list.empty())
return false;
auto it = npc_list.begin();
while (it != npc_list.end()) {
// Mobs will have a 0 as their GetID() if they're dead
if (it->second->GetNPCTypeID() == get_id && it->second->GetID() != 0)
return true;
++it;
}
return false;
}
Object *EntityList::GetObjectByDBID(uint32 id)
{
if (id == 0 || object_list.empty())
@ -1891,6 +1907,24 @@ void EntityList::MessageClose_StringID(Mob *sender, bool skipsender, float dist,
}
}
void EntityList::FilteredMessageClose_StringID(Mob *sender, bool skipsender,
float dist, uint32 type, eqFilterType filter, uint32 string_id,
const char *message1, const char *message2, const char *message3,
const char *message4, const char *message5, const char *message6,
const char *message7, const char *message8, const char *message9)
{
Client *c;
float dist2 = dist * dist;
for (auto it = client_list.begin(); it != client_list.end(); ++it) {
c = it->second;
if (c && c->DistNoRoot(*sender) <= dist2 && (!skipsender || c != sender))
c->FilteredMessage_StringID(sender, type, filter, string_id,
message1, message2, message3, message4, message5,
message6, message7, message8, message9);
}
}
void EntityList::Message_StringID(Mob *sender, bool skipsender, uint32 type, uint32 string_id, const char* message1,const char* message2,const char* message3,const char* message4,const char* message5,const char* message6,const char* message7,const char* message8,const char* message9)
{
Client *c;
@ -1902,6 +1936,23 @@ void EntityList::Message_StringID(Mob *sender, bool skipsender, uint32 type, uin
}
}
void EntityList::FilteredMessage_StringID(Mob *sender, bool skipsender,
uint32 type, eqFilterType filter, uint32 string_id,
const char *message1, const char *message2, const char *message3,
const char *message4, const char *message5, const char *message6,
const char *message7, const char *message8, const char *message9)
{
Client *c;
for (auto it = client_list.begin(); it != client_list.end(); ++it) {
c = it->second;
if (c && (!skipsender || c != sender))
c->FilteredMessage_StringID(sender, type, filter, string_id,
message1, message2, message3, message4, message5, message6,
message7, message8, message9);
}
}
void EntityList::MessageClose(Mob* sender, bool skipsender, float dist, uint32 type, const char* message, ...)
{
va_list argptr;

View File

@ -135,6 +135,7 @@ public:
inline Mob *GetMobID(uint16 id) { return(GetMob(id)); } //for perl
Mob *GetMob(const char* name);
Mob *GetMobByNpcTypeID(uint32 get_id);
bool IsMobSpawnedByNpcTypeID(uint32 get_id);
Mob *GetTargetForVirus(Mob* spreader);
inline NPC *GetNPCByID(uint16 id)
{ return npc_list.count(id) ? npc_list.at(id) : nullptr; }
@ -266,7 +267,9 @@ public:
void MessageStatus(uint32 to_guilddbid, int to_minstatus, uint32 type, const char* message, ...);
void MessageClose(Mob* sender, bool skipsender, float dist, uint32 type, const char* message, ...);
void Message_StringID(Mob *sender, bool skipsender, uint32 type, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0);
void FilteredMessage_StringID(Mob *sender, bool skipsender, uint32 type, eqFilterType filter, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0);
void MessageClose_StringID(Mob *sender, bool skipsender, float dist, uint32 type, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0);
void FilteredMessageClose_StringID(Mob *sender, bool skipsender, float dist, uint32 type, eqFilterType filter, uint32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0);
void ChannelMessageFromWorld(const char* from, const char* to, uint8 chan_num, uint32 guilddbid, uint8 language, const char* message);
void ChannelMessage(Mob* from, uint8 chan_num, uint8 language, const char* message, ...);
void ChannelMessage(Mob* from, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...);

View File

@ -53,7 +53,10 @@ void Mob::CheckFlee() {
//see if were possibly hurt enough
float ratio = GetHPRatio();
if(ratio >= RuleI(Combat, FleeHPRatio))
float fleeratio = GetSpecialAbility(FLEE_PERCENT);
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
if(ratio >= fleeratio)
return;
//we might be hurt enough, check con now..
@ -77,25 +80,24 @@ void Mob::CheckFlee() {
switch(con) {
//these values are not 100% researched
case CON_GREEN:
run_ratio = RuleI(Combat, FleeHPRatio);
run_ratio = fleeratio;
break;
case CON_LIGHTBLUE:
run_ratio = RuleI(Combat, FleeHPRatio) * 8 / 10;
run_ratio = fleeratio * 9 / 10;
break;
case CON_BLUE:
run_ratio = RuleI(Combat, FleeHPRatio) * 6 / 10;
run_ratio = fleeratio * 8 / 10;
break;
default:
run_ratio = RuleI(Combat, FleeHPRatio) * 4 / 10;
run_ratio = fleeratio * 7 / 10;
break;
}
if(ratio < run_ratio)
{
if (RuleB(Combat, FleeIfNotAlone) ||
(!RuleB(Combat, FleeIfNotAlone) &&
(entity_list.GetHatedCount(hate_top, this) == 0)))
GetSpecialAbility(ALWAYS_FLEE) ||
(!RuleB(Combat, FleeIfNotAlone) && (entity_list.GetHatedCount(hate_top, this) == 0)))
StartFleeing();
}
}
@ -110,7 +112,9 @@ void Mob::ProcessFlee() {
}
//see if we are still dying, if so, do nothing
if(GetHPRatio() < (float)RuleI(Combat, FleeHPRatio))
float fleeratio = GetSpecialAbility(FLEE_PERCENT);
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
if(GetHPRatio() < fleeratio)
return;
//we are not dying anymore... see what we do next
@ -128,19 +132,19 @@ void Mob::ProcessFlee() {
float Mob::GetFearSpeed() {
if(flee_mode) {
//we know ratio < FLEE_HP_RATIO
float speed = GetRunspeed();
float speed = GetBaseRunspeed();
float ratio = GetHPRatio();
float multiplier = RuleR(Combat, FleeMultiplier);
// mob's movement will halt with a decent snare at HP specified by rule.
if (ratio <= RuleI(Combat, FleeSnareHPRatio) && GetSnaredAmount() > 40) {
return 0.0001f;
}
if(GetSnaredAmount() > 40)
multiplier = multiplier / 6.0f;
if (ratio < FLEE_HP_MINSPEED)
ratio = FLEE_HP_MINSPEED;
speed = speed * 0.5 * ratio / 100;
speed = speed * ratio * multiplier / 100;
//NPC will eventually stop. Snares speeds this up.
if(speed < 0.09)
speed = 0.0001f;
return(speed);
}
return(GetRunspeed());

View File

@ -560,6 +560,11 @@ void Lua_Client::UnscribeSpellAll(bool update_client) {
self->UnscribeSpellAll(update_client);
}
void Lua_Client::TrainDisc(int itemid) {
Lua_Safe_Call_Void();
self->TrainDiscipline(itemid);
}
void Lua_Client::UntrainDisc(int slot) {
Lua_Safe_Call_Void();
self->UntrainDisc(slot);
@ -1352,6 +1357,7 @@ luabind::scope lua_register_client() {
.def("UnscribeSpell", (void(Lua_Client::*)(int,bool))&Lua_Client::UnscribeSpell)
.def("UnscribeSpellAll", (void(Lua_Client::*)(void))&Lua_Client::UnscribeSpellAll)
.def("UnscribeSpellAll", (void(Lua_Client::*)(bool))&Lua_Client::UnscribeSpellAll)
.def("TrainDisc", (void(Lua_Client::*)(int))&Lua_Client::TrainDisc)
.def("UntrainDisc", (void(Lua_Client::*)(int))&Lua_Client::UntrainDisc)
.def("UntrainDisc", (void(Lua_Client::*)(int,bool))&Lua_Client::UntrainDisc)
.def("UntrainDiscAll", (void(Lua_Client::*)(void))&Lua_Client::UntrainDiscAll)

View File

@ -138,6 +138,7 @@ public:
void UnscribeSpell(int slot, bool update_client);
void UnscribeSpellAll();
void UnscribeSpellAll(bool update_client);
void TrainDisc(int itemid);
void UntrainDisc(int slot);
void UntrainDisc(int slot, bool update_client);
void UntrainDiscAll();

View File

@ -65,6 +65,11 @@ Lua_Mob Lua_EntityList::GetMobByNpcTypeID(int npc_type) {
return Lua_Mob(self->GetMobByNpcTypeID(npc_type));
}
bool Lua_EntityList::IsMobSpawnedByNpcTypeID(int npc_type) {
Lua_Safe_Call_Bool();
return self->IsMobSpawnedByNpcTypeID(npc_type);
}
Lua_NPC Lua_EntityList::GetNPCByID(int id) {
Lua_Safe_Call_Class(Lua_NPC);
return Lua_NPC(self->GetNPCByID(id));
@ -420,6 +425,7 @@ luabind::scope lua_register_entity_list() {
.def("GetMob", (Lua_Mob(Lua_EntityList::*)(const char*))&Lua_EntityList::GetMob)
.def("GetMob", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMob)
.def("GetMobByNpcTypeID", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMobByNpcTypeID)
.def("IsMobSpawnedByNpcTypeID", (bool(Lua_EntityList::*)(int))&Lua_EntityList::IsMobSpawnedByNpcTypeID)
.def("GetNPCByID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByID)
.def("GetNPCByNPCTypeID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByNPCTypeID)
.def("GetClientByName", (Lua_Client(Lua_EntityList::*)(const char*))&Lua_EntityList::GetClientByName)

View File

@ -51,6 +51,7 @@ public:
Lua_Mob GetMob(const char *name);
Lua_Mob GetMob(int id);
Lua_Mob GetMobByNpcTypeID(int npc_type);
bool IsMobSpawnedByNpcTypeID(int npc_type);
Lua_NPC GetNPCByID(int id);
Lua_NPC GetNPCByNPCTypeID(int npc_type);
Lua_Client GetClientByName(const char *name);

View File

@ -2166,7 +2166,9 @@ luabind::scope lua_register_special_abilities() {
luabind::value("leash", static_cast<int>(LEASH)),
luabind::value("tether", static_cast<int>(TETHER)),
luabind::value("destructible_object", static_cast<int>(DESTRUCTIBLE_OBJECT)),
luabind::value("no_harm_from_client", static_cast<int>(NO_HARM_FROM_CLIENT))
luabind::value("no_harm_from_client", static_cast<int>(NO_HARM_FROM_CLIENT)),
luabind::value("always_flee", static_cast<int>(ALWAYS_FLEE)),
luabind::value("flee_percent", static_cast<int>(FLEE_PERCENT))
];
}

View File

@ -297,9 +297,9 @@ void Lua_NPC::AI_SetRoambox(float dist, float max_x, float min_x, float max_y, f
self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y);
}
void Lua_NPC::AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay) {
void Lua_NPC::AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay, uint32 mindelay) {
Lua_Safe_Call_Void();
self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y, delay);
self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y, delay, mindelay);
}
int Lua_NPC::GetNPCSpellsID() {
@ -494,7 +494,7 @@ luabind::scope lua_register_npc() {
.def("SaveGuardSpot", (void(Lua_NPC::*)(bool))&Lua_NPC::SaveGuardSpot)
.def("IsGuarding", (bool(Lua_NPC::*)(void))&Lua_NPC::IsGuarding)
.def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float))&Lua_NPC::AI_SetRoambox)
.def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32))&Lua_NPC::AI_SetRoambox)
.def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32,uint32))&Lua_NPC::AI_SetRoambox)
.def("GetNPCSpellsID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCSpellsID)
.def("GetSpawnPointID", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointID)
.def("GetSpawnPointX", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointX)

View File

@ -85,7 +85,7 @@ public:
void SaveGuardSpot(bool clear);
bool IsGuarding();
void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y);
void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay);
void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay, uint32 mindelay);
int GetNPCSpellsID();
int GetSpawnPointID();
float GetSpawnPointX();

View File

@ -2824,7 +2824,8 @@ int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
value = (value * GetSpellScale() / 100);
entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s delivers a critical blast! (%d)", GetName(), -value);
entity_list.MessageClose_StringID(this, false, 100, MT_SpellCrits,
OTHER_CRIT_BLAST, GetName(), itoa(-value));
return value;
}

View File

@ -523,6 +523,13 @@ public:
virtual void Message_StringID(uint32 type, uint32 string_id, const char* message, const char* message2 = 0,
const char* message3 = 0, const char* message4 = 0, const char* message5 = 0, const char* message6 = 0,
const char* message7 = 0, const char* message8 = 0, const char* message9 = 0, uint32 distance = 0) { }
virtual void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id) { }
virtual void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter,
uint32 string_id, const char *message1, const char *message2 = nullptr,
const char *message3 = nullptr, const char *message4 = nullptr,
const char *message5 = nullptr, const char *message6 = nullptr,
const char *message7 = nullptr, const char *message8 = nullptr,
const char *message9 = nullptr) { }
void Say(const char *format, ...);
void Say_StringID(uint32 string_id, const char *message3 = 0, const char *message4 = 0, const char *message5 = 0,
const char *message6 = 0, const char *message7 = 0, const char *message8 = 0, const char *message9 = 0);
@ -968,9 +975,10 @@ protected:
bool PassLimitClass(uint32 Classes_, uint16 Class_);
void TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand = 13, int damage=0);
void TryWeaponProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = 13);
void TrySpellProc(const ItemInst* inst, const Item_Struct* weapon, Mob *on, uint16 hand = 13);
void TryWeaponProc(const ItemInst* weapon, Mob *on, uint16 hand = 13);
void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on);
virtual float GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13);
virtual float GetProcChances(float ProcBonus, uint16 weapon_speed = 30, uint16 hand = 13);
virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13);
int GetWeaponDamage(Mob *against, const Item_Struct *weapon_item);
int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = nullptr);

View File

@ -336,8 +336,10 @@ int main(int argc, char** argv) {
bool worldwasconnected = worldserver.Connected();
EQStream* eqss;
EQStreamInterface *eqsi;
Timer temp_timer(10);
temp_timer.Start();
uint8 IDLEZONEUPDATE = 200;
uint8 ZONEUPDATE = 10;
Timer zoneupdate_timer(ZONEUPDATE);
zoneupdate_timer.Start();
while(RunLoops) {
{ //profiler block to omit the sleep from times
@ -381,6 +383,13 @@ int main(int argc, char** argv) {
entity_list.AddClient(client);
}
if ( numclients < 1 && zoneupdate_timer.GetDuration() != IDLEZONEUPDATE )
zoneupdate_timer.SetTimer(IDLEZONEUPDATE);
else if ( numclients > 0 && zoneupdate_timer.GetDuration() == IDLEZONEUPDATE )
{
zoneupdate_timer.SetTimer(ZONEUPDATE);
zoneupdate_timer.Trigger();
}
//check for timeouts in other threads
timeout_manager.CheckTimeouts();
@ -394,7 +403,7 @@ int main(int argc, char** argv) {
worldwasconnected = false;
}
if (ZoneLoaded && temp_timer.Check()) {
if (ZoneLoaded && zoneupdate_timer.Check()) {
{
if(net.group_timer.Enabled() && net.group_timer.Check())
entity_list.GroupProcess();

View File

@ -216,6 +216,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
roambox_min_y = -2;
roambox_movingto_x = -2;
roambox_movingto_y = -2;
roambox_min_delay = 1000;
roambox_delay = 1000;
org_heading = heading;
p_depop = false;
@ -1526,6 +1527,12 @@ void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool rem
case 'i':
SetSpecialAbility(IMMUNE_TAUNT, remove ? 0 : 1);
break;
case 'e':
SetSpecialAbility(ALWAYS_FLEE, remove ? 0 : 1);
break;
case 'h':
SetSpecialAbility(FLEE_PERCENT, remove ? 0 : 1);
break;
default:
break;
@ -1686,7 +1693,14 @@ bool Mob::HasNPCSpecialAtk(const char* parse) {
HasAllAttacks = false;
}
break;
case 'e':
if(!GetSpecialAbility(ALWAYS_FLEE))
HasAllAttacks = false;
break;
case 'h':
if(!GetSpecialAbility(FLEE_PERCENT))
HasAllAttacks = false;
break;
default:
HasAllAttacks = false;
break;

View File

@ -265,8 +265,8 @@ public:
inline bool IsGuarding() const { return(guard_heading != 0); }
void SaveGuardSpotCharm();
void RestoreGuardSpotCharm();
void AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay = 2500);
void AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay = 2500);
void AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay = 2500, uint32 iMinDelay = 2500);
void AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay = 2500, uint32 iMinDelay = 2500);
//mercenary stuff
void LoadMercTypes();
@ -430,6 +430,7 @@ protected:
float roambox_movingto_x;
float roambox_movingto_y;
uint32 roambox_delay;
uint32 roambox_min_delay;
uint16 skills[HIGHEST_SKILL+1];
uint32 equipment[MAX_WORN_INVENTORY]; //this is an array of item IDs

View File

@ -150,6 +150,33 @@ XS(XS_EntityList_GetMobByNpcTypeID)
XSRETURN(1);
}
XS(XS_EntityList_IsMobSpawnedByNpcTypeID); /* prototype pass -Wmissing-prototypes */
XS(XS_EntityList_IsMobSpawnedByNpcTypeID)
{
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: EntityList::ValidMobByNpcTypeID(THIS, get_id)");
{
EntityList * THIS;
bool RETVAL;
uint32 get_id = (uint32)SvUV(ST(1));
if (sv_derived_from(ST(0), "EntityList")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(EntityList *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type EntityList");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->IsMobSpawnedByNpcTypeID(get_id);
ST(0) = boolSV(RETVAL);
sv_2mortal(ST(0));
}
XSRETURN(1);
}
XS(XS_EntityList_GetNPCByID); /* prototype to pass -Wmissing-prototypes */
XS(XS_EntityList_GetNPCByID)
{
@ -2127,6 +2154,7 @@ XS(boot_EntityList)
newXSproto(strcpy(buf, "GetMob"), XS_EntityList_GetMob, file, "$$");
newXSproto(strcpy(buf, "GetMobByID"), XS_EntityList_GetMobByID, file, "$$");
newXSproto(strcpy(buf, "GetMobByNpcTypeID"), XS_EntityList_GetMobByNpcTypeID, file, "$$");
newXSproto(strcpy(buf, "IsMobSpawnedByNpcTypeID"), XS_EntityList_IsMobSpawnedByNpcTypeID, file, "$$");
newXSproto(strcpy(buf, "GetNPCByID"), XS_EntityList_GetNPCByID, file, "$$");
newXSproto(strcpy(buf, "GetNPCByNPCTypeID"), XS_EntityList_GetNPCByNPCTypeID, file, "$$");
newXSproto(strcpy(buf, "GetClientByName"), XS_EntityList_GetClientByName, file, "$$");

View File

@ -1433,8 +1433,8 @@ XS(XS_NPC_AI_SetRoambox); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_AI_SetRoambox)
{
dXSARGS;
if (items < 6 || items > 7)
Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay= 2500)");
if (items < 6 || items > 8)
Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay= 2500, iMinDelay= 2500)");
{
NPC * THIS;
float iDist = (float)SvNV(ST(1));
@ -1443,6 +1443,7 @@ XS(XS_NPC_AI_SetRoambox)
float iMaxY = (float)SvNV(ST(4));
float iMinY = (float)SvNV(ST(5));
uint32 iDelay;
uint32 iMinDelay;
if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
@ -1453,13 +1454,20 @@ XS(XS_NPC_AI_SetRoambox)
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items < 7)
if (items < 7){
iMinDelay = 2500;
iDelay = 2500;
else {
}
else if (items < 8){
iMinDelay = 2500;
iDelay = (uint32)SvUV(ST(6));
}
else {
iDelay = (uint32)SvUV(ST(6));
iMinDelay = (uint32)SvUV(ST(7));
}
THIS->AI_SetRoambox(iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay);
THIS->AI_SetRoambox(iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay, iMinDelay);
}
XSRETURN_EMPTY;
}
@ -2208,7 +2216,7 @@ XS(boot_NPC)
newXSproto(strcpy(buf, "NextGuardPosition"), XS_NPC_NextGuardPosition, file, "$");
newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$;$");
newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, file, "$");
newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$");
newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$$");
newXSproto(strcpy(buf, "GetNPCSpellsID"), XS_NPC_GetNPCSpellsID, file, "$");
newXSproto(strcpy(buf, "GetSpawnPointID"), XS_NPC_GetSpawnPointID, file, "$");
newXSproto(strcpy(buf, "GetSpawnPointX"), XS_NPC_GetSpawnPointX, file, "$");

View File

@ -347,8 +347,8 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id)
entity_list.AddNPC(npc);
entity_list.LimitAddNPC(npc);
if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay)
npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay);
if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay && sg->min_delay)
npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay,sg->min_delay);
if(zone->InstantGrids())
{
found_spawn->LoadGrid();

View File

@ -229,8 +229,8 @@ bool Spawn2::Process() {
entity_list.AddNPC(npc);
//this limit add must be done after the AddNPC since we need the entity ID.
entity_list.LimitAddNPC(npc);
if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay)
npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay);
if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay && sg->min_delay)
npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay,sg->min_delay);
if(zone->InstantGrids()) {
_log(SPAWNS__MAIN, "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f).", spawn2_id, spawngroup_id_, npc->GetName(), npcid, x, y, z);
LoadGrid();

View File

@ -35,7 +35,7 @@ SpawnEntry::SpawnEntry( uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_lim
npc_spawn_limit = in_npc_spawn_limit;
}
SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in ) {
SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in, int min_delay_in ) {
id = in_id;
strn0cpy( name_, name, 120);
group_spawn_limit = in_group_spawn_limit;
@ -44,6 +44,7 @@ SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, floa
roambox[2]=maxy;
roambox[3]=miny;
roamdist=dist;
min_delay=min_delay_in;
delay=delay_in;
despawn=despawn_in;
despawn_timer=despawn_timer_in;
@ -150,11 +151,11 @@ bool ZoneDatabase::LoadSpawnGroups(const char* zone_name, uint16 version, SpawnG
// CODER new spawn code
query = 0;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer FROM spawn2,spawngroup WHERE spawn2.spawngroupID=spawngroup.ID and spawn2.version=%u and zone='%s'", version, zone_name), errbuf, &result))
if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawn2,spawngroup WHERE spawn2.spawngroupID=spawngroup.ID and spawn2.version=%u and zone='%s'", version, zone_name), errbuf, &result))
{
safe_delete_array(query);
while((row = mysql_fetch_row(result))) {
SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]));
SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11]));
spawn_group_list->AddSpawnGroup(newSpawnGroup);
}
mysql_free_result(result);
@ -205,11 +206,11 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_g
// CODER new spawn code
query = 0;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT spawngroup.id, spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer FROM spawngroup WHERE spawngroup.ID='%i'", spawngroupid), errbuf, &result))
if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT spawngroup.id, spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawngroup WHERE spawngroup.ID='%i'", spawngroupid), errbuf, &result))
{
safe_delete_array(query);
while((row = mysql_fetch_row(result))) {
SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]));
SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11]));
spawn_group_list->AddSpawnGroup(newSpawnGroup);
}
mysql_free_result(result);

View File

@ -39,13 +39,14 @@ public:
class SpawnGroup
{
public:
SpawnGroup(uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in );
SpawnGroup(uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in, int min_delay_in );
~SpawnGroup();
uint32 GetNPCType();
void AddSpawnEntry( SpawnEntry* newEntry );
uint32 id;
float roamdist;
float roambox[4];
int min_delay;
int delay;
int despawn;
uint32 despawn_timer;

View File

@ -2826,6 +2826,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_CriticalMend:
case SE_LimitCastTimeMax:
case SE_TriggerOnReqCaster:
case SE_FrenziedDevastation:
{
break;
}
@ -3418,13 +3419,11 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
{
if (spellbonuses.DistanceRemoval){
int distance = sqrt(
((int(GetX()) - buffs[slot].caston_x) * (int(GetX()) - buffs[slot].caston_x)) +
((int(GetY()) - buffs[slot].caston_y) * (int(GetY()) - buffs[slot].caston_y)) +
((int(GetZ()) - buffs[slot].caston_z) * (int(GetZ()) - buffs[slot].caston_z))
);
int distance = ((int(GetX()) - buffs[slot].caston_x) * (int(GetX()) - buffs[slot].caston_x)) +
((int(GetY()) - buffs[slot].caston_y) * (int(GetY()) - buffs[slot].caston_y)) +
((int(GetZ()) - buffs[slot].caston_z) * (int(GetZ()) - buffs[slot].caston_z));
if (distance > spells[spell_id].base[i]){
if (distance > (spells[spell_id].base[i] * spells[spell_id].base[i])){
if(!TryFadeEffect(slot))
BuffFadeBySlot(slot , true);
@ -5396,14 +5395,12 @@ bool Mob::AffectedBySpellExcludingSlot(int slot, int effect)
float Mob::GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod) {
ProcBonus = spellbonuses.SpellProcChance + itembonuses.SpellProcChance;
ProcChance = 0;
if(cast_time > 0)
{
ProcChance = ((float)cast_time * RuleR(Casting, AvgSpellProcsPerMinute) / 60000.0f);
ProcChance = ProcChance * (float)(ProcRateMod/100);
ProcChance = ProcChance+(ProcChance*ProcBonus/100);
}
return ProcChance;
}

View File

@ -445,7 +445,8 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
casting_spell_resist_adjust = resist_adjust;
mlog(SPELLS__CASTING, "Spell %d: Casting time %d (orig %d), mana cost %d", orgcasttime, cast_time, mana_cost);
mlog(SPELLS__CASTING, "Spell %d: Casting time %d (orig %d), mana cost %d",
spell_id, cast_time, orgcasttime, mana_cost);
// cast time is 0, just finish it right now and be done with it
if(cast_time == 0) {
@ -461,8 +462,9 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
if (IsAIControlled())
{
SetRunAnimSpeed(0);
if(this != pMob)
this->FaceTarget(pMob);
pMob = entity_list.GetMob(target_id);
if (pMob && this != pMob)
FaceTarget(pMob);
}
// if we got here we didn't fizzle, and are starting our cast

View File

@ -47,11 +47,11 @@ static inline float ABS(float x) {
return(x);
}
void NPC::AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay) {
AI_SetRoambox(iDist, GetX()+iRoamDist, GetX()-iRoamDist, GetY()+iRoamDist, GetY()-iRoamDist, iDelay);
void NPC::AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay, uint32 iMinDelay) {
AI_SetRoambox(iDist, GetX()+iRoamDist, GetX()-iRoamDist, GetY()+iRoamDist, GetY()-iRoamDist, iDelay, iMinDelay);
}
void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay) {
void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay, uint32 iMinDelay) {
roambox_distance = iDist;
roambox_max_x = iMaxX;
roambox_min_x = iMinX;
@ -59,6 +59,7 @@ void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, floa
roambox_min_y = iMinY;
roambox_movingto_x = roambox_max_x + 1; // this will trigger a recalc
roambox_delay = iDelay;
roambox_min_delay = iMinDelay;
}
void NPC::DisplayWaypointInfo(Client *c) {