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) 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 == == 02/23/2014 ==
Secrets: Exported the client object SendTargetCommand to Perl. Secrets: Exported the client object SendTargetCommand to Perl.
cavedude: Merchants will now keep better track of charges.
== 02/20/2014 == == 02/20/2014 ==
Kayen: Implemented SE_MitigateDotDamage - dot spell mitigation rune with max value Kayen: Implemented SE_MitigateDotDamage - dot spell mitigation rune with max value

View File

@ -2516,12 +2516,20 @@ bool Database::GetUnusedInstanceID(uint16 &instance_id)
MYSQL_RES *result; MYSQL_RES *result;
MYSQL_ROW row; MYSQL_ROW row;
uint32 count = RuleI(Zone, ReservedInstances) + 1; uint32 count = RuleI(Zone, ReservedInstances);
uint32 max = 65535; uint32 max = 65535;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT IFNULL(MAX(id),%u)+1 FROM instance_list WHERE id > %u", count,count), errbuf, &result)) {
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM instance_list where id >= %i ORDER BY id", count), errbuf, &result)) {
safe_delete_array(query); safe_delete_array(query);
if (mysql_num_rows(result) != 0) { if (mysql_num_rows(result) != 0) {
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))) { while((row = mysql_fetch_row(result))) {
if(count < atoi(row[0])) { if(count < atoi(row[0])) {
instance_id = count; instance_id = count;
@ -2536,10 +2544,25 @@ bool Database::GetUnusedInstanceID(uint16 &instance_id)
} }
} }
} else { } else {
instance_id = 0;
mysql_free_result(result); mysql_free_result(result);
return false;
} }
} else { } else {
safe_delete_array(query); 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; instance_id = count;
return true; 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_BOOL ( Combat, UseIntervalAC, true)
RULE_INT ( Combat, PetAttackMagicLevel, 30) RULE_INT ( Combat, PetAttackMagicLevel, 30)
RULE_BOOL ( Combat, EnableFearPathing, true) RULE_BOOL ( Combat, EnableFearPathing, true)
RULE_INT ( Combat, FleeHPRatio, 25) 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, FleeSnareHPRatio, 11) // HP at which snare will halt movement of a fleeing NPC. 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, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it.
RULE_BOOL ( Combat, AdjustProcPerMinute, true) RULE_BOOL ( Combat, AdjustProcPerMinute, true)
RULE_REAL ( Combat, AvgProcsPerMinute, 2.0) 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_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, 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_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_END()
RULE_CATEGORY ( Bazaar ) RULE_CATEGORY ( Bazaar )

View File

@ -359,7 +359,7 @@ typedef enum {
#define SE_DispelBeneficial 209 // implemented #define SE_DispelBeneficial 209 // implemented
//#define SE_PetShield 210 // *not implemented //#define SE_PetShield 210 // *not implemented
#define SE_AEMelee 211 // 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_PetMaxHP 213 // implemented[AA] - increases the maximum hit points of your pet
#define SE_MaxHPChange 214 // implemented #define SE_MaxHPChange 214 // implemented
#define SE_PetAvoidance 215 // implemented[AA] - increases pet ability to avoid melee damage #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_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_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_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_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_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. #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 dist2 <= spells[AIspells[i].spellid].range*spells[AIspells[i].spellid].range
) )
&& (mana_cost <= GetMana() || GetMana() == GetMaxMana()) && (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 #if MobAI_DEBUG_Spells >= 21
@ -125,21 +125,19 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
break; break;
} }
case SpellType_Root: { case SpellType_Root: {
if ( Mob *rootee = GetHateRandom();
!tar->IsRooted() if (rootee && !rootee->IsRooted() && MakeRandomInt(0, 99) < 50
&& dist2 >= 900 && rootee->DontRootMeBefore() < Timer::GetCurrentTime()
&& MakeRandomInt(0, 99) < 50 && rootee->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
&& tar->DontRootMeBefore() < Timer::GetCurrentTime()
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
) { ) {
if(!checked_los) { 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 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; checked_los = true;
} }
uint32 tempTime = 0; uint32 tempTime = 0;
AIDoSpellCast(i, tar, mana_cost, &tempTime); AIDoSpellCast(i, rootee, mana_cost, &tempTime);
tar->SetDontRootMeBefore(tempTime); rootee->SetDontRootMeBefore(tempTime);
return true; return true;
} }
break; break;
@ -167,7 +165,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
} }
case SpellType_InCombatBuff: { case SpellType_InCombatBuff: {
if(MakeRandomInt(0,100) < 50) if(MakeRandomInt(0, 99) < 50)
{ {
AIDoSpellCast(i, tar, mana_cost); AIDoSpellCast(i, tar, mana_cost);
return true; return true;
@ -184,7 +182,20 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
break; break;
} }
case SpellType_Slow: 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: { case SpellType_Nuke: {
if ( if (
manaR >= 10 && MakeRandomInt(0, 99) < 70 manaR >= 10 && MakeRandomInt(0, 99) < 70
@ -201,7 +212,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
break; break;
} }
case SpellType_Dispel: { case SpellType_Dispel: {
if(MakeRandomInt(0, 100) < 15) if(MakeRandomInt(0, 99) < 15)
{ {
if(!checked_los) { if(!checked_los) {
if(!CheckLosFN(tar)) if(!CheckLosFN(tar))
@ -444,6 +455,7 @@ void NPC::AI_Init() {
roambox_distance = 0; roambox_distance = 0;
roambox_movingto_x = 0; roambox_movingto_x = 0;
roambox_movingto_y = 0; roambox_movingto_y = 0;
roambox_min_delay = 2500;
roambox_delay = 2500; roambox_delay = 2500;
} }
@ -1579,14 +1591,17 @@ void NPC::AI_DoMovement() {
movey *= MakeRandomInt(0, 1) ? 1 : -1; movey *= MakeRandomInt(0, 1) ? 1 : -1;
roambox_movingto_x = GetX() + movex; roambox_movingto_x = GetX() + movex;
roambox_movingto_y = GetY() + movey; 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) if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x)
roambox_movingto_x -= movex * 2; roambox_movingto_x -= movex * 2;
if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y) if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y)
roambox_movingto_y -= movey * 2; 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) 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) 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)", 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)) if (!CalculateNewPosition2(roambox_movingto_x, roambox_movingto_y, GetZ(), walksp, true))
{ {
roambox_movingto_x = roambox_max_x + 1; // force update 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); SetMoving(false);
SendPosition(); // makes mobs stop clientside SendPosition(); // makes mobs stop clientside
} }
@ -1870,7 +1885,7 @@ bool NPC::AI_EngagedCastCheck() {
// try casting a heal on nearby // try casting a heal on nearby
if (!entity_list.AICheckCloseBeneficialSpells(this, 25, MobAISpellRange, SpellType_Heal)) { if (!entity_list.AICheckCloseBeneficialSpells(this, 25, MobAISpellRange, SpellType_Heal)) {
//nobody to heal, try some detrimental spells. //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. //no spell to cast, try again soon.
AIautocastspell_timer->Start(RandomTimer(500, 1000), false); 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 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 TARGET_RESISTED 425 //Your target resisted the %1 spell.
#define YOU_RESIST 426 //You resist 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 429 //Summoning your corpse.
#define SUMMONING_CORPSE_OTHER 430 //Summoning %1's corpse. #define SUMMONING_CORPSE_OTHER 430 //Summoning %1's corpse.
#define MISSING_SPELL_COMP_ITEM 433 //You are missing %1. #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 OTHER_REGAIN_CAST 1033 //%1 regains concentration and continues casting.
#define GENERIC_SHOUT 1034 //%1 shouts '%2' #define GENERIC_SHOUT 1034 //%1 shouts '%2'
#define GENERIC_EMOTE 1036 //%1 %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_START 1042 //%1 has become ENRAGED.
#define NPC_ENRAGE_END 1043 //%1 is no longer enraged. #define NPC_ENRAGE_END 1043 //%1 is no longer enraged.
#define NPC_RAMPAGE 1044 //%1 goes on a RAMPAGE! #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 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 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 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_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_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! #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()) if (caster->IsClient())
{ {
//3: At maxed ability, Total Domination has a 50% chance of preventing the charm break that otherwise would have occurred. //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) if (MakeRandomInt(0, 99) < TotalDominationBonus)
return true; 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 ) { if(!bRiposte && other->GetHP() > 0 ) {
TryWeaponProc(nullptr, weapon, other, Hand); //no weapon TryWeaponProc(nullptr, weapon, other, Hand); //no weapon
TrySpellProc(nullptr, weapon, other, Hand);
} }
TriggerDefensiveProcs(nullptr, other, Hand, damage); TriggerDefensiveProcs(nullptr, other, Hand, damage);
@ -3851,24 +3852,17 @@ void Mob::HealDamage(uint32 amount, Mob* caster) {
} }
} }
if(amount > (maxhp - curhp)) if(amount > (maxhp - curhp))
acthealed = (maxhp - curhp); acthealed = (maxhp - curhp);
else else
acthealed = amount; acthealed = amount;
char *TempString = nullptr; if (acthealed > 100) {
if (caster) {
MakeAnyLenString(&TempString, "%d", acthealed); Message_StringID(MT_NonMelee, YOU_HEALED, caster->GetCleanName(), itoa(acthealed));
if (caster != this)
if(acthealed > 100){ caster->Message_StringID(MT_NonMelee, YOU_HEAL, GetCleanName(), itoa(acthealed));
if(caster){ } else {
Message_StringID(MT_NonMelee, YOU_HEALED, caster->GetCleanName(), TempString);
if(caster != this){
caster->Message_StringID(MT_NonMelee, YOU_HEAL, GetCleanName(), TempString);
}
}
else{
Message(MT_NonMelee, "You have been healed for %d points of damage.", acthealed); 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(); SendHPUpdate();
} }
safe_delete_array(TempString);
} }
//proc chance includes proc bonus //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(); int mydex = GetDEX();
float AABonus = 0; float ProcChance = 0.0f;
ProcBonus = 0;
ProcChance = 0;
if (aabonuses.ProcChance) switch (hand) {
AABonus = float(aabonuses.ProcChance) / 100.0f;
switch(hand){
case 13: case 13:
weapon_speed = attack_timer.GetDuration(); weapon_speed = attack_timer.GetDuration();
break; break;
@ -3909,24 +3896,20 @@ float Mob::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_spe
break; break;
} }
//calculate the weapon speed in ms, so we can use the rule to compare against. //calculate the weapon speed in ms, so we can use the rule to compare against.
// fast as a client can swing, so should be the floor of the proc chance
if(weapon_speed < RuleI(Combat, MinHastedDelay)) // 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); weapon_speed = RuleI(Combat, MinHastedDelay);
ProcBonus += (float(itembonuses.ProcChance + spellbonuses.ProcChance) / 1000.0f + AABonus); if (RuleB(Combat, AdjustProcPerMinute)) {
ProcChance = (static_cast<float>(weapon_speed) *
if(RuleB(Combat, AdjustProcPerMinute) == true) RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
{ ProcBonus += static_cast<float>(mydex) * RuleR(Combat, ProcPerMinDexContrib);
ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms ProcChance += ProcChance * ProcBonus / 100.0f;
ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib) / 100.0f; } else {
ProcChance = ProcChance + (ProcChance * ProcBonus); ProcChance = RuleR(Combat, BaseProcChance) +
} static_cast<float>(mydex) / RuleR(Combat, ProcDexDivideBy);
else ProcChance += ProcChance * ProcBonus / 100.0f;
{
ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy);
ProcChance = ProcChance + (ProcChance * ProcBonus);
} }
mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); 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; 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. //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)); //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 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) { if(!weapon_g) {
TryWeaponProc(nullptr, (const Item_Struct*)nullptr, on, hand); TrySpellProc(nullptr, (const Item_Struct*)nullptr, on);
return; return;
} }
if(!weapon_g->IsType(ItemClassCommon)) { if(!weapon_g->IsType(ItemClassCommon)) {
TryWeaponProc(nullptr, (const Item_Struct*) nullptr, on, hand); TrySpellProc(nullptr, (const Item_Struct*)nullptr, on);
return; return;
} }
//do main procs // Innate + aug procs from weapons
// TODO: powersource procs
TryWeaponProc(weapon_g, weapon_g->GetItem(), on, hand); 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);
return;
}
//we have to calculate these again, oh well void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand)
{
if (!weapon)
return;
uint16 skillinuse = 28;
int ourlevel = GetLevel(); int ourlevel = GetLevel();
float ProcChance, ProcBonus; float ProcBonus = static_cast<float>(aabonuses.ProcChanceSPA +
GetProcChances(ProcBonus, ProcChance, weapon_g->GetItem()->Delay, hand); spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA);
if(hand != 13) ProcBonus += static_cast<float>(itembonuses.ProcChance) / 10.0f; // Combat Effects
{ float ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand);
if (hand != 13) //Is Archery intened to proc at 50% rate?
ProcChance /= 2; ProcChance /= 2;
// 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 {
Message_StringID(13, PROC_TOOLOW);
}
} else {
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;
}
}
} }
//do augment procs if (!proced && inst) {
int r; for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) {
for(r = 0; r < MAX_AUGMENT_SLOTS; r++) { const ItemInst *aug_i = inst->GetAugment(r);
const ItemInst* aug_i = weapon_g->GetAugment(r); if (!aug_i) // no aug, try next slot!
if(!aug_i)
continue; continue;
const Item_Struct* aug = aug_i->GetItem(); const Item_Struct *aug = aug_i->GetItem();
if(!aug) if (!aug)
continue; continue;
if (aug->Proc.Type == ET_CombatProc) { if (aug->Proc.Type == ET_CombatProc) {
ProcChance = ProcChance*(100+aug->ProcRate)/100; float APC = ProcChance * (100.0f + // Proc chance for this aug
if (MakeRandomFloat(0, 1) < ProcChance) { static_cast<float>(aug->ProcRate)) / 100.0f;
if(aug->Proc.Level > ourlevel) { if (MakeRandomFloat(0, 1) <= APC) {
Mob * own = GetOwner(); if (aug->Proc.Level > ourlevel) {
if(own != nullptr) { if (IsPet()) {
own->Message_StringID(13,PROC_PETTOOLOW); Mob *own = GetOwner();
if (own)
own->Message_StringID(13, PROC_PETTOOLOW);
} else { } else {
Message_StringID(13,PROC_TOOLOW); Message_StringID(13, PROC_TOOLOW);
} }
} else { } else {
ExecWeaponProc(aug_i, aug->Proc.Effect, on); ExecWeaponProc(aug_i, aug->Proc.Effect, on);
break;
} }
} }
} }
} }
}
// TODO: Powersource procs
if (HasSkillProcs())
TrySkillProc(on, skillinuse, ProcChance);
return;
} }
void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct* weapon, Mob *on, uint16 hand) { void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand)
uint16 skillinuse = 28; {
int ourlevel = GetLevel(); float ProcBonus = static_cast<float>(spellbonuses.SpellProcChance +
float ProcChance, ProcBonus; itembonuses.SpellProcChance + aabonuses.SpellProcChance);
if(weapon!=nullptr) float ProcChance = 0.0f;
GetProcChances(ProcBonus, ProcChance, weapon->Delay, hand); if (weapon)
ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand);
else else
GetProcChances(ProcBonus, ProcChance); ProcChance = GetProcChances(ProcBonus);
if(hand != 13) //Is Archery intended to proc at 50% rate? if (hand != 13) //Is Archery intened to proc at 50% rate?
ProcChance /= 2; ProcChance /= 2;
//give weapon a chance to proc first. bool rangedattk = false;
if(weapon != nullptr) { if (weapon && hand == 11) {
skillinuse = GetSkillByItemType(weapon->ItemType); if (weapon->ItemType == ItemTypeArrow ||
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);
}
} 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);
}
} else {
mlog(COMBAT__PROCS, "Attacking weapon (%s) did no proc (%.2f percent chance).", weapon->Name, ProcChance*100);
}
}
}
if(ProcBonus == -1) {
LogFile->write(EQEMuLog::Error, "ProcBonus was -1 value!");
return;
}
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 == ItemTypeLargeThrowing ||
weapon->ItemType == ItemTypeSmallThrowing || weapon->ItemType == ItemTypeSmallThrowing ||
weapon->ItemType == ItemTypeBow) weapon->ItemType == ItemTypeBow)
{ rangedattk = true;
isRanged = true;
}
} }
uint32 i; for (uint32 i = 0; i < MAX_PROCS; i++) {
for(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 (PermaProcs[i].spellID != SPELL_UNKNOWN) {
if(MakeRandomInt(0, 100) < PermaProcs[i].chance) { 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); 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); ExecWeaponProc(nullptr, PermaProcs[i].spellID, on);
} else { } else {
mlog(COMBAT__PROCS, "Permanent proc %d failed to proc %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance); mlog(COMBAT__PROCS,
"Permanent proc %d failed to proc %d (%d percent chance)",
i, PermaProcs[i].spellID, PermaProcs[i].chance);
} }
} }
if(!isRanged) // Spell procs (buffs)
{ if (SpellProcs[i].spellID != SPELL_UNKNOWN) {
if(IsPet() && hand != 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets) float chance = ProcChance * (SpellProcs[i].chance / 100.0f);
{ if (MakeRandomFloat(0, 1) <= chance) {
//Maybe implement this later if pets are ever given dual procs? mlog(COMBAT__PROCS,
} "Spell proc %d procing spell %d (%.2f percent chance)",
else i, SpellProcs[i].spellID, chance);
{
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);
ExecWeaponProc(nullptr, SpellProcs[i].spellID, on); ExecWeaponProc(nullptr, SpellProcs[i].spellID, on);
CheckNumHitsRemaining(11, 0, SpellProcs[i].base_spellID); CheckNumHitsRemaining(11, 0, SpellProcs[i].base_spellID);
} else { } 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);
} }
} }
} } else if (rangedattk) { // ranged only
if (bRangedAttack) { // ranged spell procs (buffs)
int chance = ProcChance * RangedProcs[i].chance; if (RangedProcs[i].spellID != SPELL_UNKNOWN) {
if(MakeRandomInt(0, 100) < chance) { float chance = ProcChance * (RangedProcs[i].chance / 100.0f);
mlog(COMBAT__PROCS, "Ranged proc %d procing spell %d", i, RangedProcs[i].spellID, RangedProcs[i].chance); 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); ExecWeaponProc(nullptr, RangedProcs[i].spellID, on);
CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID); CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID);
} else { } else {
mlog(COMBAT__PROCS, "Ranged proc %d failed to proc %d", i, RangedProcs[i].spellID, RangedProcs[i].chance); mlog(COMBAT__PROCS,
"Ranged proc %d failed to proc %d (%.2f percent chance)",
i, RangedProcs[i].spellID, chance);
}
} }
} }
} }
if (HasSkillProcs()) return;
TrySkillProc(on, skillinuse, ProcChance);
} }
void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage) 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; newbon->GiveDoubleAttack += base1;
break; break;
case SE_ProcChance: case SE_ProcChance:
newbon->ProcChance += base1; newbon->ProcChanceSPA += base1;
break; break;
case SE_RiposteChance: case SE_RiposteChance:
newbon->RiposteChance += base1; newbon->RiposteChance += base1;
@ -1228,6 +1228,14 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
break; 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: 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) if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
newbon->ProcChance += effect_value; newbon->ProcChanceSPA += effect_value;
else if((effect_value < 0) && (newbon->DoubleAttackChance > effect_value)) else if((effect_value < 0) && (newbon->ProcChanceSPA > effect_value))
newbon->ProcChance = effect_value; newbon->ProcChanceSPA = effect_value;
if(newbon->ProcChance < effect_value) if(newbon->ProcChanceSPA < effect_value)
newbon->ProcChance = effect_value; newbon->ProcChanceSPA = effect_value;
break; break;
} }
@ -2524,6 +2529,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
newbon->DistanceRemoval = true; newbon->DistanceRemoval = true;
break; break;
case SE_FrenziedDevastation:
newbon->FrenziedDevastation += spells[spell_id].base2[i];
break;
} }
} }
} }
@ -3311,9 +3320,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
break; break;
case SE_ProcChance: case SE_ProcChance:
spellbonuses.ProcChance = effect_value; spellbonuses.ProcChanceSPA = effect_value;
aabonuses.ProcChance = effect_value; aabonuses.ProcChanceSPA = effect_value;
itembonuses.ProcChance = effect_value; itembonuses.ProcChanceSPA = effect_value;
break; break;
case SE_ExtraAttackChance: case SE_ExtraAttackChance:
@ -3866,7 +3875,13 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
spellbonuses.ImprovedTaunt[0] = effect_value; spellbonuses.ImprovedTaunt[0] = effect_value;
spellbonuses.ImprovedTaunt[1] = effect_value; spellbonuses.ImprovedTaunt[1] = effect_value;
spellbonuses.ImprovedTaunt[2] = -1; 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 //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(); int mydex = GetDEX();
float AABonus = 0; float ProcChance = 0.0f;
ProcBonus = 0;
ProcChance = 0;
if (aabonuses.ProcChance) switch (hand) {
AABonus = float(aabonuses.ProcChance) / 100.0f;
switch(hand){
case SLOT_PRIMARY: case SLOT_PRIMARY:
weapon_speed = attack_timer.GetDuration(); weapon_speed = attack_timer.GetDuration();
break; break;
@ -7726,24 +7721,20 @@ float Bot::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_spe
break; break;
} }
//calculate the weapon speed in ms, so we can use the rule to compare against. //calculate the weapon speed in ms, so we can use the rule to compare against.
// fast as a client can swing, so should be the floor of the proc chance
if(weapon_speed < RuleI(Combat, MinHastedDelay)) // 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); weapon_speed = RuleI(Combat, MinHastedDelay);
ProcBonus += (float(itembonuses.ProcChance + spellbonuses.ProcChance) / 1000.0f + AABonus); if (RuleB(Combat, AdjustProcPerMinute)) {
ProcChance = (static_cast<float>(weapon_speed) *
if(RuleB(Combat, AdjustProcPerMinute) == true) RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms
{ ProcBonus += static_cast<float>(mydex) * RuleR(Combat, ProcPerMinDexContrib);
ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms ProcChance += ProcChance * ProcBonus / 100.0f;
ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib) / 100.0f; } else {
ProcChance = ProcChance + (ProcChance * ProcBonus); ProcChance = RuleR(Combat, BaseProcChance) +
} static_cast<float>(mydex) / RuleR(Combat, ProcDexDivideBy);
else ProcChance += ProcChance*ProcBonus / 100.0f;
{
ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy);
ProcChance = ProcChance + (ProcChance * ProcBonus);
} }
mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); 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 BotGetSpells(int spellslot) { return AIspells[spellslot].spellid; }
uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; } uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; }
uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; } 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 bool AvoidDamage(Mob* other, int32 &damage, bool CanRiposte);
virtual int GetMonkHandToHandDamage(void); virtual int GetMonkHandToHandDamage(void);
virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse); 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); 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) { void Client::SetTint(int16 in_slot, uint32 color) {
Color_Struct new_color; Color_Struct new_color;

View File

@ -250,6 +250,14 @@ public:
uint8 SlotConvert(uint8 slot,bool bracer=false); 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, 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); 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 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); void SendTraderItem(uint32 item_id,uint16 quantity);
uint16 FindTraderItem(int32 SerialNumber,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) 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++) for (int y = 0; y < 2; y++)
{ {
if (shield_target->shielder[y].shielder_id == GetID()) 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) 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(); shield_target->shielder[x].shielder_id = GetID();
int shieldbonus = shield->AC*2; int shieldbonus = shield->AC*2;
switch (GetAA(197)) switch (GetAA(197))
@ -1677,7 +1679,7 @@ void Client::Handle_OP_Shielding(const EQApplicationPacket *app)
} }
if (!ack) if (!ack)
{ {
Message(0, "No more than two warriors may shield the same being."); Message_StringID(0, ALREADY_SHIELDED);
shield_target = 0; shield_target = 0;
return; return;
} }
@ -12618,32 +12620,38 @@ void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app)
uint32 cost = 0; uint32 cost = 0;
uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id);
uint32 merchant_id = tar->MerchantType; uint32 merchant_id = tar->MerchantType;
if (RuleB(Merchant, EnableAltCurrencySell)) {
bool found = false; bool found = false;
std::list<MerchantList> merlist = zone->merchanttable[merchant_id]; std::list<MerchantList> merlist = zone->merchanttable[merchant_id];
std::list<MerchantList>::const_iterator itr; std::list<MerchantList>::const_iterator itr;
for(itr = merlist.begin(); itr != merlist.end(); ++itr) { for (itr = merlist.begin(); itr != merlist.end(); ++itr) {
MerchantList ml = *itr; MerchantList ml = *itr;
if(GetLevel() < ml.level_required) { if (GetLevel() < ml.level_required) {
continue; continue;
} }
int32 fac = tar->GetPrimaryFaction(); int32 fac = tar->GetPrimaryFaction();
if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) {
continue; continue;
} }
item = database.GetItem(ml.item); item = database.GetItem(ml.item);
if(!item) if (!item)
continue; continue;
if(item->ID == inst->GetItem()->ID) { if (item->ID == inst->GetItem()->ID) {
cost = ml.alt_currency_cost; cost = ml.alt_currency_cost;
found = true; found = true;
break; break;
} }
} }
if(!found) { if (!found) {
cost = 0;
}
}
else {
cost = 0; cost = 0;
} }
@ -12793,6 +12801,10 @@ void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) {
return; return;
} }
if (!RuleB(Merchant, EnableAltCurrencySell)) {
return;
}
const Item_Struct* item = nullptr; const Item_Struct* item = nullptr;
uint32 cost = 0; uint32 cost = 0;
uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id);

View File

@ -581,7 +581,8 @@ bool Client::Process() {
{ {
if (!CombatRange(shield_target)) 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++) for (int y = 0; y < 2; y++)
{ {
if (shield_target->shielder[y].shielder_id == GetID()) 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) { else if (strcasecmp(sep->arg[1], "roambox") == 0) {
if (target && target->IsAIControlled() && target->IsNPC()) { 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 tmp = 2500;
uint32 tmp2 = 2500;
if (sep->IsNumber(7)) if (sep->IsNumber(7))
tmp = atoi(sep->arg[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)) { else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) {
uint32 tmp = 2500; uint32 tmp = 2500;
uint32 tmp2 = 2500;
if (sep->IsNumber(4)) if (sep->IsNumber(4))
tmp = atoi(sep->arg[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 { else {
c->Message(0, "Usage: #ai roambox dist max_x min_x max_y min_y [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]"); c->Message(0, "Usage: #ai roambox dist roamdist [delay] [mindelay]");
} }
} }
else else

View File

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

View File

@ -82,6 +82,8 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
int chance = RuleI(Spells, BaseCritChance); int chance = RuleI(Spells, BaseCritChance);
chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
if (chance > 0){ if (chance > 0){
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals. int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
@ -123,7 +125,9 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100; 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; return value;
} }
@ -148,7 +152,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
return value; return value;
} }
int32 Client::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) { int32 Client::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
@ -295,8 +299,11 @@ int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
value += value*target->GetHealRate(spell_id, this)/100; value += value*target->GetHealRate(spell_id, this)/100;
if (Critical) if (Critical) {
entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s performs an exceptional heal! (%d)", GetName(), value); 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; return value;
} }
@ -321,6 +328,12 @@ int32 Client::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
int32 Client::GetActSpellCost(uint16 spell_id, int32 cost) 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 // 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) 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 // 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 // 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) || IsFearSpell(spell_id) ||
IsCharmSpell(spell_id) || IsCharmSpell(spell_id) ||
IsMezSpell(spell_id) || IsMezSpell(spell_id) ||

View File

@ -1012,6 +1012,22 @@ Mob *EntityList::GetMobByNpcTypeID(uint32 get_id)
return nullptr; 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) Object *EntityList::GetObjectByDBID(uint32 id)
{ {
if (id == 0 || object_list.empty()) 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) 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; 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, ...) void EntityList::MessageClose(Mob* sender, bool skipsender, float dist, uint32 type, const char* message, ...)
{ {
va_list argptr; va_list argptr;

View File

@ -135,6 +135,7 @@ public:
inline Mob *GetMobID(uint16 id) { return(GetMob(id)); } //for perl inline Mob *GetMobID(uint16 id) { return(GetMob(id)); } //for perl
Mob *GetMob(const char* name); Mob *GetMob(const char* name);
Mob *GetMobByNpcTypeID(uint32 get_id); Mob *GetMobByNpcTypeID(uint32 get_id);
bool IsMobSpawnedByNpcTypeID(uint32 get_id);
Mob *GetTargetForVirus(Mob* spreader); Mob *GetTargetForVirus(Mob* spreader);
inline NPC *GetNPCByID(uint16 id) inline NPC *GetNPCByID(uint16 id)
{ return npc_list.count(id) ? npc_list.at(id) : nullptr; } { 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 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 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 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 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 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, const char* message, ...);
void ChannelMessage(Mob* from, uint8 chan_num, uint8 language, uint8 lang_skill, 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 //see if were possibly hurt enough
float ratio = GetHPRatio(); float ratio = GetHPRatio();
if(ratio >= RuleI(Combat, FleeHPRatio)) float fleeratio = GetSpecialAbility(FLEE_PERCENT);
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
if(ratio >= fleeratio)
return; return;
//we might be hurt enough, check con now.. //we might be hurt enough, check con now..
@ -77,25 +80,24 @@ void Mob::CheckFlee() {
switch(con) { switch(con) {
//these values are not 100% researched //these values are not 100% researched
case CON_GREEN: case CON_GREEN:
run_ratio = RuleI(Combat, FleeHPRatio); run_ratio = fleeratio;
break; break;
case CON_LIGHTBLUE: case CON_LIGHTBLUE:
run_ratio = RuleI(Combat, FleeHPRatio) * 8 / 10; run_ratio = fleeratio * 9 / 10;
break; break;
case CON_BLUE: case CON_BLUE:
run_ratio = RuleI(Combat, FleeHPRatio) * 6 / 10; run_ratio = fleeratio * 8 / 10;
break; break;
default: default:
run_ratio = RuleI(Combat, FleeHPRatio) * 4 / 10; run_ratio = fleeratio * 7 / 10;
break; break;
} }
if(ratio < run_ratio) if(ratio < run_ratio)
{ {
if (RuleB(Combat, FleeIfNotAlone) || if (RuleB(Combat, FleeIfNotAlone) ||
(!RuleB(Combat, FleeIfNotAlone) && GetSpecialAbility(ALWAYS_FLEE) ||
(entity_list.GetHatedCount(hate_top, this) == 0))) (!RuleB(Combat, FleeIfNotAlone) && (entity_list.GetHatedCount(hate_top, this) == 0)))
StartFleeing(); StartFleeing();
} }
} }
@ -110,7 +112,9 @@ void Mob::ProcessFlee() {
} }
//see if we are still dying, if so, do nothing //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; return;
//we are not dying anymore... see what we do next //we are not dying anymore... see what we do next
@ -128,18 +132,18 @@ void Mob::ProcessFlee() {
float Mob::GetFearSpeed() { float Mob::GetFearSpeed() {
if(flee_mode) { if(flee_mode) {
//we know ratio < FLEE_HP_RATIO //we know ratio < FLEE_HP_RATIO
float speed = GetRunspeed(); float speed = GetBaseRunspeed();
float ratio = GetHPRatio(); float ratio = GetHPRatio();
float multiplier = RuleR(Combat, FleeMultiplier);
// mob's movement will halt with a decent snare at HP specified by rule. if(GetSnaredAmount() > 40)
if (ratio <= RuleI(Combat, FleeSnareHPRatio) && GetSnaredAmount() > 40) { multiplier = multiplier / 6.0f;
return 0.0001f;
}
if (ratio < FLEE_HP_MINSPEED) speed = speed * ratio * multiplier / 100;
ratio = FLEE_HP_MINSPEED;
speed = speed * 0.5 * ratio / 100; //NPC will eventually stop. Snares speeds this up.
if(speed < 0.09)
speed = 0.0001f;
return(speed); return(speed);
} }

View File

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

View File

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

View File

@ -65,6 +65,11 @@ Lua_Mob Lua_EntityList::GetMobByNpcTypeID(int npc_type) {
return Lua_Mob(self->GetMobByNpcTypeID(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_NPC Lua_EntityList::GetNPCByID(int id) {
Lua_Safe_Call_Class(Lua_NPC); Lua_Safe_Call_Class(Lua_NPC);
return Lua_NPC(self->GetNPCByID(id)); 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::*)(const char*))&Lua_EntityList::GetMob)
.def("GetMob", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMob) .def("GetMob", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMob)
.def("GetMobByNpcTypeID", (Lua_Mob(Lua_EntityList::*)(int))&Lua_EntityList::GetMobByNpcTypeID) .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("GetNPCByID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByID)
.def("GetNPCByNPCTypeID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByNPCTypeID) .def("GetNPCByNPCTypeID", (Lua_NPC(Lua_EntityList::*)(int))&Lua_EntityList::GetNPCByNPCTypeID)
.def("GetClientByName", (Lua_Client(Lua_EntityList::*)(const char*))&Lua_EntityList::GetClientByName) .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(const char *name);
Lua_Mob GetMob(int id); Lua_Mob GetMob(int id);
Lua_Mob GetMobByNpcTypeID(int npc_type); Lua_Mob GetMobByNpcTypeID(int npc_type);
bool IsMobSpawnedByNpcTypeID(int npc_type);
Lua_NPC GetNPCByID(int id); Lua_NPC GetNPCByID(int id);
Lua_NPC GetNPCByNPCTypeID(int npc_type); Lua_NPC GetNPCByNPCTypeID(int npc_type);
Lua_Client GetClientByName(const char *name); 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("leash", static_cast<int>(LEASH)),
luabind::value("tether", static_cast<int>(TETHER)), luabind::value("tether", static_cast<int>(TETHER)),
luabind::value("destructible_object", static_cast<int>(DESTRUCTIBLE_OBJECT)), 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); 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(); 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() { int Lua_NPC::GetNPCSpellsID() {
@ -494,7 +494,7 @@ luabind::scope lua_register_npc() {
.def("SaveGuardSpot", (void(Lua_NPC::*)(bool))&Lua_NPC::SaveGuardSpot) .def("SaveGuardSpot", (void(Lua_NPC::*)(bool))&Lua_NPC::SaveGuardSpot)
.def("IsGuarding", (bool(Lua_NPC::*)(void))&Lua_NPC::IsGuarding) .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))&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("GetNPCSpellsID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCSpellsID)
.def("GetSpawnPointID", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointID) .def("GetSpawnPointID", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointID)
.def("GetSpawnPointX", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointX) .def("GetSpawnPointX", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointX)

View File

@ -85,7 +85,7 @@ public:
void SaveGuardSpot(bool clear); void SaveGuardSpot(bool clear);
bool IsGuarding(); 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);
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 GetNPCSpellsID();
int GetSpawnPointID(); int GetSpawnPointID();
float GetSpawnPointX(); float GetSpawnPointX();

View File

@ -2824,7 +2824,8 @@ int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
value = (value * GetSpellScale() / 100); 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; 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, 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* 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) { } 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(const char *format, ...);
void Say_StringID(uint32 string_id, const char *message3 = 0, const char *message4 = 0, const char *message5 = 0, 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); 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_); bool PassLimitClass(uint32 Classes_, uint16 Class_);
void TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand = 13, int damage=0); 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 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 TryWeaponProc(const ItemInst* weapon, Mob *on, uint16 hand = 13);
void ExecWeaponProc(const ItemInst* weapon, uint16 spell_id, Mob *on); 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); 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 Item_Struct *weapon_item);
int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = nullptr); 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(); bool worldwasconnected = worldserver.Connected();
EQStream* eqss; EQStream* eqss;
EQStreamInterface *eqsi; EQStreamInterface *eqsi;
Timer temp_timer(10); uint8 IDLEZONEUPDATE = 200;
temp_timer.Start(); uint8 ZONEUPDATE = 10;
Timer zoneupdate_timer(ZONEUPDATE);
zoneupdate_timer.Start();
while(RunLoops) { while(RunLoops) {
{ //profiler block to omit the sleep from times { //profiler block to omit the sleep from times
@ -381,6 +383,13 @@ int main(int argc, char** argv) {
entity_list.AddClient(client); 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 //check for timeouts in other threads
timeout_manager.CheckTimeouts(); timeout_manager.CheckTimeouts();
@ -394,7 +403,7 @@ int main(int argc, char** argv) {
worldwasconnected = false; worldwasconnected = false;
} }
if (ZoneLoaded && temp_timer.Check()) { if (ZoneLoaded && zoneupdate_timer.Check()) {
{ {
if(net.group_timer.Enabled() && net.group_timer.Check()) if(net.group_timer.Enabled() && net.group_timer.Check())
entity_list.GroupProcess(); 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_min_y = -2;
roambox_movingto_x = -2; roambox_movingto_x = -2;
roambox_movingto_y = -2; roambox_movingto_y = -2;
roambox_min_delay = 1000;
roambox_delay = 1000; roambox_delay = 1000;
org_heading = heading; org_heading = heading;
p_depop = false; p_depop = false;
@ -1526,6 +1527,12 @@ void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool rem
case 'i': case 'i':
SetSpecialAbility(IMMUNE_TAUNT, remove ? 0 : 1); SetSpecialAbility(IMMUNE_TAUNT, remove ? 0 : 1);
break; break;
case 'e':
SetSpecialAbility(ALWAYS_FLEE, remove ? 0 : 1);
break;
case 'h':
SetSpecialAbility(FLEE_PERCENT, remove ? 0 : 1);
break;
default: default:
break; break;
@ -1686,7 +1693,14 @@ bool Mob::HasNPCSpecialAtk(const char* parse) {
HasAllAttacks = false; HasAllAttacks = false;
} }
break; break;
case 'e':
if(!GetSpecialAbility(ALWAYS_FLEE))
HasAllAttacks = false;
break;
case 'h':
if(!GetSpecialAbility(FLEE_PERCENT))
HasAllAttacks = false;
break;
default: default:
HasAllAttacks = false; HasAllAttacks = false;
break; break;

View File

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

View File

@ -150,6 +150,33 @@ XS(XS_EntityList_GetMobByNpcTypeID)
XSRETURN(1); 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); /* prototype to pass -Wmissing-prototypes */
XS(XS_EntityList_GetNPCByID) XS(XS_EntityList_GetNPCByID)
{ {
@ -2127,6 +2154,7 @@ XS(boot_EntityList)
newXSproto(strcpy(buf, "GetMob"), XS_EntityList_GetMob, file, "$$"); newXSproto(strcpy(buf, "GetMob"), XS_EntityList_GetMob, file, "$$");
newXSproto(strcpy(buf, "GetMobByID"), XS_EntityList_GetMobByID, file, "$$"); newXSproto(strcpy(buf, "GetMobByID"), XS_EntityList_GetMobByID, file, "$$");
newXSproto(strcpy(buf, "GetMobByNpcTypeID"), XS_EntityList_GetMobByNpcTypeID, 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, "GetNPCByID"), XS_EntityList_GetNPCByID, file, "$$");
newXSproto(strcpy(buf, "GetNPCByNPCTypeID"), XS_EntityList_GetNPCByNPCTypeID, file, "$$"); newXSproto(strcpy(buf, "GetNPCByNPCTypeID"), XS_EntityList_GetNPCByNPCTypeID, file, "$$");
newXSproto(strcpy(buf, "GetClientByName"), XS_EntityList_GetClientByName, 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) XS(XS_NPC_AI_SetRoambox)
{ {
dXSARGS; dXSARGS;
if (items < 6 || items > 7) if (items < 6 || items > 8)
Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay= 2500)"); Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay= 2500, iMinDelay= 2500)");
{ {
NPC * THIS; NPC * THIS;
float iDist = (float)SvNV(ST(1)); float iDist = (float)SvNV(ST(1));
@ -1443,6 +1443,7 @@ XS(XS_NPC_AI_SetRoambox)
float iMaxY = (float)SvNV(ST(4)); float iMaxY = (float)SvNV(ST(4));
float iMinY = (float)SvNV(ST(5)); float iMinY = (float)SvNV(ST(5));
uint32 iDelay; uint32 iDelay;
uint32 iMinDelay;
if (sv_derived_from(ST(0), "NPC")) { if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV*)SvRV(ST(0))); IV tmp = SvIV((SV*)SvRV(ST(0)));
@ -1453,13 +1454,20 @@ XS(XS_NPC_AI_SetRoambox)
if(THIS == nullptr) if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items < 7) if (items < 7){
iMinDelay = 2500;
iDelay = 2500; iDelay = 2500;
else { }
else if (items < 8){
iMinDelay = 2500;
iDelay = (uint32)SvUV(ST(6)); 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; XSRETURN_EMPTY;
} }
@ -2208,7 +2216,7 @@ XS(boot_NPC)
newXSproto(strcpy(buf, "NextGuardPosition"), XS_NPC_NextGuardPosition, file, "$"); newXSproto(strcpy(buf, "NextGuardPosition"), XS_NPC_NextGuardPosition, file, "$");
newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$;$"); newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$;$");
newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, 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, "GetNPCSpellsID"), XS_NPC_GetNPCSpellsID, file, "$");
newXSproto(strcpy(buf, "GetSpawnPointID"), XS_NPC_GetSpawnPointID, file, "$"); newXSproto(strcpy(buf, "GetSpawnPointID"), XS_NPC_GetSpawnPointID, file, "$");
newXSproto(strcpy(buf, "GetSpawnPointX"), XS_NPC_GetSpawnPointX, 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.AddNPC(npc);
entity_list.LimitAddNPC(npc); entity_list.LimitAddNPC(npc);
if(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); 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()) if(zone->InstantGrids())
{ {
found_spawn->LoadGrid(); found_spawn->LoadGrid();

View File

@ -229,8 +229,8 @@ bool Spawn2::Process() {
entity_list.AddNPC(npc); entity_list.AddNPC(npc);
//this limit add must be done after the AddNPC since we need the entity ID. //this limit add must be done after the AddNPC since we need the entity ID.
entity_list.LimitAddNPC(npc); entity_list.LimitAddNPC(npc);
if(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); 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()) { 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); _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(); 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; 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; id = in_id;
strn0cpy( name_, name, 120); strn0cpy( name_, name, 120);
group_spawn_limit = in_group_spawn_limit; 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[2]=maxy;
roambox[3]=miny; roambox[3]=miny;
roamdist=dist; roamdist=dist;
min_delay=min_delay_in;
delay=delay_in; delay=delay_in;
despawn=despawn_in; despawn=despawn_in;
despawn_timer=despawn_timer_in; despawn_timer=despawn_timer_in;
@ -150,11 +151,11 @@ bool ZoneDatabase::LoadSpawnGroups(const char* zone_name, uint16 version, SpawnG
// CODER new spawn code // CODER new spawn code
query = 0; 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); safe_delete_array(query);
while((row = mysql_fetch_row(result))) { 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); spawn_group_list->AddSpawnGroup(newSpawnGroup);
} }
mysql_free_result(result); mysql_free_result(result);
@ -205,11 +206,11 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_g
// CODER new spawn code // CODER new spawn code
query = 0; 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); safe_delete_array(query);
while((row = mysql_fetch_row(result))) { 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); spawn_group_list->AddSpawnGroup(newSpawnGroup);
} }
mysql_free_result(result); mysql_free_result(result);

View File

@ -39,13 +39,14 @@ public:
class SpawnGroup class SpawnGroup
{ {
public: 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(); ~SpawnGroup();
uint32 GetNPCType(); uint32 GetNPCType();
void AddSpawnEntry( SpawnEntry* newEntry ); void AddSpawnEntry( SpawnEntry* newEntry );
uint32 id; uint32 id;
float roamdist; float roamdist;
float roambox[4]; float roambox[4];
int min_delay;
int delay; int delay;
int despawn; int despawn;
uint32 despawn_timer; uint32 despawn_timer;

View File

@ -2826,6 +2826,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_CriticalMend: case SE_CriticalMend:
case SE_LimitCastTimeMax: case SE_LimitCastTimeMax:
case SE_TriggerOnReqCaster: case SE_TriggerOnReqCaster:
case SE_FrenziedDevastation:
{ {
break; break;
} }
@ -3418,13 +3419,11 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
{ {
if (spellbonuses.DistanceRemoval){ if (spellbonuses.DistanceRemoval){
int distance = sqrt( int distance = ((int(GetX()) - buffs[slot].caston_x) * (int(GetX()) - buffs[slot].caston_x)) +
((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(GetY()) - buffs[slot].caston_y) * (int(GetY()) - buffs[slot].caston_y)) +
((int(GetZ()) - buffs[slot].caston_z) * (int(GetZ()) - buffs[slot].caston_z)) ((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)) if(!TryFadeEffect(slot))
BuffFadeBySlot(slot , true); 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) { float Mob::GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod) {
ProcBonus = spellbonuses.SpellProcChance + itembonuses.SpellProcChance;
ProcChance = 0; ProcChance = 0;
if(cast_time > 0) if(cast_time > 0)
{ {
ProcChance = ((float)cast_time * RuleR(Casting, AvgSpellProcsPerMinute) / 60000.0f); ProcChance = ((float)cast_time * RuleR(Casting, AvgSpellProcsPerMinute) / 60000.0f);
ProcChance = ProcChance * (float)(ProcRateMod/100); ProcChance = ProcChance * (float)(ProcRateMod/100);
ProcChance = ProcChance+(ProcChance*ProcBonus/100);
} }
return ProcChance; 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; 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 // cast time is 0, just finish it right now and be done with it
if(cast_time == 0) { if(cast_time == 0) {
@ -461,8 +462,9 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
if (IsAIControlled()) if (IsAIControlled())
{ {
SetRunAnimSpeed(0); SetRunAnimSpeed(0);
if(this != pMob) pMob = entity_list.GetMob(target_id);
this->FaceTarget(pMob); if (pMob && this != pMob)
FaceTarget(pMob);
} }
// if we got here we didn't fizzle, and are starting our cast // 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); return(x);
} }
void NPC::AI_SetRoambox(float iDist, float iRoamDist, uint32 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); 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_distance = iDist;
roambox_max_x = iMaxX; roambox_max_x = iMaxX;
roambox_min_x = iMinX; 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_min_y = iMinY;
roambox_movingto_x = roambox_max_x + 1; // this will trigger a recalc roambox_movingto_x = roambox_max_x + 1; // this will trigger a recalc
roambox_delay = iDelay; roambox_delay = iDelay;
roambox_min_delay = iMinDelay;
} }
void NPC::DisplayWaypointInfo(Client *c) { void NPC::DisplayWaypointInfo(Client *c) {