mirror of
https://github.com/EQEmu/Server.git
synced 2026-09-03 05:16:37 +00:00
Merge branch 'master' into raycast
This commit is contained in:
+3
-1
@@ -232,7 +232,9 @@ IF(UNIX)
|
||||
TARGET_LINK_LIBRARIES(zone "${CMAKE_DL_LIBS}")
|
||||
TARGET_LINK_LIBRARIES(zone "z")
|
||||
TARGET_LINK_LIBRARIES(zone "m")
|
||||
TARGET_LINK_LIBRARIES(zone "rt")
|
||||
IF(NOT DARWIN)
|
||||
TARGET_LINK_LIBRARIES(zone "rt")
|
||||
ENDIF(NOT DARWIN)
|
||||
TARGET_LINK_LIBRARIES(zone "pthread")
|
||||
ADD_DEFINITIONS(-fPIC)
|
||||
ENDIF(UNIX)
|
||||
|
||||
@@ -1627,11 +1627,32 @@ void NPC::AI_DoMovement() {
|
||||
|
||||
if (gridno > 0 || cur_wp==-2) {
|
||||
if (movetimercompleted==true) { // time to pause at wp is over
|
||||
|
||||
int32 spawn_id = this->GetSpawnPointID();
|
||||
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
|
||||
iterator.Reset();
|
||||
Spawn2 *found_spawn = nullptr;
|
||||
|
||||
while(iterator.MoreElements())
|
||||
{
|
||||
Spawn2* cur = iterator.GetData();
|
||||
iterator.Advance();
|
||||
if(cur->GetID() == spawn_id)
|
||||
{
|
||||
found_spawn = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) {
|
||||
CastToNPC()->Depop(true); //depop and resart spawn timer
|
||||
if(found_spawn)
|
||||
found_spawn->SetNPCPointerNull();
|
||||
}
|
||||
else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) {
|
||||
CastToNPC()->Depop(false);//depop without spawn timer
|
||||
if(found_spawn)
|
||||
found_spawn->SetNPCPointerNull();
|
||||
}
|
||||
else {
|
||||
movetimercompleted=false;
|
||||
|
||||
+27
-6
@@ -80,6 +80,7 @@
|
||||
#define CANNOT_AFFECT_NPC 251 //That spell can not affect this target NPC.
|
||||
#define SUSPEND_MINION_HAS_AGGRO 256 //Your pet is the focus of something's attention.
|
||||
#define NO_PET 255 //You do not have a pet.
|
||||
#define GATE_FAIL 260 //Your gate is too unstable, and collapses.
|
||||
#define CORPSE_CANT_SENSE 262 //You cannot sense any corpses for this PC in this zone.
|
||||
#define SPELL_NO_HOLD 263 //Your spell did not take hold.
|
||||
#define CANNOT_CHARM 267 //This NPC cannot be charmed.
|
||||
@@ -99,6 +100,12 @@
|
||||
#define TRADESKILL_FAILED 336 //You lacked the skills to fashion the items together.
|
||||
#define TRADESKILL_TRIVIAL 338 //You can no longer advance your skill from making this item.
|
||||
#define TRADESKILL_SUCCEED 339 //You have fashioned the items together to create something new!
|
||||
#define EVADE_SUCCESS 343 //You have momentarily ducked away from the main combat.
|
||||
#define EVADE_FAIL 344 //Your attempts at ducking clear of combat fail.
|
||||
#define HIDE_FAIL 345 //You failed to hide yourself.
|
||||
#define HIDE_SUCCESS 346 //You have hidden yourself from view.
|
||||
#define SNEAK_SUCCESS 347 //You are as quiet as a cat stalking its prey.
|
||||
#define SNEAK_FAIL 348 //You are as quiet as a herd of running elephants.
|
||||
#define MEND_CRITICAL 349 //You magically mend your wounds and heal considerable damage.
|
||||
#define MEND_SUCCESS 350 //You mend your wounds and heal some damage.
|
||||
#define MEND_WORSEN 351 //You have worsened your wounds!
|
||||
@@ -160,6 +167,7 @@
|
||||
#define ASSASSINATES 1016 //%1 ASSASSINATES their victim!!
|
||||
#define CRIPPLING_BLOW 1021 //%1 lands a Crippling Blow!(%2)
|
||||
#define CRITICAL_HIT 1023 //%1 scores a critical hit! (%2)
|
||||
#define DEADLY_STRIKE 1024 //%1 scores a Deadly Strike!(%2)
|
||||
#define RESISTS_URGE 1025 //%1 resists their urge to flee.
|
||||
#define BERSERK_START 1027 //%1 goes into a berserker frenzy!
|
||||
#define DEATH_PACT 1028 //%1's death pact has been benevolently fulfilled!
|
||||
@@ -201,6 +209,7 @@
|
||||
#define AA_POINTS 1215 //points
|
||||
#define SPELL_FIZZLE_OTHER 1218 //%1's spell fizzles!
|
||||
#define MISSED_NOTE_OTHER 1219 //A missed note brings %1's song to a close!
|
||||
#define SPELL_LEVEL_REQ 1226 //This spell only works on people who are level %1 and under.
|
||||
#define CORPSE_DECAY_NOW 1227 //This corpse is waiting to expire.
|
||||
#define SURNAME_REJECTED 1374 //Your new surname was rejected. Please try a different name.
|
||||
#define DUEL_DECLINE 1383 //%1 has declined your challenge to duel to the death.
|
||||
@@ -234,24 +243,26 @@
|
||||
#define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1!
|
||||
#define EXPEDITION_MIN_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end.
|
||||
#define REWIND_WAIT 4059 //You must wait a bit longer before using the rewind command again.
|
||||
#define CORPSEDRAG_LIMIT 4061 //You are already dragging as much as you can!
|
||||
#define CORPSEDRAG_ALREADY 4062 //You are already dragging %1.
|
||||
#define CORPSEDRAG_LIMIT 4061 //You are already dragging as much as you can!
|
||||
#define CORPSEDRAG_ALREADY 4062 //You are already dragging %1.
|
||||
#define CORPSEDRAG_SOMEONE_ELSE 4063 //Someone else is dragging %1.
|
||||
#define CORPSEDRAG_BEGIN 4064 //You begin to drag %1.
|
||||
#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses.
|
||||
#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse.
|
||||
#define CORPSEDRAG_BEGIN 4064 //You begin to drag %1.
|
||||
#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses.
|
||||
#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse.
|
||||
#define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters.
|
||||
#define PETITION_NO_DELETE 5053 //You do not have a petition in the queue.
|
||||
#define PETITION_DELETED 5054 //Your petition was successfully deleted.
|
||||
#define GAIN_RAIDEXP 5085 //You gained raid experience!
|
||||
#define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there.
|
||||
#define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure.
|
||||
#define SUCCOR_FAIL 5169 //The portal collapes before you can escape!
|
||||
#define PET_ATTACKING 5501 //%1 tells you, 'Attacking %2 Master.'
|
||||
#define FATAL_BOW_SHOT 5745 //%1 performs a FATAL BOW SHOT!!
|
||||
#define MELEE_SILENCE 5806 //You *CANNOT* use this melee ability, you are suffering from amnesia!
|
||||
#define DISCIPLINE_REUSE_MSG 5807 //You can use the ability %1 again in %2 hour(s) %3 minute(s) %4 seconds.
|
||||
#define DISCIPLINE_REUSE_MSG2 5808 //You can use the ability %1 again in %2 minute(s) %3 seconds.
|
||||
#define FAILED_TAUNT 5811 //You have failed to taunt your target.
|
||||
#define PHYSICAL_RESIST_FAIL 5817 //Your target avoided your %1 ability.
|
||||
#define AA_NO_TARGET 5825 //You must first select a target for this ability!
|
||||
#define FORAGE_MASTERY 6012 //Your forage mastery has enabled you to find something else!
|
||||
#define GUILD_BANK_CANNOT_DEPOSIT 6097 // Cannot deposit this item. Containers must be empty, and only one of each LORE and no NO TRADE or TEMPORARY items may be deposited.
|
||||
@@ -289,8 +300,13 @@
|
||||
#define GAIN_RAID_LEADERSHIP_EXP 8789 //
|
||||
#define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining)
|
||||
#define FEAR_TOO_HIGH 9035 //Your target is too high of a level for your fear spell.
|
||||
#define SLOW_MOSTLY_SUCCESSFUL 9029 //Your spell was mostly successful.
|
||||
#define SLOW_PARTIALLY_SUCCESSFUL 9030 // Your spell was partially successful.
|
||||
#define SLOW_SLIGHTLY_SUCCESSFUL 9031 //Your spell was slightly successful.
|
||||
#define SPELL_OPPOSITE_EFFECT 9032 //Your spell may have had the opposite effect of what you desired.
|
||||
#define HAS_BEEN_AWAKENED 9037 //%1 has been awakened by %2.
|
||||
#define YOU_HEAL 9068 //You have healed %1 for %2 points of damage.
|
||||
#define OTHER_HIT_DOT 9072 //%1 has taken %2 damage from your %3.
|
||||
#define YOUR_HIT_DOT 9072 //%1 has taken %2 damage from your %3.
|
||||
#define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage.
|
||||
#define SHAKE_OFF_STUN 9077
|
||||
#define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses!
|
||||
@@ -323,6 +339,7 @@
|
||||
#define ALREADY_CASTING 12442 //You are already casting a spell!
|
||||
#define SENSE_CORPSE_NOT_NAME 12446 //You don't sense any corpses of that name.
|
||||
#define SENSE_CORPSE_NONE 12447 //You don't sense any corpses.
|
||||
#define SCREECH_BUFF_BLOCK 12448 //Your immunity buff protected you from the spell %1!
|
||||
#define NOT_HOLDING_ITEM 12452 //You are not holding an item!
|
||||
#define SENSE_UNDEAD 12471 //You sense undead in this direction.
|
||||
#define SENSE_ANIMAL 12472 //You sense an animal in this direction.
|
||||
@@ -353,10 +370,14 @@
|
||||
#define NOW_INVISIBLE 12950 //%1 is now Invisible.
|
||||
#define NOW_VISIBLE 12951 //%1 is now Visible.
|
||||
#define GUILD_NOT_MEMBER2 12966 //You are not in a guild.
|
||||
#define HOT_HEAL_SELF 12976 //You have been healed for %1 hit points by your %2.
|
||||
#define HOT_HEAL_OTHER 12997 //You have healed %1 for %2 hit points with your %3.
|
||||
#define HOT_HEALED_OTHER 12998 //%1 healed you for %2 hit points by %3.
|
||||
#define DISC_LEVEL_USE_ERROR 13004 //You are not sufficient level to use this discipline.
|
||||
#define TOGGLE_ON 13172 //Asking server to turn ON your incoming tells.
|
||||
#define TOGGLE_OFF 13173 //Asking server to turn OFF all incoming tells for you.
|
||||
#define DUEL_INPROGRESS 13251 //You have already accepted a duel with someone else cowardly dog.
|
||||
#define OTHER_HIT_DOT 13327 //%1 has taken %2 damage from %3 by %4.
|
||||
#define GENERIC_MISS 15041 //%1 missed %2
|
||||
|
||||
#endif
|
||||
|
||||
+35
-4
@@ -513,6 +513,14 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
|
||||
else if(our_owner && our_owner == target)
|
||||
return false;
|
||||
|
||||
// invalidate for swarm pets for later on if their owner is a corpse
|
||||
if (IsNPC() && CastToNPC()->GetSwarmInfo() && our_owner &&
|
||||
our_owner->IsCorpse() && !our_owner->IsPlayerCorpse())
|
||||
our_owner = nullptr;
|
||||
if (target->IsNPC() && target->CastToNPC()->GetSwarmInfo() && target_owner &&
|
||||
target_owner->IsCorpse() && !target_owner->IsPlayerCorpse())
|
||||
target_owner = nullptr;
|
||||
|
||||
//cannot hurt untargetable mobs
|
||||
bodyType bt = target->GetBodyType();
|
||||
|
||||
@@ -1196,14 +1204,21 @@ void Mob::ClearFeignMemory() {
|
||||
|
||||
bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) {
|
||||
|
||||
/*
|
||||
Charm formula is correct based on over 50 hours of personal live parsing - Kayen
|
||||
Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 255 CHA (min ~ 75 CHA)
|
||||
Charisma DOES NOT extend charm durations.
|
||||
Base effect value of charm spells in the spell file DOES NOT effect duration OR resist rate (unclear if does anything)
|
||||
Charm has a lower limit of 5% chance to break per tick, regardless of resist modifiers / level difference.
|
||||
*/
|
||||
|
||||
if(!caster) return false;
|
||||
|
||||
if(spells[spell_id].ResistDiff <= -600)
|
||||
return true;
|
||||
|
||||
//Applies additional Charisma bonus to resist rate
|
||||
float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster,0,0,1);
|
||||
|
||||
float resist_check = 0;
|
||||
|
||||
if(IsCharmSpell(spell_id)) {
|
||||
|
||||
if (spells[spell_id].powerful_flag == -1) //If charm spell has this set(-1), it can not break till end of duration.
|
||||
@@ -1213,8 +1228,13 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) {
|
||||
if (MakeRandomInt(0, 99) > RuleI(Spells, CharmBreakCheckChance))
|
||||
return true;
|
||||
|
||||
if (RuleB(Spells, CharismaCharmDuration))
|
||||
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster,0,0,true,true);
|
||||
else
|
||||
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, 0,0, false, true);
|
||||
|
||||
//2: The mob makes a resistance check against the charm
|
||||
if (resist_check == 100)
|
||||
if (resist_check == 100)
|
||||
return true;
|
||||
|
||||
else
|
||||
@@ -1234,6 +1254,9 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) {
|
||||
else
|
||||
{
|
||||
// Assume this is a harmony/pacify spell
|
||||
// If 'Lull' spell resists, do a second resist check with a charisma modifier AND regular resist checks. If resists agian you gain aggro.
|
||||
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, true);
|
||||
|
||||
if (resist_check == 100)
|
||||
return true;
|
||||
}
|
||||
@@ -1241,3 +1264,11 @@ bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Mob::RogueEvade(Mob *other)
|
||||
{
|
||||
int amount = other->GetHateAmount(this) - (GetLevel() * 13);
|
||||
other->SetHate(this, std::max(1, amount));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+160
-66
@@ -91,7 +91,7 @@ bool Mob::AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* w
|
||||
case ItemType2HBlunt: // 2H Blunt
|
||||
{
|
||||
skillinuse = Skill2HBlunt;
|
||||
type = anim2HWeapon;
|
||||
type = anim2HSlashing; //anim2HWeapon
|
||||
break;
|
||||
}
|
||||
case ItemType2HPiercing: // 2H Piercing
|
||||
@@ -140,7 +140,7 @@ bool Mob::AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* w
|
||||
}
|
||||
case Skill2HBlunt: // 2H Blunt
|
||||
{
|
||||
type = anim2HWeapon;
|
||||
type = anim2HSlashing; //anim2HWeapon
|
||||
break;
|
||||
}
|
||||
case 99: // 2H Piercing // change to Skill2HPiercing once activated
|
||||
@@ -1371,6 +1371,9 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
@@ -1983,6 +1986,9 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden)
|
||||
{
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
|
||||
@@ -3188,7 +3194,6 @@ int32 Mob::ReduceDamage(int32 damage)
|
||||
damage -= damage_to_reduce;
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
//UpdateRuneFlags();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3213,7 +3218,6 @@ int32 Mob::ReduceDamage(int32 damage)
|
||||
damage -= damage_to_reduce;
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
UpdateRuneFlags();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3221,7 +3225,6 @@ int32 Mob::ReduceDamage(int32 damage)
|
||||
" damage remaining.", damage_to_reduce, buffs[slot].melee_rune);
|
||||
buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce);
|
||||
damage -= damage_to_reduce;
|
||||
UpdateRuneFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3243,7 +3246,7 @@ int32 Mob::ReduceDamage(int32 damage)
|
||||
if(damage < 1)
|
||||
return -6;
|
||||
|
||||
if (HasRune())
|
||||
if (spellbonuses.MeleeRune[0] && spellbonuses.MeleeRune[1] >= 0)
|
||||
damage = RuneAbsorb(damage, SE_Rune);
|
||||
|
||||
if(damage < 1)
|
||||
@@ -3317,7 +3320,6 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
|
||||
damage -= damage_to_reduce;
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
//UpdateRuneFlags();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3341,7 +3343,6 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
|
||||
damage -= damage_to_reduce;
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
UpdateRuneFlags();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3349,7 +3350,6 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
|
||||
" damage remaining.", damage_to_reduce, buffs[slot].magic_rune);
|
||||
buffs[slot].magic_rune = (buffs[slot].magic_rune - damage_to_reduce);
|
||||
damage -= damage_to_reduce;
|
||||
UpdateRuneFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3372,7 +3372,7 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi
|
||||
return 0;
|
||||
|
||||
|
||||
if (HasSpellRune())
|
||||
if (spellbonuses.AbsorbMagicAtt[0] && spellbonuses.AbsorbMagicAtt[1] >= 0)
|
||||
damage = RuneAbsorb(damage, SE_AbsorbMagicAtt);
|
||||
|
||||
if(damage < 1)
|
||||
@@ -3616,6 +3616,8 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
//fade mez if we are mezzed
|
||||
if (IsMezzed()) {
|
||||
mlog(COMBAT__HITS, "Breaking mez due to attack.");
|
||||
entity_list.MessageClose_StringID(this, true, 100, MT_WornOff,
|
||||
HAS_BEEN_AWAKENED, GetCleanName(), attacker->GetCleanName());
|
||||
BuffFadeByEffect(SE_Mez);
|
||||
}
|
||||
|
||||
@@ -3685,29 +3687,8 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
|
||||
if(spell_id != SPELL_UNKNOWN && !iBuffTic) {
|
||||
//see if root will break
|
||||
if (IsRooted() && !FromDamageShield) { // neotoyko: only spells cancel root
|
||||
|
||||
/*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443
|
||||
The Viscid Roots AA does the following: Reduces the chance for root to break by X percent.
|
||||
There is no distinction of any kind between the caster inflicted damage, or anyone
|
||||
else's damage. There is also no distinction between Direct and DOT damage in the root code.
|
||||
There is however, a provision that if the damage inflicted is greater than 500 per hit, the
|
||||
chance to break root is increased. My guess is when this code was put in place, the devs at
|
||||
the time couldn't imagine DOT damage getting that high.
|
||||
*/
|
||||
int BreakChance = RuleI(Spells, RootBreakFromSpells);
|
||||
BreakChance -= BreakChance*rooted_mod/100;
|
||||
|
||||
if (BreakChance < 1)
|
||||
BreakChance = 1;
|
||||
|
||||
if (MakeRandomInt(0, 99) < BreakChance) {
|
||||
mlog(COMBAT__HITS, "Spell broke root! BreakChance percent chance");
|
||||
BuffFadeByEffect(SE_Root, buffslot); // buff slot is passed through so a root w/ dam doesnt cancel itself
|
||||
} else {
|
||||
mlog(COMBAT__HITS, "Spell did not break root. BreakChance percent chance");
|
||||
}
|
||||
}
|
||||
if (IsRooted() && !FromDamageShield) // neotoyko: only spells cancel root
|
||||
TryRootFadeByDamage(buffslot, attacker);
|
||||
}
|
||||
else if(spell_id == SPELL_UNKNOWN)
|
||||
{
|
||||
@@ -3829,49 +3810,83 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
// Everhood - So we can see our dot dmg like live shows it.
|
||||
if(spell_id != SPELL_UNKNOWN && damage > 0 && attacker && attacker != this && attacker->IsClient()) {
|
||||
//might filter on (attack_skill>200 && attack_skill<250), but I dont think we need it
|
||||
if(attacker->CastToClient()->GetFilter(FilterDOT) != FilterHide) {
|
||||
attacker->Message_StringID(MT_DoTDamage, OTHER_HIT_DOT, GetCleanName(),itoa(damage),spells[spell_id].name);
|
||||
}
|
||||
attacker->FilteredMessage_StringID(attacker, MT_DoTDamage, FilterDOT,
|
||||
YOUR_HIT_DOT, GetCleanName(), itoa(damage), spells[spell_id].name);
|
||||
// older clients don't have the below String ID, but it will be filtered
|
||||
entity_list.FilteredMessageClose_StringID(attacker, true, 200,
|
||||
MT_DoTDamage, FilterDOT, OTHER_HIT_DOT, GetCleanName(),
|
||||
itoa(damage), attacker->GetCleanName(), spells[spell_id].name);
|
||||
}
|
||||
} //end packet sending
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Mob::HealDamage(uint32 amount, Mob* caster) {
|
||||
void Mob::HealDamage(uint32 amount, Mob *caster, uint16 spell_id)
|
||||
{
|
||||
int32 maxhp = GetMaxHP();
|
||||
int32 curhp = GetHP();
|
||||
uint32 acthealed = 0;
|
||||
|
||||
if(caster && amount > 0)
|
||||
{
|
||||
if(caster->IsNPC() && !caster->IsPet())
|
||||
{
|
||||
if (caster && amount > 0) {
|
||||
if (caster->IsNPC() && !caster->IsPet()) {
|
||||
float npchealscale = caster->CastToNPC()->GetHealScale();
|
||||
amount = ((float)amount * npchealscale) / (float)100;
|
||||
amount = (static_cast<float>(amount) * npchealscale) / 100.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if(amount > (maxhp - curhp))
|
||||
if (amount > (maxhp - curhp))
|
||||
acthealed = (maxhp - curhp);
|
||||
else
|
||||
acthealed = amount;
|
||||
|
||||
if (acthealed > 100) {
|
||||
if (caster) {
|
||||
Message_StringID(MT_NonMelee, YOU_HEALED, caster->GetCleanName(), itoa(acthealed));
|
||||
if (caster != this)
|
||||
caster->Message_StringID(MT_NonMelee, YOU_HEAL, GetCleanName(), itoa(acthealed));
|
||||
if (IsBuffSpell(spell_id)) { // hots
|
||||
// message to caster
|
||||
if (caster->IsClient() && caster == this) {
|
||||
if (caster->CastToClient()->GetClientVersionBit() & BIT_SoFAndLater)
|
||||
FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime,
|
||||
HOT_HEAL_SELF, itoa(acthealed), spells[spell_id].name);
|
||||
else
|
||||
FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime,
|
||||
YOU_HEALED, GetCleanName(), itoa(acthealed));
|
||||
} else if (caster->IsClient() && caster != this) {
|
||||
if (caster->CastToClient()->GetClientVersionBit() & BIT_SoFAndLater)
|
||||
caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime,
|
||||
HOT_HEAL_OTHER, GetCleanName(), itoa(acthealed),
|
||||
spells[spell_id].name);
|
||||
else
|
||||
caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime,
|
||||
YOU_HEAL, GetCleanName(), itoa(acthealed));
|
||||
}
|
||||
// message to target
|
||||
if (IsClient() && caster != this) {
|
||||
if (CastToClient()->GetClientVersionBit() & BIT_SoFAndLater)
|
||||
FilteredMessage_StringID(this, MT_NonMelee, FilterHealOverTime,
|
||||
HOT_HEALED_OTHER, caster->GetCleanName(),
|
||||
itoa(acthealed), spells[spell_id].name);
|
||||
else
|
||||
FilteredMessage_StringID(this, MT_NonMelee, FilterHealOverTime,
|
||||
YOU_HEALED, caster->GetCleanName(), itoa(acthealed));
|
||||
}
|
||||
} else { // normal heals
|
||||
FilteredMessage_StringID(caster, MT_NonMelee, FilterSpellDamage,
|
||||
YOU_HEALED, caster->GetCleanName(), itoa(acthealed));
|
||||
if (caster != this)
|
||||
caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterSpellDamage,
|
||||
YOU_HEAL, GetCleanName(), itoa(acthealed));
|
||||
}
|
||||
} else {
|
||||
Message(MT_NonMelee, "You have been healed for %d points of damage.", acthealed);
|
||||
}
|
||||
}
|
||||
|
||||
if (curhp < maxhp) {
|
||||
if ((curhp+amount)>maxhp)
|
||||
curhp=maxhp;
|
||||
if ((curhp + amount) > maxhp)
|
||||
curhp = maxhp;
|
||||
else
|
||||
curhp+=amount;
|
||||
curhp += amount;
|
||||
SetHP(curhp);
|
||||
|
||||
SendHPUpdate();
|
||||
@@ -4072,6 +4087,10 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on
|
||||
}
|
||||
}
|
||||
}
|
||||
//If OneProcPerWeapon is not enabled, we reset the try for that weapon regardless of if we procced or not.
|
||||
//This is for some servers that may want to have as many procs triggering from weapons as possible in a single round.
|
||||
if(!RuleB(Combat, OneProcPerWeapon))
|
||||
proced = false;
|
||||
|
||||
if (!proced && inst) {
|
||||
for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) {
|
||||
@@ -4096,7 +4115,8 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on
|
||||
}
|
||||
} else {
|
||||
ExecWeaponProc(aug_i, aug->Proc.Effect, on);
|
||||
break;
|
||||
if (RuleB(Combat, OneProcPerWeapon))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4232,7 +4252,9 @@ void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage)
|
||||
{
|
||||
critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100
|
||||
damage = (damage * critMod) / 100;
|
||||
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRITICAL_HIT, GetCleanName(), itoa(damage));
|
||||
entity_list.FilteredMessageClose_StringID(this, false, 200,
|
||||
MT_CritMelee, FilterMeleeCrits, CRITICAL_HIT,
|
||||
GetCleanName(), itoa(damage));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4297,11 +4319,16 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack
|
||||
}
|
||||
}
|
||||
|
||||
int deadlyChance = 0;
|
||||
int deadlyMod = 0;
|
||||
if(skill == SkillArchery && GetClass() == RANGER && GetSkill(SkillArchery) >= 65)
|
||||
critChance += 6;
|
||||
|
||||
if(skill == SkillThrowing && GetClass() == ROGUE && GetSkill(SkillThrowing) >= 65)
|
||||
critChance += 6;
|
||||
if (skill == SkillThrowing && GetClass() == ROGUE && GetSkill(SkillThrowing) >= 65) {
|
||||
critChance += RuleI(Combat, RogueCritThrowingChance);
|
||||
deadlyChance = RuleI(Combat, RogueDeadlyStrikeChance);
|
||||
deadlyMod = RuleI(Combat, RogueDeadlyStrikeMod);
|
||||
}
|
||||
|
||||
int CritChanceBonus = GetCriticalChanceBonus(skill);
|
||||
|
||||
@@ -4347,16 +4374,32 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack
|
||||
critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100
|
||||
damage = damage * critMod / 100;
|
||||
|
||||
bool deadlySuccess = false;
|
||||
if (deadlyChance && MakeRandomFloat(0, 1) < static_cast<float>(deadlyChance) / 100.0f) {
|
||||
if (BehindMob(defender, GetX(), GetY())) {
|
||||
damage *= deadlyMod;
|
||||
deadlySuccess = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (crip_success) {
|
||||
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRIPPLING_BLOW, GetCleanName(), itoa(damage));
|
||||
entity_list.FilteredMessageClose_StringID(this, false, 200,
|
||||
MT_CritMelee, FilterMeleeCrits, CRIPPLING_BLOW,
|
||||
GetCleanName(), itoa(damage));
|
||||
// Crippling blows also have a chance to stun
|
||||
//Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a staggers message.
|
||||
if (defender->GetLevel() <= 55 && !defender->GetSpecialAbility(IMMUNE_STUN)){
|
||||
defender->Emote("staggers.");
|
||||
defender->Stun(0);
|
||||
}
|
||||
} else if (deadlySuccess) {
|
||||
entity_list.FilteredMessageClose_StringID(this, false, 200,
|
||||
MT_CritMelee, FilterMeleeCrits, DEADLY_STRIKE,
|
||||
GetCleanName(), itoa(damage));
|
||||
} else {
|
||||
entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRITICAL_HIT, GetCleanName(), itoa(damage));
|
||||
entity_list.FilteredMessageClose_StringID(this, false, 200,
|
||||
MT_CritMelee, FilterMeleeCrits, CRITICAL_HIT,
|
||||
GetCleanName(), itoa(damage));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4518,14 +4561,67 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, float chance)
|
||||
}
|
||||
}
|
||||
|
||||
bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) {
|
||||
|
||||
/*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443
|
||||
The Viscid Roots AA does the following: Reduces the chance for root to break by X percent.
|
||||
There is no distinction of any kind between the caster inflicted damage, or anyone
|
||||
else's damage. There is also no distinction between Direct and DOT damage in the root code.
|
||||
|
||||
/* General Mechanics
|
||||
- Check buffslot to make sure damage from a root does not cancel the root
|
||||
- If multiple roots on target, always and only checks first root slot and if broken only removes that slots root.
|
||||
- Only roots on determental spells can be broken by damage.
|
||||
- Root break chance values obtained from live parses.
|
||||
*/
|
||||
|
||||
if (!attacker || !spellbonuses.Root[0] || spellbonuses.Root[1] < 0)
|
||||
return false;
|
||||
|
||||
if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot){
|
||||
|
||||
int BreakChance = RuleI(Spells, RootBreakFromSpells);
|
||||
|
||||
BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance/100;
|
||||
int level_diff = attacker->GetLevel() - GetLevel();
|
||||
|
||||
//Use baseline if level difference <= 1 (ie. If target is (1) level less than you, or equal or greater level)
|
||||
|
||||
if (level_diff == 2)
|
||||
BreakChance = (BreakChance * 80) /100; //Decrease by 20%;
|
||||
|
||||
else if (level_diff >= 3 && level_diff <= 20)
|
||||
BreakChance = (BreakChance * 60) /100; //Decrease by 40%;
|
||||
|
||||
else if (level_diff > 21)
|
||||
BreakChance = (BreakChance * 20) /100; //Decrease by 80%;
|
||||
|
||||
if (BreakChance < 1)
|
||||
BreakChance = 1;
|
||||
|
||||
if (MakeRandomInt(0, 99) < BreakChance) {
|
||||
|
||||
if (!TryFadeEffect(spellbonuses.Root[1])) {
|
||||
BuffFadeBySlot(spellbonuses.Root[1]);
|
||||
mlog(COMBAT__HITS, "Spell broke root! BreakChance percent chance");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mlog(COMBAT__HITS, "Spell did not break root. BreakChance percent chance");
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 Mob::RuneAbsorb(int32 damage, uint16 type)
|
||||
{
|
||||
uint32 buff_max = GetMaxTotalSlots();
|
||||
if (type == SE_Rune){
|
||||
for(uint32 slot = 0; slot < buff_max; slot++) {
|
||||
if((buffs[slot].spellid != SPELL_UNKNOWN) && (buffs[slot].melee_rune) && IsEffectInSpell(buffs[slot].spellid, type)){
|
||||
if(slot == spellbonuses.MeleeRune[1] && spellbonuses.MeleeRune[0] && buffs[slot].melee_rune && IsValidSpell(buffs[slot].spellid)){
|
||||
uint32 melee_rune_left = buffs[slot].melee_rune;
|
||||
if(melee_rune_left >= damage)
|
||||
|
||||
if(melee_rune_left > damage)
|
||||
{
|
||||
melee_rune_left -= damage;
|
||||
buffs[slot].melee_rune = melee_rune_left;
|
||||
@@ -4536,22 +4632,20 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type)
|
||||
{
|
||||
if(melee_rune_left > 0)
|
||||
damage -= melee_rune_left;
|
||||
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
UpdateRuneFlags();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
else{
|
||||
for(uint32 slot = 0; slot < buff_max; slot++) {
|
||||
if((buffs[slot].spellid != SPELL_UNKNOWN) && (buffs[slot].magic_rune) && IsEffectInSpell(buffs[slot].spellid, type)){
|
||||
if(slot == spellbonuses.AbsorbMagicAtt[1] && spellbonuses.AbsorbMagicAtt[0] && buffs[slot].magic_rune && IsValidSpell(buffs[slot].spellid)){
|
||||
uint32 magic_rune_left = buffs[slot].magic_rune;
|
||||
if(magic_rune_left >= damage)
|
||||
if(magic_rune_left > damage)
|
||||
{
|
||||
magic_rune_left -= damage;
|
||||
buffs[slot].magic_rune = magic_rune_left;
|
||||
@@ -4562,14 +4656,14 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type)
|
||||
{
|
||||
if(magic_rune_left > 0)
|
||||
damage -= magic_rune_left;
|
||||
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
UpdateRuneFlags();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return damage;
|
||||
}
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
|
||||
+88
-27
@@ -1338,19 +1338,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
}
|
||||
}
|
||||
else if ((effect_value - 100) < 0) { // Slow
|
||||
//Slow Mitigation works by taking the amount that would be slowed, and adding a multiplied version of the difference.
|
||||
int real_slow_value = (100 - effect_value) * -1;
|
||||
if (slow_mitigation){
|
||||
int new_effect_value = SlowMitigation(false,caster,real_slow_value);
|
||||
if (new_effect_value < newbon->haste) {
|
||||
newbon->haste = new_effect_value;
|
||||
SlowMitigation(true,caster);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (real_slow_value < newbon->haste)
|
||||
newbon->haste = real_slow_value;
|
||||
}
|
||||
real_slow_value -= ((real_slow_value * GetSlowMitigation()/100));
|
||||
if (real_slow_value < newbon->haste)
|
||||
newbon->haste = real_slow_value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1358,16 +1349,29 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
case SE_AttackSpeed2:
|
||||
{
|
||||
if ((effect_value - 100) > 0) { // Haste V2 - Stacks with V1 but does not Overcap
|
||||
if (newbon->hastetype2 < 0) break; //Slowed - Don't apply haste2
|
||||
if ((effect_value - 100) > newbon->hastetype2) {
|
||||
newbon->hastetype2 = effect_value - 100;
|
||||
}
|
||||
}
|
||||
else if ((effect_value - 100) < 0) { // Slow
|
||||
int real_slow_value = (100 - effect_value) * -1;
|
||||
real_slow_value -= ((real_slow_value * GetSlowMitigation()/100));
|
||||
if (real_slow_value < newbon->hastetype2)
|
||||
newbon->hastetype2 = real_slow_value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_AttackSpeed3:
|
||||
{
|
||||
if (effect_value > 0) { // Haste V3 - Stacks and Overcaps
|
||||
if (effect_value < 0){ //Slow
|
||||
effect_value -= ((effect_value * GetSlowMitigation()/100));
|
||||
if (effect_value < newbon->hastetype3)
|
||||
newbon->hastetype3 = effect_value;
|
||||
}
|
||||
|
||||
else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps
|
||||
if (effect_value > newbon->hastetype3) {
|
||||
newbon->hastetype3 = effect_value;
|
||||
}
|
||||
@@ -1377,18 +1381,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
case SE_AttackSpeed4:
|
||||
{
|
||||
if (effect_value > 0) {
|
||||
if (slow_mitigation){
|
||||
int new_effect_value = SlowMitigation(false,caster,effect_value);
|
||||
if (new_effect_value > newbon->inhibitmelee) {
|
||||
newbon->inhibitmelee = new_effect_value;
|
||||
SlowMitigation(true,caster);
|
||||
}
|
||||
}
|
||||
else if (effect_value > newbon->inhibitmelee) {
|
||||
newbon->inhibitmelee = effect_value;
|
||||
}
|
||||
if (effect_value < 0) //A few spells use negative values(Descriptions all indicate it should be a slow)
|
||||
effect_value = effect_value * -1;
|
||||
|
||||
if (effect_value > 0 && effect_value > newbon->inhibitmelee) {
|
||||
effect_value -= ((effect_value * GetSlowMitigation()/100));
|
||||
if (effect_value > newbon->inhibitmelee)
|
||||
newbon->inhibitmelee = effect_value;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2533,6 +2534,50 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
newbon->FrenziedDevastation += spells[spell_id].base2[i];
|
||||
break;
|
||||
|
||||
case SE_Root:
|
||||
if (newbon->Root[0] && (newbon->Root[1] > buffslot)){
|
||||
newbon->Root[0] = 1;
|
||||
newbon->Root[1] = buffslot;
|
||||
}
|
||||
else if (!newbon->Root[0]){
|
||||
newbon->Root[0] = 1;
|
||||
newbon->Root[1] = buffslot;
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_Rune:
|
||||
|
||||
if (newbon->MeleeRune[0] && (newbon->MeleeRune[1] > buffslot)){
|
||||
|
||||
newbon->MeleeRune[0] = effect_value;
|
||||
newbon->MeleeRune[1] = buffslot;
|
||||
}
|
||||
else if (!newbon->MeleeRune[0]){
|
||||
newbon->MeleeRune[0] = effect_value;
|
||||
newbon->MeleeRune[1] = buffslot;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SE_AbsorbMagicAtt:
|
||||
if (newbon->AbsorbMagicAtt[0] && (newbon->AbsorbMagicAtt[1] > buffslot)){
|
||||
newbon->AbsorbMagicAtt[0] = effect_value;
|
||||
newbon->AbsorbMagicAtt[1] = buffslot;
|
||||
}
|
||||
else if (!newbon->AbsorbMagicAtt[0]){
|
||||
newbon->AbsorbMagicAtt[0] = effect_value;
|
||||
newbon->AbsorbMagicAtt[1] = buffslot;
|
||||
}
|
||||
break;
|
||||
|
||||
case SE_NegateIfCombat:
|
||||
newbon->NegateIfCombat = true;
|
||||
break;
|
||||
|
||||
case SE_Screech:
|
||||
newbon->Screech = effect_value;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3878,10 +3923,26 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
||||
break;
|
||||
|
||||
case SE_FrenziedDevastation:
|
||||
spellbonuses.FrenziedDevastation += effect_value;
|
||||
aabonuses.FrenziedDevastation += effect_value;
|
||||
itembonuses.FrenziedDevastation += effect_value;
|
||||
spellbonuses.FrenziedDevastation = effect_value;
|
||||
aabonuses.FrenziedDevastation = effect_value;
|
||||
itembonuses.FrenziedDevastation = effect_value;
|
||||
break;
|
||||
|
||||
case SE_Root:
|
||||
spellbonuses.Root[0] = effect_value;
|
||||
spellbonuses.Root[1] = -1;
|
||||
break;
|
||||
|
||||
case SE_Rune:
|
||||
spellbonuses.MeleeRune[0] = effect_value;
|
||||
spellbonuses.MeleeRune[1] = -1;
|
||||
break;
|
||||
|
||||
case SE_AbsorbMagicAtt:
|
||||
spellbonuses.AbsorbMagicAtt[0] = effect_value;
|
||||
spellbonuses.AbsorbMagicAtt[1] = -1;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-3
@@ -3151,9 +3151,9 @@ void Bot::SpellProcess()
|
||||
void Bot::BotMeditate(bool isSitting) {
|
||||
if(isSitting) {
|
||||
// If the bot is a caster has less than 99% mana while its not engaged, he needs to sit to meditate
|
||||
if(GetManaRatio() < 99.0f)
|
||||
if(GetManaRatio() < 99.0f || GetHPRatio() < 99.0f)
|
||||
{
|
||||
if(!IsSitting())
|
||||
if (!IsEngaged() && !IsSitting())
|
||||
Sit();
|
||||
}
|
||||
else
|
||||
@@ -3237,6 +3237,9 @@ void Bot::BotRangedAttack(Mob* other) {
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
@@ -5281,6 +5284,28 @@ uint32 Bot::GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) {
|
||||
// This essentially performs a '#bot update,' with appearance packets, based on the current methods.
|
||||
// This should not be called outside of Client::SetEXP() due to it's lack of rule checks.
|
||||
if(client) {
|
||||
std::list<Bot*> blist = entity_list.GetBotsByBotOwnerCharacterID(client->CharacterID());
|
||||
|
||||
for(std::list<Bot*>::iterator biter = blist.begin(); biter != blist.end(); ++biter) {
|
||||
Bot* bot = *biter;
|
||||
if(bot && (bot->GetLevel() != client->GetLevel())) {
|
||||
bot->SetPetChooser(false); // not sure what this does, but was in bot 'update' code
|
||||
bot->CalcBotStats(false);
|
||||
if(sendlvlapp)
|
||||
bot->SendLevelAppearance();
|
||||
// modified from Client::SetLevel()
|
||||
bot->SendAppearancePacket(AT_WhoLevel, level, true, true); // who level change
|
||||
}
|
||||
}
|
||||
|
||||
blist.clear();
|
||||
}
|
||||
}
|
||||
|
||||
std::string Bot::ClassIdToString(uint16 classId) {
|
||||
std::string Result;
|
||||
|
||||
@@ -6640,6 +6665,9 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(GetTarget())
|
||||
TriggerDefensiveProcs(weapon, other, Hand, damage);
|
||||
|
||||
@@ -14967,7 +14995,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bot* targetedBot;
|
||||
Bot *targetedBot = nullptr;
|
||||
|
||||
if(c->GetTarget() != nullptr) {
|
||||
if (c->GetTarget()->IsBot() && (c->GetTarget()->CastToBot()->GetBotOwner() == c))
|
||||
|
||||
@@ -352,6 +352,7 @@ public:
|
||||
static uint32 CreatedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage);
|
||||
static uint32 AllowedBotSpawns(uint32 botOwnerCharacterID, std::string* errorMessage);
|
||||
static uint32 GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage);
|
||||
static void LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp);
|
||||
//static bool SetBotOwnerCharacterID(uint32 botID, uint32 botOwnerCharacterID, std::string* errorMessage);
|
||||
static std::string ClassIdToString(uint16 classId);
|
||||
static std::string RaceIdToString(uint16 raceId);
|
||||
|
||||
+89
-11
@@ -1022,11 +1022,12 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
|
||||
if(command_dispatch(this, message) == -2) {
|
||||
if(parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0);
|
||||
if(i == 0) {
|
||||
if(i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(13, "Command '%s' not recognized.", message);
|
||||
}
|
||||
} else {
|
||||
Message(13, "Command '%s' not recognized.", message);
|
||||
if(!RuleB(Chat, SuppressCommandErrors))
|
||||
Message(13, "Command '%s' not recognized.", message);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -2306,6 +2307,8 @@ uint64 Client::GetAllMoney() {
|
||||
}
|
||||
|
||||
bool Client::CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int chancemodi) {
|
||||
if (IsDead() || IsUnconscious())
|
||||
return false;
|
||||
if (IsAIControlled()) // no skillups while chamred =p
|
||||
return false;
|
||||
if (skillid > HIGHEST_SKILL)
|
||||
@@ -2349,6 +2352,10 @@ bool Client::CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int cha
|
||||
}
|
||||
|
||||
void Client::CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill) {
|
||||
if (IsDead() || IsUnconscious())
|
||||
return;
|
||||
if (IsAIControlled())
|
||||
return;
|
||||
if (langid >= MAX_PP_LANGUAGE)
|
||||
return; // do nothing if langid is an invalid language
|
||||
|
||||
@@ -2852,7 +2859,12 @@ void Client::ServerFilter(SetServerFilter_Struct* filter){
|
||||
else
|
||||
ClientFilters[FilterSpellCrits] = FilterHide;
|
||||
|
||||
Filter1(FilterMeleeCrits);
|
||||
if (filter->filters[FilterMeleeCrits] == 0)
|
||||
ClientFilters[FilterMeleeCrits] = FilterShow;
|
||||
else if (filter->filters[FilterMeleeCrits] == 1)
|
||||
ClientFilters[FilterMeleeCrits] = FilterShowSelfOnly;
|
||||
else
|
||||
ClientFilters[FilterMeleeCrits] = FilterHide;
|
||||
|
||||
if(filter->filters[FilterSpellDamage] == 0)
|
||||
ClientFilters[FilterSpellDamage] = FilterShow;
|
||||
@@ -2866,12 +2878,41 @@ void Client::ServerFilter(SetServerFilter_Struct* filter){
|
||||
Filter0(FilterOthersHit);
|
||||
Filter0(FilterMissedMe);
|
||||
Filter1(FilterDamageShields);
|
||||
Filter1(FilterDOT);
|
||||
|
||||
if (GetClientVersionBit() & BIT_SoDAndLater) {
|
||||
if (filter->filters[FilterDOT] == 0)
|
||||
ClientFilters[FilterDOT] = FilterShow;
|
||||
else if (filter->filters[FilterDOT] == 1)
|
||||
ClientFilters[FilterDOT] = FilterShowSelfOnly;
|
||||
else if (filter->filters[FilterDOT] == 2)
|
||||
ClientFilters[FilterDOT] = FilterShowGroupOnly;
|
||||
else
|
||||
ClientFilters[FilterDOT] = FilterHide;
|
||||
} else {
|
||||
if (filter->filters[FilterDOT] == 0) // show functions as self only
|
||||
ClientFilters[FilterDOT] = FilterShowSelfOnly;
|
||||
else
|
||||
ClientFilters[FilterDOT] = FilterHide;
|
||||
}
|
||||
|
||||
Filter1(FilterPetHits);
|
||||
Filter1(FilterPetMisses);
|
||||
Filter1(FilterFocusEffects);
|
||||
Filter1(FilterPetSpells);
|
||||
Filter1(FilterHealOverTime);
|
||||
|
||||
if (GetClientVersionBit() & BIT_SoDAndLater) {
|
||||
if (filter->filters[FilterHealOverTime] == 0)
|
||||
ClientFilters[FilterHealOverTime] = FilterShow;
|
||||
// This is called 'Show Mine Only' in the clients, but functions the same as show
|
||||
// so instead of apply special logic, just set to show
|
||||
else if (filter->filters[FilterHealOverTime] == 1)
|
||||
ClientFilters[FilterHealOverTime] = FilterShow;
|
||||
else
|
||||
ClientFilters[FilterHealOverTime] = FilterHide;
|
||||
} else {
|
||||
// these clients don't have a 'self only' filter
|
||||
Filter1(FilterHealOverTime);
|
||||
}
|
||||
}
|
||||
|
||||
// this version is for messages with no parameters
|
||||
@@ -2983,8 +3024,18 @@ bool Client::FilteredMessageCheck(Mob *sender, eqFilterType filter)
|
||||
return false;
|
||||
} else if (mode == FilterShowGroupOnly) {
|
||||
Group *g = GetGroup();
|
||||
if (!g || !g->IsGroupMember(sender))
|
||||
Raid *r = GetRaid();
|
||||
if (g) {
|
||||
if (g->IsGroupMember(sender))
|
||||
return true;
|
||||
} else if (r && sender->IsClient()) {
|
||||
uint32 rgid1 = r->GetGroup(this);
|
||||
uint32 rgid2 = r->GetGroup(sender->CastToClient());
|
||||
if (rgid1 != 0xFFFFFFFF && rgid1 == rgid2)
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3027,7 +3078,7 @@ void Client::FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType fil
|
||||
type = 4;
|
||||
|
||||
if (!message1) {
|
||||
Message_StringID(type, string_id); // use the simple message instead
|
||||
FilteredMessage_StringID(sender, type, filter, string_id); // use the simple message instead
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6666,9 +6717,9 @@ void Client::SendStatsWindow(Client* client, bool use_window)
|
||||
uint32 buff_count = GetMaxTotalSlots();
|
||||
for (int i=0; i < buff_count; i++) {
|
||||
if (buffs[i].spellid != SPELL_UNKNOWN) {
|
||||
if ((HasRune() || HasPartialMeleeRune()) && buffs[i].melee_rune > 0) { rune_number += buffs[i].melee_rune; }
|
||||
if (buffs[i].melee_rune > 0) { rune_number += buffs[i].melee_rune; }
|
||||
|
||||
if ((HasSpellRune() || HasPartialSpellRune()) && buffs[i].magic_rune > 0) { magic_rune_number += buffs[i].magic_rune; }
|
||||
if (buffs[i].magic_rune > 0) { magic_rune_number += buffs[i].magic_rune; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8111,7 +8162,7 @@ void Client::Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_
|
||||
if(!auto_consume) //no message if the client consumed for us
|
||||
entity_list.MessageClose_StringID(this, true, 50, 0, EATING_MESSAGE, GetName(), item->Name);
|
||||
|
||||
#if EQDEBUG >= 1
|
||||
#if EQDEBUG >= 5
|
||||
LogFile->write(EQEMuLog::Debug, "Eating from slot:%i", (int)slot);
|
||||
#endif
|
||||
}
|
||||
@@ -8128,7 +8179,7 @@ void Client::Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_
|
||||
if(!auto_consume) //no message if the client consumed for us
|
||||
entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name);
|
||||
|
||||
#if EQDEBUG >= 1
|
||||
#if EQDEBUG >= 5
|
||||
LogFile->write(EQEMuLog::Debug, "Drinking from slot:%i", (int)slot);
|
||||
#endif
|
||||
}
|
||||
@@ -8163,3 +8214,30 @@ void Client::PlayMP3(const char* fname)
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::ExpeditionSay(const char *str, int ExpID) {
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char* query = 0;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
|
||||
if (!database.RunQuery(query,MakeAnyLenString(&query, "SELECT `player_name` FROM `cust_inst_players` WHERE `inst_id` = %i", ExpID),errbuf,&result)){
|
||||
safe_delete_array(query);
|
||||
return;
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
if(result)
|
||||
this->Message(14, "You say to the expedition, '%s'", str);
|
||||
|
||||
while((row = mysql_fetch_row(result))) {
|
||||
const char* CharName = row[0];
|
||||
if(strcmp(CharName, this->GetCleanName()) != 0)
|
||||
worldserver.SendEmoteMessage(CharName, 0, 0, 14, "%s says to the expedition, '%s'", this->GetCleanName(), str);
|
||||
// ChannelList->CreateChannel(ChannelName, ChannelOwner, ChannelPassword, true, atoi(row[3]));
|
||||
}
|
||||
|
||||
mysql_free_result(result);
|
||||
|
||||
}
|
||||
+3
-1
@@ -305,6 +305,7 @@ public:
|
||||
void SetHideMe(bool hm);
|
||||
inline uint16 GetPort() const { return port; }
|
||||
bool IsDead() const { return(dead); }
|
||||
bool IsUnconscious() const { return ((cur_hp <= 0) ? true : false); }
|
||||
inline bool IsLFP() { return LFP; }
|
||||
void UpdateLFP();
|
||||
|
||||
@@ -794,7 +795,7 @@ public:
|
||||
void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false);
|
||||
void PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootItem_Struct** bag_item_data = 0);
|
||||
bool AutoPutLootInInventory(ItemInst& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0);
|
||||
void SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0, bool attuned=false, uint16 to_slot=SLOT_CURSOR);
|
||||
bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0, bool attuned=false, uint16 to_slot=SLOT_CURSOR);
|
||||
void SetStats(uint8 type,int16 set_val);
|
||||
void IncStats(uint8 type,int16 increase_val);
|
||||
void DropItem(int16 slot_id);
|
||||
@@ -1169,6 +1170,7 @@ public:
|
||||
std::string GetAccountFlag(std::string flag); float GetDamageMultiplier(SkillUseTypes);
|
||||
void Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_consume);
|
||||
void PlayMP3(const char* fname);
|
||||
void ExpeditionSay(const char *str, int ExpID);
|
||||
int mod_client_damage(int damage, SkillUseTypes skillinuse, int hand, const ItemInst* weapon, Mob* other);
|
||||
bool mod_client_message(char* message, uint8 chan_num);
|
||||
bool mod_can_increase_skill(SkillUseTypes skillid, Mob* against_who);
|
||||
|
||||
+41
-9
@@ -3478,21 +3478,23 @@ void Client::Handle_OP_Hide(const EQApplicationPacket *app)
|
||||
}
|
||||
if(GetClass() == ROGUE){
|
||||
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage,sizeof(SimpleMessage_Struct));
|
||||
SimpleMessage_Struct *msg=(SimpleMessage_Struct *)outapp->pBuffer;
|
||||
msg->color=0x010E;
|
||||
if (!auto_attack && entity_list.Fighting(this)) {
|
||||
SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer;
|
||||
msg->color = 0x010E;
|
||||
Mob *evadetar = GetTarget();
|
||||
if (!auto_attack && (evadetar && evadetar->CheckAggro(this)
|
||||
&& evadetar->IsNPC())) {
|
||||
if (MakeRandomInt(0, 260) < (int)GetSkill(SkillHide)) {
|
||||
msg->string_id=343;
|
||||
entity_list.Evade(this);
|
||||
msg->string_id = EVADE_SUCCESS;
|
||||
RogueEvade(evadetar);
|
||||
} else {
|
||||
msg->string_id=344;
|
||||
msg->string_id = EVADE_FAIL;
|
||||
}
|
||||
} else {
|
||||
if (hidden){
|
||||
msg->string_id=346;
|
||||
msg->string_id = HIDE_SUCCESS;
|
||||
}
|
||||
else {
|
||||
msg->string_id=345;
|
||||
msg->string_id = HIDE_FAIL;
|
||||
}
|
||||
}
|
||||
FastQueuePacket(&outapp);
|
||||
@@ -5419,6 +5421,12 @@ void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app)
|
||||
action = 0;
|
||||
}
|
||||
|
||||
// 1199 I don't have time for that now. etc
|
||||
if (!tmp->CastToNPC()->IsMerchantOpen()) {
|
||||
tmp->Say_StringID(MakeRandomInt(1199, 1202));
|
||||
action = 0;
|
||||
}
|
||||
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct));
|
||||
Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer;
|
||||
|
||||
@@ -12466,7 +12474,15 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app)
|
||||
//
|
||||
|
||||
char *GuildName = (char *)app->pBuffer;
|
||||
#ifdef DARWIN
|
||||
#if __DARWIN_C_LEVEL < 200809L
|
||||
if (strlen(GuildName) > 60)
|
||||
#else
|
||||
if(strnlen(GuildName, 64) > 60)
|
||||
#endif // __DARWIN_C_LEVEL
|
||||
#else
|
||||
if(strnlen(GuildName, 64) > 60)
|
||||
#endif // DARWIN
|
||||
{
|
||||
Message(clientMessageError, "Guild name too long.");
|
||||
return;
|
||||
@@ -12924,7 +12940,15 @@ void Client::Handle_OP_LFGuild(const EQApplicationPacket *app)
|
||||
VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_PlayerToggle_Struct);
|
||||
LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)app->pBuffer;
|
||||
|
||||
#ifdef DARWIN
|
||||
#if __DARWIN_C_LEVEL < 200809L
|
||||
if (strlen(pts->Comment) > 256)
|
||||
#else
|
||||
if(strnlen(pts->Comment, 256) > 256)
|
||||
#endif // __DARWIN_C_LEVEL
|
||||
#else
|
||||
if(strnlen(pts->Comment, 256) > 256)
|
||||
#endif // DARWIN
|
||||
return;
|
||||
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(pts->Comment) + 38);
|
||||
@@ -12951,7 +12975,15 @@ void Client::Handle_OP_LFGuild(const EQApplicationPacket *app)
|
||||
VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_GuildToggle_Struct);
|
||||
LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)app->pBuffer;
|
||||
|
||||
if(strnlen(gts->Comment, 256) > 256)
|
||||
#ifdef DARWIN
|
||||
#if __DARWIN_C_LEVEL < 200809L
|
||||
if (strlen(gts->Comment) > 256)
|
||||
#else
|
||||
if(strnlen(gts->Comment, 256) > 256)
|
||||
#endif // __DARWIN_C_LEVEL
|
||||
#else
|
||||
if(strnlen(gts->Comment, 256) > 256)
|
||||
#endif // __DARWIN
|
||||
return;
|
||||
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(gts->Comment) + strlen(guild_mgr.GetGuildName(GuildID())) + 43);
|
||||
|
||||
@@ -570,6 +570,9 @@ bool Client::Process() {
|
||||
viral_timer_counter = 0;
|
||||
}
|
||||
|
||||
if(projectile_timer.Check())
|
||||
SpellProjectileEffect();
|
||||
|
||||
if(spellbonuses.GravityEffect == 1) {
|
||||
if(gravity_timer.Check())
|
||||
DoGravityEffect();
|
||||
|
||||
+52
-9
@@ -449,7 +449,11 @@ int command_init(void) {
|
||||
command_add("questerrors", "Shows quest errors.", 100, command_questerrors) ||
|
||||
command_add("enablerecipe", "[recipe_id] - Enables a recipe using the recipe id.", 80, command_enablerecipe) ||
|
||||
command_add("disablerecipe", "[recipe_id] - Disables a recipe using the recipe id.", 80, command_disablerecipe) ||
|
||||
command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache)
|
||||
command_add("npctype_cache", "[id] or all - Clears the npc type cache for either the id or all npcs.", 250, command_npctype_cache) ||
|
||||
command_add("merchant_open_shop", "Opens a merchants shop", 100, command_merchantopenshop) ||
|
||||
command_add("open_shop", nullptr, 100, command_merchantopenshop) ||
|
||||
command_add("merchant_close_shop", "Closes a merchant shop", 100, command_merchantcloseshop) ||
|
||||
command_add("close_shop", nullptr, 100, command_merchantcloseshop)
|
||||
)
|
||||
{
|
||||
command_deinit();
|
||||
@@ -2638,19 +2642,35 @@ void command_makepet(Client *c, const Seperator *sep)
|
||||
void command_level(Client *c, const Seperator *sep)
|
||||
{
|
||||
uint16 level = atoi(sep->arg[1]);
|
||||
if ((level <= 0) || ((level > RuleI(Character, MaxLevel)) && (c->Admin() < commandLevelAboveCap)) )
|
||||
|
||||
if ((level <= 0) || ((level > RuleI(Character, MaxLevel)) && (c->Admin() < commandLevelAboveCap))) {
|
||||
c->Message(0, "Error: #Level: Invalid Level");
|
||||
else if (c->Admin() < 100)
|
||||
}
|
||||
else if (c->Admin() < 100) {
|
||||
c->SetLevel(level, true);
|
||||
else if (!c->GetTarget())
|
||||
#ifdef BOTS
|
||||
if(RuleB(Bots, BotLevelsWithOwner))
|
||||
Bot::LevelBotWithClient(c, level, true);
|
||||
#endif
|
||||
}
|
||||
else if (!c->GetTarget()) {
|
||||
c->Message(0, "Error: #Level: No target");
|
||||
else
|
||||
if (!c->GetTarget()->IsNPC() && ((c->Admin() < commandLevelNPCAboveCap) && (level > RuleI(Character, MaxLevel))))
|
||||
}
|
||||
else {
|
||||
if (!c->GetTarget()->IsNPC() && ((c->Admin() < commandLevelNPCAboveCap) && (level > RuleI(Character, MaxLevel)))) {
|
||||
c->Message(0, "Error: #Level: Invalid Level");
|
||||
else
|
||||
}
|
||||
else {
|
||||
c->GetTarget()->SetLevel(level, true);
|
||||
if(c->GetTarget() && c->GetTarget()->IsClient())
|
||||
c->GetTarget()->CastToClient()->SendLevelAppearance();
|
||||
if(c->GetTarget()->IsClient()) {
|
||||
c->GetTarget()->CastToClient()->SendLevelAppearance();
|
||||
#ifdef BOTS
|
||||
if(RuleB(Bots, BotLevelsWithOwner))
|
||||
Bot::LevelBotWithClient(c->GetTarget()->CastToClient(), level, true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void command_spawn(Client *c, const Seperator *sep)
|
||||
@@ -11465,3 +11485,26 @@ void command_npctype_cache(Client *c, const Seperator *sep)
|
||||
c->Message(0, "#npctype_cache all");
|
||||
}
|
||||
}
|
||||
|
||||
void command_merchantopenshop(Client *c, const Seperator *sep)
|
||||
{
|
||||
Mob *merchant = c->GetTarget();
|
||||
if (!merchant || merchant->GetClass() != MERCHANT) {
|
||||
c->Message(0, "You must target a merchant to open their shop.");
|
||||
return;
|
||||
}
|
||||
|
||||
merchant->CastToNPC()->MerchantOpenShop();
|
||||
}
|
||||
|
||||
void command_merchantcloseshop(Client *c, const Seperator *sep)
|
||||
{
|
||||
Mob *merchant = c->GetTarget();
|
||||
if (!merchant || merchant->GetClass() != MERCHANT) {
|
||||
c->Message(0, "You must target a merchant to close their shop.");
|
||||
return;
|
||||
}
|
||||
|
||||
merchant->CastToNPC()->MerchantCloseShop();
|
||||
}
|
||||
|
||||
|
||||
@@ -324,6 +324,8 @@ void command_enablerecipe(Client *c, const Seperator *sep);
|
||||
void command_disablerecipe(Client *c, const Seperator *sep);
|
||||
void command_showspellslist(Client *c, const Seperator *sep);
|
||||
void command_npctype_cache(Client *c, const Seperator *sep);
|
||||
void command_merchantopenshop(Client *c, const Seperator *sep);
|
||||
void command_merchantcloseshop(Client *c, const Seperator *sep);
|
||||
|
||||
#ifdef EQPROFILE
|
||||
void command_profiledump(Client *c, const Seperator *sep);
|
||||
|
||||
+8
-3
@@ -5,6 +5,7 @@
|
||||
#include "../common/spdat.h"
|
||||
|
||||
#define HIGHEST_RESIST 9 //Max resist type value
|
||||
#define MAX_SPELL_PROJECTILE 10 //Max amount of spell projectiles that can be active by a single mob.
|
||||
|
||||
/* solar: macros for IsAttackAllowed, IsBeneficialAllowed */
|
||||
#define _CLIENT(x) (x && x->IsClient() && !x->CastToClient()->IsBecomeNPC())
|
||||
@@ -162,7 +163,8 @@ struct Buffs_Struct {
|
||||
int32 caston_x;
|
||||
int32 caston_y;
|
||||
int32 caston_z;
|
||||
int32 ExtraDIChance;
|
||||
int32 ExtraDIChance;
|
||||
int16 RootBreakChance; //Not saved to dbase
|
||||
bool persistant_buff;
|
||||
bool client; //True if the caster is a client
|
||||
bool UpdateClient;
|
||||
@@ -339,9 +341,12 @@ struct StatBonuses {
|
||||
bool DivineAura; // invulnerability
|
||||
bool DistanceRemoval; // Check if Cancle if Moved effect is present
|
||||
int16 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid
|
||||
int8 Root[2]; // The lowest buff slot a root can be found. [0] = Bool if has root [1] = buff slot
|
||||
int16 FrenziedDevastation; // base1= AArank(used) base2= chance increase spell criticals + all DD spells 2x mana.
|
||||
//bool AbsorbMagicAtt; // Magic Rune *Need to be implemented for NegateEffect
|
||||
//bool MeleeRune; // Melee Rune *Need to be implemented for NegateEffect
|
||||
uint16 AbsorbMagicAtt[2]; // 0 = magic rune value 1 = buff slot
|
||||
uint16 MeleeRune[2]; // 0 = rune value 1 = buff slot
|
||||
bool NegateIfCombat; // Bool Drop buff if cast or melee
|
||||
int8 Screech; // -1 = Will be blocked if another Screech is +(1)
|
||||
|
||||
// AAs
|
||||
int8 Packrat; //weight reduction for items, 1 point = 10%
|
||||
|
||||
+3
-3
@@ -79,12 +79,12 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch
|
||||
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
|
||||
|
||||
int chance = RuleI(Spells, BaseCritChance);
|
||||
int chance = RuleI(Spells, BaseCritChance); //Wizard base critical chance is 2% (Does not scale with level)
|
||||
chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
|
||||
|
||||
chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
|
||||
|
||||
if (chance > 0){
|
||||
if (chance > 0 || (GetClass() == WIZARD && GetLevel() >= RuleI(Spells, WizCritLevel))) {
|
||||
|
||||
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
|
||||
|
||||
@@ -99,7 +99,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
}
|
||||
|
||||
else if (GetClass() == WIZARD && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (MakeRandomInt(1,100) <= RuleI(Spells, WizCritChance))) {
|
||||
ratio = MakeRandomInt(1,100); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio.
|
||||
ratio += MakeRandomInt(20,70); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. (20-70 is parse confirmed)
|
||||
Critical = true;
|
||||
}
|
||||
|
||||
|
||||
+67
-6
@@ -632,6 +632,37 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32
|
||||
perl->eval(cmd.c_str());
|
||||
|
||||
#ifdef EMBPERL_XS_CLASSES
|
||||
|
||||
{
|
||||
std::string cl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client";
|
||||
std::string np = (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc";
|
||||
std::string qi = (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem";
|
||||
std::string enl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list";
|
||||
if(clear_vars_.find(cl) != clear_vars_.end()) {
|
||||
std::string eval_str = cl;
|
||||
eval_str += " = undef;";
|
||||
perl->eval(eval_str.c_str());
|
||||
}
|
||||
|
||||
if (clear_vars_.find(np) != clear_vars_.end()) {
|
||||
std::string eval_str = np;
|
||||
eval_str += " = undef;";
|
||||
perl->eval(eval_str.c_str());
|
||||
}
|
||||
|
||||
if (clear_vars_.find(qi) != clear_vars_.end()) {
|
||||
std::string eval_str = qi;
|
||||
eval_str += " = undef;";
|
||||
perl->eval(eval_str.c_str());
|
||||
}
|
||||
|
||||
if (clear_vars_.find(enl) != clear_vars_.end()) {
|
||||
std::string eval_str = enl;
|
||||
eval_str += " = undef;";
|
||||
perl->eval(eval_str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
char namebuf[64];
|
||||
|
||||
//init a couple special vars: client, npc, entity_list
|
||||
@@ -670,11 +701,16 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32
|
||||
ret_value = perl->dosub(std::string(pkgprefix).append("::").append(event).c_str());
|
||||
|
||||
#ifdef EMBPERL_XS_CLASSES
|
||||
std::string eval_str = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client = undef;";
|
||||
eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc = undef;";
|
||||
eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem = undef;";
|
||||
eval_str += (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list = undef;";
|
||||
perl->eval(eval_str.c_str());
|
||||
{
|
||||
std::string cl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::client";
|
||||
std::string np = (std::string)"$" + (std::string)pkgprefix + (std::string)"::npc";
|
||||
std::string qi = (std::string)"$" + (std::string)pkgprefix + (std::string)"::questitem";
|
||||
std::string enl = (std::string)"$" + (std::string)pkgprefix + (std::string)"::entity_list";
|
||||
clear_vars_[cl] = 1;
|
||||
clear_vars_[np] = 1;
|
||||
clear_vars_[qi] = 1;
|
||||
clear_vars_[enl] = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
} catch(const char * err) {
|
||||
@@ -687,12 +723,37 @@ int PerlembParser::SendCommands(const char *pkgprefix, const char *event, uint32
|
||||
error += "::";
|
||||
error += event;
|
||||
error += " - ";
|
||||
error += err;
|
||||
if(strlen(err) > 0)
|
||||
error += err;
|
||||
AddError(error);
|
||||
}
|
||||
}
|
||||
|
||||
quest_manager.EndQuest();
|
||||
|
||||
#ifdef EMBPERL_XS_CLASSES
|
||||
if(!quest_manager.QuestsRunning()) {
|
||||
auto iter = clear_vars_.begin();
|
||||
std::string eval_str;
|
||||
while(iter != clear_vars_.end()) {
|
||||
eval_str += iter->first;
|
||||
eval_str += " = undef;";
|
||||
++iter;
|
||||
}
|
||||
clear_vars_.clear();
|
||||
|
||||
try {
|
||||
perl->eval(eval_str.c_str());
|
||||
}
|
||||
catch (const char * err) {
|
||||
std::string error = "Script clear error: ";
|
||||
if (strlen(err) > 0)
|
||||
error += err;
|
||||
AddError(error);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ private:
|
||||
|
||||
std::map<std::string, std::string> vars_;
|
||||
SV *_empty_sv;
|
||||
std::map<std::string, int> clear_vars_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1678,14 +1678,15 @@ XS(XS__toggle_spawn_event);
|
||||
XS(XS__toggle_spawn_event)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 2)
|
||||
Perl_croak(aTHX_ "Usage: toggle_spawn_event(event_id, enabled?, reset_base)");
|
||||
if (items != 4)
|
||||
Perl_croak(aTHX_ "Usage: toggle_spawn_event(event_id, enabled?, strict, reset_base)");
|
||||
|
||||
uint32 event_id = (int)SvIV(ST(0));
|
||||
bool enabled = ((int)SvIV(ST(1))) == 0?false:true;
|
||||
bool reset_base = ((int)SvIV(ST(1))) == 0?false:true;
|
||||
bool strict = ((int)SvIV(ST(2))) == 0?false:true;
|
||||
bool reset_base = ((int)SvIV(ST(3))) == 0?false:true;
|
||||
|
||||
quest_manager.toggle_spawn_event(event_id, enabled, reset_base);
|
||||
quest_manager.toggle_spawn_event(event_id, enabled, strict, reset_base);
|
||||
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
+26
-24
@@ -3016,6 +3016,7 @@ void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 thedam)
|
||||
{
|
||||
NPC *cur = nullptr;
|
||||
uint16 count = 0;
|
||||
std::list<NPC *> npc_sub_list;
|
||||
auto it = npc_list.begin();
|
||||
while (it != npc_list.end()) {
|
||||
cur = it->second;
|
||||
@@ -3025,11 +3026,13 @@ void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 thedam)
|
||||
continue;
|
||||
}
|
||||
if (!cur->IsMezzed() && !cur->IsStunned() && !cur->IsFeared()) {
|
||||
npc_sub_list.push_back(cur);
|
||||
++count;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
|
||||
if (thedam > 1) {
|
||||
if (count > 0)
|
||||
thedam /= count;
|
||||
@@ -3039,32 +3042,26 @@ void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 thedam)
|
||||
}
|
||||
|
||||
cur = nullptr;
|
||||
it = npc_list.begin();
|
||||
while (it != npc_list.end()) {
|
||||
cur = it->second;
|
||||
if (!cur->CheckAggro(target)) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
auto sit = npc_sub_list.begin();
|
||||
while (sit != npc_sub_list.end()) {
|
||||
cur = *sit;
|
||||
|
||||
if (!cur->IsMezzed() && !cur->IsStunned() && !cur->IsFeared()) {
|
||||
if (cur->IsPet()) {
|
||||
if (caster) {
|
||||
if (cur->CheckAggro(caster)) {
|
||||
cur->AddToHateList(caster, thedam);
|
||||
}
|
||||
if (cur->IsPet()) {
|
||||
if (caster) {
|
||||
if (cur->CheckAggro(caster)) {
|
||||
cur->AddToHateList(caster, thedam);
|
||||
}
|
||||
} else {
|
||||
if (caster) {
|
||||
if (cur->CheckAggro(caster)) {
|
||||
cur->AddToHateList(caster, thedam);
|
||||
} else {
|
||||
cur->AddToHateList(caster, thedam * 0.33);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (caster) {
|
||||
if (cur->CheckAggro(caster)) {
|
||||
cur->AddToHateList(caster, thedam);
|
||||
} else {
|
||||
cur->AddToHateList(caster, thedam * 0.33);
|
||||
}
|
||||
}
|
||||
}
|
||||
++it;
|
||||
++sit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3418,9 +3415,14 @@ void EntityList::ReloadAllClientsTaskState(int TaskID)
|
||||
|
||||
bool EntityList::IsMobInZone(Mob *who)
|
||||
{
|
||||
auto it = mob_list.find(who->GetID());
|
||||
if (it != mob_list.end())
|
||||
return who == it->second;
|
||||
//We don't use mob_list.find(who) because this code needs to be able to handle dangling pointers for the quest code.
|
||||
auto it = mob_list.begin();
|
||||
while(it != mob_list.end()) {
|
||||
if(it->second == who) {
|
||||
return true;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -326,7 +326,18 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
|
||||
}
|
||||
else
|
||||
Message(15, "Welcome to level %i!", check_level);
|
||||
|
||||
#ifdef BOTS
|
||||
uint8 myoldlevel = GetLevel();
|
||||
#endif
|
||||
|
||||
SetLevel(check_level);
|
||||
|
||||
#ifdef BOTS
|
||||
if(RuleB(Bots, BotLevelsWithOwner))
|
||||
// hack way of doing this..but, least invasive... (same criteria as gain level for sendlvlapp)
|
||||
Bot::LevelBotWithClient(this, GetLevel(), (myoldlevel==check_level-1));
|
||||
#endif
|
||||
}
|
||||
|
||||
//If were at max level then stop gaining experience if we make it to the cap
|
||||
|
||||
+27
-8
@@ -60,14 +60,16 @@ void HateList::Wipe()
|
||||
while(iterator != list.end())
|
||||
{
|
||||
Mob* m = (*iterator)->ent;
|
||||
parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), m, "0", 0);
|
||||
//iterator
|
||||
if(m)
|
||||
{
|
||||
parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), m, "0", 0);
|
||||
|
||||
if(m->IsClient())
|
||||
m->CastToClient()->DecrementAggroCount();
|
||||
}
|
||||
delete (*iterator);
|
||||
iterator = list.erase(iterator);
|
||||
|
||||
if(m->IsClient())
|
||||
m->CastToClient()->DecrementAggroCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +205,9 @@ void HateList::Add(Mob *ent, int32 in_hate, int32 in_dam, bool bFrenzy, bool iAd
|
||||
|
||||
bool HateList::RemoveEnt(Mob *ent)
|
||||
{
|
||||
if (!ent)
|
||||
return false;
|
||||
|
||||
bool found = false;
|
||||
auto iterator = list.begin();
|
||||
|
||||
@@ -210,15 +215,18 @@ bool HateList::RemoveEnt(Mob *ent)
|
||||
{
|
||||
if((*iterator)->ent == ent)
|
||||
{
|
||||
if(ent)
|
||||
parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), ent, "0", 0);
|
||||
delete (*iterator);
|
||||
iterator = list.erase(iterator);
|
||||
found = true;
|
||||
|
||||
if(ent->IsClient())
|
||||
|
||||
if(ent && ent->IsClient())
|
||||
ent->CastToClient()->DecrementAggroCount();
|
||||
|
||||
}
|
||||
delete (*iterator);
|
||||
iterator = list.erase(iterator);
|
||||
|
||||
}
|
||||
else
|
||||
++iterator;
|
||||
}
|
||||
@@ -445,6 +453,17 @@ Mob *HateList::GetMostHate(){
|
||||
Mob *HateList::GetRandom()
|
||||
{
|
||||
int count = list.size();
|
||||
if(count == 0) //If we don't have any entries it'll crash getting a random 0, -1 position.
|
||||
return NULL;
|
||||
|
||||
if(count == 1) //No need to do all that extra work if we only have one hate entry
|
||||
{
|
||||
if(*list.begin()) // Just in case tHateEntry is invalidated somehow...
|
||||
return (*list.begin())->ent;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto iterator = list.begin();
|
||||
int random = MakeRandomInt(0, count - 1);
|
||||
for (int i = 0; i < random; i++)
|
||||
|
||||
+384
-109
@@ -200,124 +200,399 @@ bool Client::CheckLoreConflict(const Item_Struct* item) {
|
||||
return (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != SLOT_INVALID);
|
||||
}
|
||||
|
||||
void Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) {
|
||||
bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) {
|
||||
// TODO: update calling methods and script apis to handle a failure return
|
||||
|
||||
const Item_Struct* item = database.GetItem(item_id);
|
||||
|
||||
if (item == nullptr) {
|
||||
Message(0, "No such item: %i", item_id);
|
||||
return;
|
||||
} else {
|
||||
// if the item is stackable and the charge amount is -1 or 0 then set to 1 charge.
|
||||
// removed && item->MaxCharges == 0 if -1 or 0 was passed max charges is irrelevant
|
||||
if (charges <= 0 && item->Stackable) {
|
||||
charges = 1;
|
||||
// if the charges is -1, then no charge value was passed in set to max charges
|
||||
} else if(charges == -1) {
|
||||
charges = item->MaxCharges;
|
||||
// in any other situation just use charges as passed
|
||||
}
|
||||
// make sure the item exists
|
||||
if(item == nullptr) {
|
||||
Message(13, "Item %u does not exist.", item_id);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item_id, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
// Checking to see if the Item is lore or not.
|
||||
bool foundlore = CheckLoreConflict(item);
|
||||
// check that there is not a lore conflict between base item and existing inventory
|
||||
else if(CheckLoreConflict(item)) {
|
||||
// DuplicateLoreMessage(item_id);
|
||||
Message(13, "You already have a lore %s (%i) in your inventory.", item->Name, item_id);
|
||||
|
||||
//TODO: check for lore conflict on augments
|
||||
return false;
|
||||
}
|
||||
// check to make sure we are augmenting an augmentable item
|
||||
else if(((item->ItemClass != ItemClassCommon) || (item->AugType > 0)) && (aug1 | aug2 | aug3 | aug4 | aug5)) {
|
||||
Message(13, "You can not augment an augment or a non-common class item.");
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an augment or a non-common class item.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
// Checking to see if it is a GM only Item or not.
|
||||
//bool foundgm = (item->gm && (this->Admin() < 100));
|
||||
bool foundgm = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!foundlore && !foundgm) { // Okay, It isn't LORE, or if it is, it is not in player's inventory.
|
||||
ItemInst* inst = database.CreateItem(item, charges);
|
||||
if (inst) {
|
||||
// Corrected the augment references to reflect augment name/id instead of base item name/id
|
||||
if (aug1) {
|
||||
const Item_Struct* augitem1 = database.GetItem(aug1);
|
||||
if (augitem1) {
|
||||
if (!CheckLoreConflict(augitem1)) {
|
||||
inst->PutAugment(&database, 0, aug1);
|
||||
}
|
||||
else {
|
||||
Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem1->Name, aug1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aug2) {
|
||||
const Item_Struct* augitem2 = database.GetItem(aug2);
|
||||
if (augitem2) {
|
||||
if (!CheckLoreConflict(augitem2)) {
|
||||
inst->PutAugment(&database, 1, aug2);
|
||||
}
|
||||
else {
|
||||
Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem2->Name, aug2);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aug3) {
|
||||
const Item_Struct* augitem3 = database.GetItem(aug3);
|
||||
if (augitem3) {
|
||||
if (!CheckLoreConflict(augitem3)) {
|
||||
inst->PutAugment(&database, 2, aug3);
|
||||
}
|
||||
else {
|
||||
Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem3->Name, aug3);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aug4) {
|
||||
const Item_Struct* augitem4 = database.GetItem(aug4);
|
||||
if (augitem4) {
|
||||
if (!CheckLoreConflict(augitem4)) {
|
||||
inst->PutAugment(&database, 3, aug4);
|
||||
}
|
||||
else {
|
||||
Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem4->Name, aug4);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (aug5) {
|
||||
const Item_Struct* augitem5 = database.GetItem(aug5);
|
||||
if (augitem5) {
|
||||
if (!CheckLoreConflict(augitem5)) {
|
||||
inst->PutAugment(&database, 4, aug5);
|
||||
}
|
||||
else {
|
||||
Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem5->Name, aug5);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (attuned) {
|
||||
if (inst->GetItem()->Attuneable) {
|
||||
inst->SetInstNoDrop(true);
|
||||
}
|
||||
}
|
||||
if (to_slot == SLOT_CURSOR)
|
||||
{
|
||||
//inst->SetCharges(
|
||||
PushItemOnCursor(*inst);
|
||||
// Send item packet to user
|
||||
SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
PutItemInInventory(to_slot, *inst, true);
|
||||
}
|
||||
safe_delete(inst);
|
||||
// This code is ready to implement once the item load code is changed to process the 'minstatus' field.
|
||||
// Checking #iteminfo in-game verfies that item->MinStatus is set to '0' regardless of field value.
|
||||
// An optional sql script will also need to be added, once this goes live, to allow changing of the min status.
|
||||
|
||||
if ((RuleB(Character, EnableDiscoveredItems)))
|
||||
{
|
||||
if(!GetGM() && !IsDiscovered(item_id))
|
||||
DiscoverItem(item_id);
|
||||
// check to make sure we are a GM if the item is GM-only
|
||||
/*
|
||||
else if(item->MinStatus && ((this->Admin() < item->MinStatus) || (this->Admin() < RuleI(GM, MinStatusToSummonItem)))) {
|
||||
Message(13, "You are not a GM or do not have the status to summon this item.");
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only item with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, MinStatus: %u)\n",
|
||||
GetName(), account_name, this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5, item->MinStatus);
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
uint32 augments[MAX_AUGMENT_SLOTS] = { aug1, aug2, aug3, aug4, aug5 };
|
||||
|
||||
uint32 classes = item->Classes;
|
||||
uint32 races = item->Races;
|
||||
uint32 slots = item->Slots;
|
||||
|
||||
bool enforcewear = RuleB(Inventory, EnforceAugmentWear);
|
||||
bool enforcerestr = RuleB(Inventory, EnforceAugmentRestriction);
|
||||
bool enforceusable = RuleB(Inventory, EnforceAugmentUsability);
|
||||
|
||||
for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) {
|
||||
const Item_Struct* augtest = database.GetItem(augments[iter]);
|
||||
|
||||
if(augtest == nullptr) {
|
||||
if(augments[iter]) {
|
||||
Message(13, "Augment %u (Aug%i) does not exist.", augments[iter], iter + 1);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an augment (Aug%i) with an invalid id.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// check that there is not a lore conflict between augment and existing inventory
|
||||
if(CheckLoreConflict(augtest)) {
|
||||
// DuplicateLoreMessage(augtest->ID);
|
||||
Message(13, "You already have a lore %s (%u) in your inventory.", augtest->Name, augtest->ID);
|
||||
|
||||
return false;
|
||||
}
|
||||
// check that augment is an actual augment
|
||||
else if(augtest->AugType == 0) {
|
||||
Message(13, "%s (%u) (Aug%i) is not an actual augment.", augtest->Name, augtest->ID, iter + 1);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to use a non-augment item (Aug%i) as an augment.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, (iter + 1), aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Same as GM check above
|
||||
|
||||
// check to make sure we are a GM if the augment is GM-only
|
||||
/*
|
||||
else if(augtest->MinStatus && ((this->Admin() < augtest->MinStatus) || (this->Admin() < RuleI(GM, MinStatusToSummonItem)))) {
|
||||
Message(13, "You are not a GM or do not have the status to summon this augment.");
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create a GM-only augment (Aug%i) with a status of %i.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u, MinStatus: %u)\n",
|
||||
GetName(), account_name, (iter + 1), this->Admin(), item->ID, aug1, aug2, aug3, aug4, aug5, item->MinStatus);
|
||||
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
// check for augment type allowance
|
||||
if(enforcewear) {
|
||||
if((item->AugSlotType[iter] == AugTypeNone) || !(((uint32)1 << (item->AugSlotType[iter] - 1)) & augtest->AugType)) {
|
||||
Message(13, "Augment %u (Aug%i) is not acceptable wear on Item %u.", augments[iter], iter + 1, item->ID);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an item with an unacceptable augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(item->AugSlotVisible[iter] == 0) {
|
||||
Message(13, "Item %u has not evolved enough to accept Augment %u (Aug%i).", item->ID, augments[iter], iter + 1);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an unevolved item with augment type (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check for augment to item restriction
|
||||
if(enforcerestr) {
|
||||
bool restrictfail = false;
|
||||
uint8 it = item->ItemType;
|
||||
|
||||
switch(augtest->AugRestrict) {
|
||||
case AugRestrAny:
|
||||
break;
|
||||
case AugRestrArmor:
|
||||
switch(it) {
|
||||
case ItemTypeArmor:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestrWeapons:
|
||||
switch(it) {
|
||||
case ItemType1HSlash:
|
||||
case ItemType1HBlunt:
|
||||
case ItemType1HPiercing:
|
||||
case ItemTypeMartial:
|
||||
case ItemType2HSlash:
|
||||
case ItemType2HBlunt:
|
||||
case ItemType2HPiercing:
|
||||
case ItemTypeBow:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr1HWeapons:
|
||||
switch(it) {
|
||||
case ItemType1HSlash:
|
||||
case ItemType1HBlunt:
|
||||
case ItemType1HPiercing:
|
||||
case ItemTypeMartial:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr2HWeapons:
|
||||
switch(it) {
|
||||
case ItemType2HSlash:
|
||||
case ItemType2HBlunt:
|
||||
case ItemType2HPiercing:
|
||||
case ItemTypeBow:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr1HSlash:
|
||||
switch(it) {
|
||||
case ItemType1HSlash:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr1HBlunt:
|
||||
switch(it) {
|
||||
case ItemType1HBlunt:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestrPiercing:
|
||||
switch(it) {
|
||||
case ItemType1HPiercing:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestrHandToHand:
|
||||
switch(it) {
|
||||
case ItemTypeMartial:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr2HSlash:
|
||||
switch(it) {
|
||||
case ItemType2HSlash:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr2HBlunt:
|
||||
switch(it) {
|
||||
case ItemType2HBlunt:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr2HPierce:
|
||||
switch(it) {
|
||||
case ItemType2HPiercing:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestrBows:
|
||||
switch(it) {
|
||||
case ItemTypeBow:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestrShields:
|
||||
switch(it) {
|
||||
case ItemTypeShield:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr1HSlash1HBluntOrHandToHand:
|
||||
switch(it) {
|
||||
case ItemType1HSlash:
|
||||
case ItemType1HBlunt:
|
||||
case ItemTypeMartial:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AugRestr1HBluntOrHandToHand:
|
||||
switch(it) {
|
||||
case ItemType1HBlunt:
|
||||
case ItemTypeMartial:
|
||||
break;
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// These 3 are in-work
|
||||
case AugRestrUnknown1:
|
||||
case AugRestrUnknown2:
|
||||
case AugRestrUnknown3:
|
||||
default:
|
||||
restrictfail = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if(restrictfail) {
|
||||
Message(13, "Augment %u (Aug%i) is restricted from wear on Item %u.", augments[iter], (iter + 1), item->ID);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to augment an item with a restricted augment (Aug%i).\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, (iter + 1), item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(enforceusable) {
|
||||
// check for class usability
|
||||
if(item->Classes && !(classes &= augtest->Classes)) {
|
||||
Message(13, "Augment %u (Aug%i) will result in an item not usable by any class.", augments[iter], (iter + 1));
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item unusable by any class.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for race usability
|
||||
if(item->Races && !(races &= augtest->Races)) {
|
||||
Message(13, "Augment %u (Aug%i) will result in an item not usable by any race.", augments[iter], (iter + 1));
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item unusable by any race.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for slot usability
|
||||
if(item->Slots && !(slots &= augtest->Slots)) {
|
||||
Message(13, "Augment %u (Aug%i) will result in an item not usable in any slot.", augments[iter], (iter + 1));
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to create an item unusable in any slot.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // Item was already in inventory & is a LORE item or was a GM only item. Give them a message about it.
|
||||
if (foundlore){
|
||||
DuplicateLoreMessage(item_id);
|
||||
//Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
|
||||
}
|
||||
else if (foundgm)
|
||||
Message(0, "You are not a GM to summon this item");
|
||||
|
||||
// validation passed..so, set the charges and create the actual item
|
||||
|
||||
// if the item is stackable and the charge amount is -1 or 0 then set to 1 charge.
|
||||
// removed && item->MaxCharges == 0 if -1 or 0 was passed max charges is irrelevant
|
||||
if(charges <= 0 && item->Stackable)
|
||||
charges = 1;
|
||||
|
||||
// if the charges is -1, then no charge value was passed in set to max charges
|
||||
else if(charges == -1)
|
||||
charges = item->MaxCharges;
|
||||
|
||||
// in any other situation just use charges as passed
|
||||
|
||||
ItemInst* inst = database.CreateItem(item, charges);
|
||||
|
||||
if(inst == nullptr) {
|
||||
Message(13, "An unknown server error has occurred and your item was not created.");
|
||||
// this goes to logfile since this is a major error
|
||||
LogFile->write(EQEMuLog::Error, "Player %s on account %s encountered an unknown item creation error.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// add any validated augments
|
||||
for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) {
|
||||
if(augments[iter])
|
||||
inst->PutAugment(&database, iter, augments[iter]);
|
||||
}
|
||||
|
||||
// attune item
|
||||
if(attuned && inst->GetItem()->Attuneable)
|
||||
inst->SetInstNoDrop(true);
|
||||
|
||||
// check to see if item is usable in requested slot
|
||||
if(enforceusable && (((to_slot >= 0) && (to_slot <= 21)) || (to_slot == 9999))) {
|
||||
uint32 slottest = (to_slot == 9999) ? 22 : to_slot;
|
||||
|
||||
if(!(slots & ((uint32)1 << slottest))) {
|
||||
Message(0, "This item is not equipable at slot %u - moving to cursor.", to_slot);
|
||||
mlog(INVENTORY__ERROR, "Player %s on account %s attempted to equip an item unusable in slot %u - moved to cursor.\n(Item: %u, Aug1: %u, Aug2: %u, Aug3: %u, Aug4: %u, Aug5: %u)\n",
|
||||
GetName(), account_name, to_slot, item->ID, aug1, aug2, aug3, aug4, aug5);
|
||||
|
||||
to_slot = SLOT_CURSOR;
|
||||
}
|
||||
}
|
||||
|
||||
// put item into inventory
|
||||
if(to_slot == SLOT_CURSOR) {
|
||||
PushItemOnCursor(*inst);
|
||||
SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem);
|
||||
}
|
||||
else {
|
||||
PutItemInInventory(to_slot, *inst, true);
|
||||
}
|
||||
|
||||
safe_delete(inst);
|
||||
|
||||
// discover item and any augments
|
||||
if((RuleB(Character, EnableDiscoveredItems)) && !GetGM()) {
|
||||
if(!IsDiscovered(item_id))
|
||||
DiscoverItem(item_id);
|
||||
|
||||
for(int iter = 0; iter < MAX_AUGMENT_SLOTS; ++iter) {
|
||||
if(augments[iter] && !IsDiscovered(augments[iter]))
|
||||
DiscoverItem(augments[iter]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Drop item from inventory to ground (generally only dropped from SLOT_CURSOR)
|
||||
@@ -1120,7 +1395,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
|
||||
//verify shared bank transactions in the database
|
||||
if(src_inst && src_slot_id >= 2500 && src_slot_id <= 2550) {
|
||||
if(!database.VerifyInventory(account_id, src_slot_id, src_inst)) {
|
||||
LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploiting the shared bank.\n", account_name, GetName());
|
||||
LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploiting the shared bank.\n", GetName(), account_name);
|
||||
DeleteItemInInventory(dst_slot_id,0,true);
|
||||
return(false);
|
||||
}
|
||||
@@ -1135,7 +1410,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
|
||||
}
|
||||
if(dst_inst && dst_slot_id >= 2500 && dst_slot_id <= 2550) {
|
||||
if(!database.VerifyInventory(account_id, dst_slot_id, dst_inst)) {
|
||||
LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploting the shared bank.\n", account_name, GetName());
|
||||
LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploting the shared bank.\n", GetName(), account_name);
|
||||
DeleteItemInInventory(src_slot_id,0,true);
|
||||
return(false);
|
||||
}
|
||||
|
||||
+45
-5
@@ -168,14 +168,38 @@ void lua_set_timer(const char *timer, int time_ms) {
|
||||
quest_manager.settimerMS(timer, time_ms);
|
||||
}
|
||||
|
||||
void lua_set_timer(const char *timer, int time_ms, Lua_ItemInst inst) {
|
||||
quest_manager.settimerMS(timer, time_ms, inst);
|
||||
}
|
||||
|
||||
void lua_set_timer(const char *timer, int time_ms, Lua_Mob mob) {
|
||||
quest_manager.settimerMS(timer, time_ms, mob);
|
||||
}
|
||||
|
||||
void lua_stop_timer(const char *timer) {
|
||||
quest_manager.stoptimer(timer);
|
||||
}
|
||||
|
||||
void lua_stop_timer(const char *timer, Lua_ItemInst inst) {
|
||||
quest_manager.stoptimer(timer, inst);
|
||||
}
|
||||
|
||||
void lua_stop_timer(const char *timer, Lua_Mob mob) {
|
||||
quest_manager.stoptimer(timer, mob);
|
||||
}
|
||||
|
||||
void lua_stop_all_timers() {
|
||||
quest_manager.stopalltimers();
|
||||
}
|
||||
|
||||
void lua_stop_all_timers(Lua_ItemInst inst) {
|
||||
quest_manager.stopalltimers(inst);
|
||||
}
|
||||
|
||||
void lua_stop_all_timers(Lua_Mob mob) {
|
||||
quest_manager.stopalltimers(mob);
|
||||
}
|
||||
|
||||
void lua_depop() {
|
||||
quest_manager.depop(0);
|
||||
}
|
||||
@@ -348,8 +372,8 @@ int lua_get_spawn_condition(const char *zone, uint32 instance_id, int condition_
|
||||
return quest_manager.get_spawn_condition(zone, instance_id, condition_id);
|
||||
}
|
||||
|
||||
void lua_toggle_spawn_event(int event_id, bool enable, bool reset) {
|
||||
quest_manager.toggle_spawn_event(event_id, enable, reset);
|
||||
void lua_toggle_spawn_event(int event_id, bool enable, bool strict, bool reset) {
|
||||
quest_manager.toggle_spawn_event(event_id, enable, strict, reset);
|
||||
}
|
||||
|
||||
void lua_summon_burried_player_corpse(uint32 char_id, float x, float y, float z, float h) {
|
||||
@@ -659,6 +683,14 @@ void lua_assign_raid_to_instance(uint32 instance_id) {
|
||||
quest_manager.AssignRaidToInstance(instance_id);
|
||||
}
|
||||
|
||||
void lua_remove_from_instance(uint32 instance_id) {
|
||||
quest_manager.RemoveFromInstance(instance_id);
|
||||
}
|
||||
|
||||
void lua_remove_all_from_instance(uint32 instance_id) {
|
||||
quest_manager.RemoveAllFromInstance(instance_id);
|
||||
}
|
||||
|
||||
void lua_flag_instance_by_group_leader(uint32 zone, uint32 version) {
|
||||
quest_manager.FlagInstanceByGroupLeader(zone, version);
|
||||
}
|
||||
@@ -1091,9 +1123,15 @@ luabind::scope lua_register_general() {
|
||||
luabind::def("spawn_from_spawn2", (Lua_Mob(*)(uint32))&lua_spawn_from_spawn2),
|
||||
luabind::def("enable_spawn2", &lua_enable_spawn2),
|
||||
luabind::def("disable_spawn2", &lua_disable_spawn2),
|
||||
luabind::def("set_timer", &lua_set_timer),
|
||||
luabind::def("stop_timer", &lua_stop_timer),
|
||||
luabind::def("stop_all_timers", &lua_stop_all_timers),
|
||||
luabind::def("set_timer", (void(*)(const char*, int))&lua_set_timer),
|
||||
luabind::def("set_timer", (void(*)(const char*, int, Lua_ItemInst))&lua_set_timer),
|
||||
luabind::def("set_timer", (void(*)(const char*, int, Lua_Mob))&lua_set_timer),
|
||||
luabind::def("stop_timer", (void(*)(const char*))&lua_stop_timer),
|
||||
luabind::def("stop_timer", (void(*)(const char*, Lua_ItemInst))&lua_stop_timer),
|
||||
luabind::def("stop_timer", (void(*)(const char*, Lua_Mob))&lua_stop_timer),
|
||||
luabind::def("stop_all_timers", (void(*)(void))&lua_stop_all_timers),
|
||||
luabind::def("stop_all_timers", (void(*)(Lua_ItemInst))&lua_stop_all_timers),
|
||||
luabind::def("stop_all_timers", (void(*)(Lua_Mob))&lua_stop_all_timers),
|
||||
luabind::def("depop", (void(*)(void))&lua_depop),
|
||||
luabind::def("depop", (void(*)(int))&lua_depop),
|
||||
luabind::def("depop_with_timer", (void(*)(void))&lua_depop_with_timer),
|
||||
@@ -1195,6 +1233,8 @@ luabind::scope lua_register_general() {
|
||||
luabind::def("assign_to_instance", &lua_assign_to_instance),
|
||||
luabind::def("assign_group_to_instance", &lua_assign_group_to_instance),
|
||||
luabind::def("assign_raid_to_instance", &lua_assign_raid_to_instance),
|
||||
luabind::def("remove_from_instance", &lua_remove_from_instance),
|
||||
luabind::def("remove_all_from_instance", &lua_remove_all_from_instance),
|
||||
luabind::def("flag_instance_by_group_leader", &lua_flag_instance_by_group_leader),
|
||||
luabind::def("flag_instance_by_raid_leader", &lua_flag_instance_by_raid_leader),
|
||||
luabind::def("fly_mode", &lua_fly_mode),
|
||||
|
||||
+56
-1
@@ -1816,6 +1816,52 @@ void Lua_Mob::SetDestructibleObject(bool set) {
|
||||
self->SetDestructibleObject(set);
|
||||
}
|
||||
|
||||
bool Lua_Mob::IsImmuneToSpell(int spell_id, Lua_Mob caster) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->IsImmuneToSpell(spell_id, caster);
|
||||
}
|
||||
|
||||
void Lua_Mob::BuffFadeBySpellID(int spell_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->BuffFadeBySpellID(spell_id);
|
||||
}
|
||||
|
||||
void Lua_Mob::BuffFadeByEffect(int effect_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->BuffFadeByEffect(effect_id);
|
||||
}
|
||||
|
||||
void Lua_Mob::BuffFadeByEffect(int effect_id, int skipslot) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->BuffFadeByEffect(effect_id, skipslot);
|
||||
}
|
||||
|
||||
void Lua_Mob::BuffFadeAll() {
|
||||
Lua_Safe_Call_Void();
|
||||
self->BuffFadeAll();
|
||||
}
|
||||
|
||||
void Lua_Mob::BuffFadeBySlot(int slot) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->BuffFadeBySlot(slot);
|
||||
}
|
||||
|
||||
void Lua_Mob::BuffFadeBySlot(int slot, bool recalc_bonuses) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->BuffFadeBySlot(slot, recalc_bonuses);
|
||||
}
|
||||
|
||||
int Lua_Mob::CanBuffStack(int spell_id, int caster_level) {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->CanBuffStack(spell_id, caster_level);
|
||||
}
|
||||
|
||||
int Lua_Mob::CanBuffStack(int spell_id, int caster_level, bool fail_if_overwrite) {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->CanBuffStack(spell_id, caster_level, fail_if_overwrite);
|
||||
}
|
||||
|
||||
|
||||
luabind::scope lua_register_mob() {
|
||||
return luabind::class_<Lua_Mob, Lua_Entity>("Mob")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -2125,7 +2171,16 @@ luabind::scope lua_register_mob() {
|
||||
.def("ProcessSpecialAbilities", (void(Lua_Mob::*)(std::string))&Lua_Mob::ProcessSpecialAbilities)
|
||||
.def("SetAppearance", (void(Lua_Mob::*)(int))&Lua_Mob::SetAppearance)
|
||||
.def("SetAppearance", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetAppearance)
|
||||
.def("SetDestructibleObject", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDestructibleObject);
|
||||
.def("SetDestructibleObject", (void(Lua_Mob::*)(bool))&Lua_Mob::SetDestructibleObject)
|
||||
.def("IsImmuneToSpell", (bool(Lua_Mob::*)(int,Lua_Mob))&Lua_Mob::IsImmuneToSpell)
|
||||
.def("BuffFadeBySpellID", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySpellID)
|
||||
.def("BuffFadeByEffect", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeByEffect)
|
||||
.def("BuffFadeByEffect", (void(Lua_Mob::*)(int,int))&Lua_Mob::BuffFadeByEffect)
|
||||
.def("BuffFadeAll", (void(Lua_Mob::*)(void))&Lua_Mob::BuffFadeAll)
|
||||
.def("BuffFadeBySlot", (void(Lua_Mob::*)(int))&Lua_Mob::BuffFadeBySlot)
|
||||
.def("BuffFadeBySlot", (void(Lua_Mob::*)(int,bool))&Lua_Mob::BuffFadeBySlot)
|
||||
.def("CanBuffStack", (int(Lua_Mob::*)(int,int))&Lua_Mob::CanBuffStack)
|
||||
.def("CanBuffStack", (int(Lua_Mob::*)(int,int,bool))&Lua_Mob::CanBuffStack);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_special_abilities() {
|
||||
|
||||
@@ -345,6 +345,15 @@ public:
|
||||
void SetAppearance(int app);
|
||||
void SetAppearance(int app, bool ignore_self);
|
||||
void SetDestructibleObject(bool set);
|
||||
bool IsImmuneToSpell(int spell_id, Lua_Mob caster);
|
||||
void BuffFadeBySpellID(int spell_id);
|
||||
void BuffFadeByEffect(int effect_id);
|
||||
void BuffFadeByEffect(int effect_id, int skipslot);
|
||||
void BuffFadeAll();
|
||||
void BuffFadeBySlot(int slot);
|
||||
void BuffFadeBySlot(int slot, bool recalc_bonuses);
|
||||
int CanBuffStack(int spell_id, int caster_level);
|
||||
int CanBuffStack(int spell_id, int caster_level, bool fail_if_overwrite);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+15
-3
@@ -408,7 +408,7 @@ void Lua_NPC::SetSpellFocusHeal(int focus) {
|
||||
}
|
||||
|
||||
float Lua_NPC::GetSlowMitigation() {
|
||||
Lua_Safe_Call_Real();
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetSlowMitigation();
|
||||
}
|
||||
|
||||
@@ -432,6 +432,16 @@ int Lua_NPC::GetScore() {
|
||||
return self->GetScore();
|
||||
}
|
||||
|
||||
void Lua_NPC::MerchantOpenShop() {
|
||||
Lua_Safe_Call_Void();
|
||||
self->MerchantOpenShop();
|
||||
}
|
||||
|
||||
void Lua_NPC::MerchantCloseShop() {
|
||||
Lua_Safe_Call_Void();
|
||||
self->MerchantCloseShop();
|
||||
}
|
||||
|
||||
|
||||
luabind::scope lua_register_npc() {
|
||||
return luabind::class_<Lua_NPC, Lua_Mob>("NPC")
|
||||
@@ -516,11 +526,13 @@ luabind::scope lua_register_npc() {
|
||||
.def("RemoveAISpell", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpell)
|
||||
.def("SetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusDMG)
|
||||
.def("SetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusHeal)
|
||||
.def("GetSlowMitigation", (float(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation)
|
||||
.def("GetSlowMitigation", (int(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation)
|
||||
.def("GetAttackSpeed", (float(Lua_NPC::*)(void))&Lua_NPC::GetAttackSpeed)
|
||||
.def("GetAccuracyRating", (int(Lua_NPC::*)(void))&Lua_NPC::GetAccuracyRating)
|
||||
.def("GetSpawnKillCount", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnKillCount)
|
||||
.def("GetScore", (int(Lua_NPC::*)(void))&Lua_NPC::GetScore);
|
||||
.def("GetScore", (int(Lua_NPC::*)(void))&Lua_NPC::GetScore)
|
||||
.def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop)
|
||||
.def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+3
-1
@@ -112,7 +112,9 @@ public:
|
||||
int GetAccuracyRating();
|
||||
int GetSpawnKillCount();
|
||||
int GetScore();
|
||||
void MerchantOpenShop();
|
||||
void MerchantCloseShop();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+94
-51
@@ -276,6 +276,14 @@ Mob::Mob(const char* in_name,
|
||||
casting_spell_inventory_slot = 0;
|
||||
target = 0;
|
||||
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_spell_id[i] = 0; }
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_target_id[i] = 0; }
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_increment[i] = 0; }
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_x[i] = 0; }
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_y[i] = 0; }
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_z[i] = 0; }
|
||||
projectile_timer.Disable();
|
||||
|
||||
memset(&itembonuses, 0, sizeof(StatBonuses));
|
||||
memset(&spellbonuses, 0, sizeof(StatBonuses));
|
||||
memset(&aabonuses, 0, sizeof(StatBonuses));
|
||||
@@ -349,12 +357,6 @@ Mob::Mob(const char* in_name,
|
||||
nextinchpevent = -1;
|
||||
|
||||
TempPets(false);
|
||||
SetHasRune(false);
|
||||
SetHasSpellRune(false);
|
||||
SetHasPartialMeleeRune(false);
|
||||
SetHasPartialSpellRune(false);
|
||||
|
||||
m_hasDeathSaveChance = false;
|
||||
|
||||
m_is_running = false;
|
||||
|
||||
@@ -889,7 +891,8 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
||||
|
||||
ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players
|
||||
ns->spawn.NPC = IsClient() ? 0 : 1;
|
||||
ns->spawn.IsMercenary = IsMerc() ? 1 : 0;
|
||||
ns->spawn.IsMercenary = (IsMerc() || no_target_hotkey) ? 1 : 0;
|
||||
|
||||
ns->spawn.petOwnerId = ownerid;
|
||||
|
||||
ns->spawn.haircolor = haircolor;
|
||||
@@ -1624,7 +1627,7 @@ void Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32
|
||||
la->parm4 = parm4;
|
||||
la->parm5 = parm5;
|
||||
// Note that setting the b values to 0 will disable the related effect from the corresponding parameter.
|
||||
// Setting the a value appears to have no affect at all.
|
||||
// Setting the a value appears to have no affect at all.s
|
||||
la->value1a = 1;
|
||||
la->value1b = 1;
|
||||
la->value2a = 1;
|
||||
@@ -2086,27 +2089,35 @@ void Mob::SetAttackTimer() {
|
||||
|
||||
}
|
||||
|
||||
bool Mob::CanThisClassDualWield(void) const
|
||||
{
|
||||
if (!IsClient()) {
|
||||
bool Mob::CanThisClassDualWield(void) const {
|
||||
if(!IsClient()) {
|
||||
return(GetSkill(SkillDualWield) > 0);
|
||||
} else {
|
||||
const ItemInst* inst = CastToClient()->GetInv().GetItem(SLOT_PRIMARY);
|
||||
}
|
||||
else if(CastToClient()->HasSkill(SkillDualWield)) {
|
||||
const ItemInst* pinst = CastToClient()->GetInv().GetItem(SLOT_PRIMARY);
|
||||
const ItemInst* sinst = CastToClient()->GetInv().GetItem(SLOT_SECONDARY);
|
||||
|
||||
// 2HS, 2HB, or 2HP
|
||||
if (inst && inst->IsType(ItemClassCommon)) {
|
||||
const Item_Struct* item = inst->GetItem();
|
||||
if ((item->ItemType == ItemType2HBlunt) || (item->ItemType == ItemType2HSlash) || (item->ItemType == ItemType2HPiercing))
|
||||
if(pinst && pinst->IsWeapon()) {
|
||||
const Item_Struct* item = pinst->GetItem();
|
||||
|
||||
if((item->ItemType == ItemType2HBlunt) || (item->ItemType == ItemType2HSlash) || (item->ItemType == ItemType2HPiercing))
|
||||
return false;
|
||||
} else {
|
||||
//No weapon in hand... using hand-to-hand...
|
||||
//only monks and beastlords? can dual wield their fists.
|
||||
if(class_ != MONK && class_ != MONKGM && class_ != BEASTLORD && class_ != BEASTLORDGM) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (CastToClient()->HasSkill(SkillDualWield)); // No skill = no chance
|
||||
// OffHand Weapon
|
||||
if(sinst && !sinst->IsWeapon())
|
||||
return false;
|
||||
|
||||
// Dual-Wielding Empty Fists
|
||||
if(!pinst && !sinst)
|
||||
if(class_ != MONK && class_ != MONKGM && class_ != BEASTLORD && class_ != BEASTLORDGM)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::CanThisClassDoubleAttack(void) const
|
||||
@@ -2456,13 +2467,18 @@ bool Mob::RemoveFromHateList(Mob* mob)
|
||||
|
||||
return bFound;
|
||||
}
|
||||
|
||||
void Mob::WipeHateList()
|
||||
{
|
||||
if(IsEngaged())
|
||||
{
|
||||
hate_list.Wipe();
|
||||
AI_Event_NoLongerEngaged();
|
||||
}
|
||||
hate_list.Wipe();
|
||||
else
|
||||
{
|
||||
hate_list.Wipe();
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Mob::RandomTimer(int min,int max) {
|
||||
@@ -4349,6 +4365,49 @@ bool Mob::TryReflectSpell(uint32 spell_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
void Mob::SpellProjectileEffect()
|
||||
{
|
||||
bool time_disable = false;
|
||||
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) {
|
||||
|
||||
if (projectile_increment[i] == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
Mob* target = entity_list.GetMobID(projectile_target_id[i]);
|
||||
|
||||
float dist = 0;
|
||||
|
||||
if (target)
|
||||
dist = target->CalculateDistance(projectile_x[i], projectile_y[i], projectile_z[i]);
|
||||
|
||||
int increment_end = 0;
|
||||
increment_end = (dist / 10) - 1; //This pretty accurately determines end time for speed for 1.5 and timer of 250 ms
|
||||
|
||||
if (increment_end <= projectile_increment[i]){
|
||||
|
||||
if (target && IsValidSpell(projectile_spell_id[i]))
|
||||
SpellOnTarget(projectile_spell_id[i], target, false, true, spells[projectile_spell_id[i]].ResistDiff, true);
|
||||
|
||||
projectile_spell_id[i] = 0;
|
||||
projectile_target_id[i] = 0;
|
||||
projectile_x[i] = 0, projectile_y[i] = 0, projectile_z[i] = 0;
|
||||
projectile_increment[i] = 0;
|
||||
time_disable = true;
|
||||
}
|
||||
|
||||
else {
|
||||
projectile_increment[i]++;
|
||||
time_disable = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (time_disable)
|
||||
projectile_timer.Disable();
|
||||
}
|
||||
|
||||
|
||||
void Mob::DoGravityEffect()
|
||||
{
|
||||
Mob *caster = nullptr;
|
||||
@@ -4582,33 +4641,21 @@ void Mob::CastOnNumHitFade(uint32 spell_id)
|
||||
}
|
||||
}
|
||||
|
||||
int Mob::SlowMitigation(bool slow_msg, Mob *caster, int slow_value)
|
||||
void Mob::SlowMitigation(Mob* caster)
|
||||
{
|
||||
float int_slow_mitigation = slow_mitigation * 100.0f;
|
||||
|
||||
if (int_slow_mitigation > 100.0f)
|
||||
return 0;
|
||||
|
||||
if (slow_msg)
|
||||
if (GetSlowMitigation() && caster && caster->IsClient())
|
||||
{
|
||||
if (caster && caster->IsClient())
|
||||
{
|
||||
if ((int_slow_mitigation > 0.0f) && (int_slow_mitigation < 26.0f))
|
||||
caster->Message(262, "Your spell was mostly successful");
|
||||
if ((GetSlowMitigation() > 0) && (GetSlowMitigation() < 26))
|
||||
caster->Message_StringID(MT_SpellFailure, SLOW_MOSTLY_SUCCESSFUL);
|
||||
|
||||
else if ((int_slow_mitigation >= 26.0f) && (int_slow_mitigation < 74.0f))
|
||||
caster->Message(262, "Your spell was partially successful");
|
||||
else if ((GetSlowMitigation() >= 26) && (GetSlowMitigation() < 74))
|
||||
caster->Message_StringID(MT_SpellFailure, SLOW_PARTIALLY_SUCCESSFUL);
|
||||
|
||||
else if ((int_slow_mitigation >= 74.0f) && (int_slow_mitigation < 101.0f))
|
||||
caster->Message(262, "Your spell was slightly successful");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if ((GetSlowMitigation() >= 74) && (GetSlowMitigation() < 101))
|
||||
caster->Message_StringID(MT_SpellFailure, SLOW_SLIGHTLY_SUCCESSFUL);
|
||||
|
||||
else
|
||||
{
|
||||
slow_value -= (slow_value * static_cast<int>(int_slow_mitigation) / 100);
|
||||
return slow_value;
|
||||
else if (GetSlowMitigation() > 100)
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_OPPOSITE_EFFECT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4751,11 +4798,7 @@ int32 Mob::GetItemFactionBonus(uint32 pFactionID) {
|
||||
}
|
||||
|
||||
void Mob::ClearItemFactionBonuses() {
|
||||
std::map <uint32, int32> :: iterator itr;
|
||||
for(itr = item_faction_bonuses.begin(); itr != item_faction_bonuses.end(); ++itr)
|
||||
{
|
||||
item_faction_bonuses.erase(itr->first);
|
||||
}
|
||||
item_faction_bonuses.clear();
|
||||
}
|
||||
|
||||
FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) {
|
||||
|
||||
+22
-21
@@ -146,6 +146,7 @@ public:
|
||||
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
|
||||
bool CombatRange(Mob* other);
|
||||
virtual inline bool IsBerserk() { return false; } // only clients
|
||||
void RogueEvade(Mob *other);
|
||||
|
||||
//Appearance
|
||||
void SendLevelAppearance();
|
||||
@@ -160,7 +161,7 @@ public:
|
||||
virtual void WearChange(uint8 material_slot, uint16 texture, uint32 color);
|
||||
void DoAnim(const int animnum, int type=0, bool ackreq = true, eqFilterType filter = FilterNone);
|
||||
void ProjectileAnimation(Mob* to, int item_id, bool IsArrow = false, float speed = 0,
|
||||
float angle = 0, float tilt = 0, float arc = 0);
|
||||
float angle = 0, float tilt = 0, float arc = 0, const char *IDFile = nullptr);
|
||||
void ChangeSize(float in_size, bool bNoRestriction = false);
|
||||
inline uint8 SeeInvisible() const { return see_invis; }
|
||||
inline bool SeeInvisibleUndead() const { return see_invis_undead; }
|
||||
@@ -169,7 +170,7 @@ public:
|
||||
bool IsInvisible(Mob* other = 0) const;
|
||||
void SetInvisible(uint8 state);
|
||||
bool AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* weapon);
|
||||
|
||||
|
||||
//Song
|
||||
bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1);
|
||||
bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot);
|
||||
@@ -190,7 +191,8 @@ public:
|
||||
virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration){ return duration;}
|
||||
virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime);
|
||||
float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false,
|
||||
int resist_override = 0, bool CharismaCheck = false);
|
||||
int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false);
|
||||
int ResistPhysical(int level_diff, uint8 caster_level);
|
||||
uint16 GetSpecializeSkillValue(uint16 spell_id) const;
|
||||
void SendSpellBarDisable();
|
||||
void SendSpellBarEnable(uint16 spellid);
|
||||
@@ -221,6 +223,8 @@ public:
|
||||
uint16 CastingSpellID() const { return casting_spell_id; }
|
||||
bool DoCastingChecks();
|
||||
bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier);
|
||||
void SpellProjectileEffect();
|
||||
bool TrySpellProjectile(Mob* spell_target, uint16 spell_id);
|
||||
|
||||
//Buff
|
||||
void BuffProcess();
|
||||
@@ -247,14 +251,6 @@ public:
|
||||
virtual int GetMaxTotalSlots() const { return 0; }
|
||||
virtual void InitializeBuffSlots() { buffs = nullptr; current_buff_count = 0; }
|
||||
virtual void UninitializeBuffSlots() { }
|
||||
inline bool HasRune() const { return m_hasRune; }
|
||||
inline bool HasSpellRune() const { return m_hasSpellRune; }
|
||||
inline bool HasPartialMeleeRune() const { return m_hasPartialMeleeRune; }
|
||||
inline bool HasPartialSpellRune() const { return m_hasPartialSpellRune; }
|
||||
inline void SetHasRune(bool hasRune) { m_hasRune = hasRune; }
|
||||
inline void SetHasSpellRune(bool hasSpellRune) { m_hasSpellRune = hasSpellRune; }
|
||||
inline void SetHasPartialMeleeRune(bool hasPartialMeleeRune) { m_hasPartialMeleeRune = hasPartialMeleeRune; }
|
||||
inline void SetHasPartialSpellRune(bool hasPartialSpellRune) { m_hasPartialSpellRune = hasPartialSpellRune; }
|
||||
EQApplicationPacket *MakeBuffsPacket(bool for_target = true);
|
||||
void SendBuffsToClient(Client *c);
|
||||
inline Buffs_Struct* GetBuffs() { return buffs; }
|
||||
@@ -297,7 +293,7 @@ public:
|
||||
bool ChangeHP(Mob* other, int32 amount, uint16 spell_id = 0, int8 buffslot = -1, bool iBuffTic = false);
|
||||
inline void SetOOCRegen(int32 newoocregen) {oocregen = newoocregen;}
|
||||
virtual void Heal();
|
||||
virtual void HealDamage(uint32 ammount, Mob* caster = nullptr);
|
||||
virtual void HealDamage(uint32 ammount, Mob* caster = nullptr, uint16 spell_id = SPELL_UNKNOWN);
|
||||
virtual void SetMaxHP() { cur_hp = max_hp; }
|
||||
virtual inline uint16 GetBaseRace() const { return base_race; }
|
||||
virtual inline uint8 GetBaseGender() const { return base_gender; }
|
||||
@@ -344,6 +340,7 @@ public:
|
||||
inline virtual int16 GetPR() const { return PR + itembonuses.PR + spellbonuses.PR; }
|
||||
inline virtual int16 GetCR() const { return CR + itembonuses.CR + spellbonuses.CR; }
|
||||
inline virtual int16 GetCorrup() const { return Corrup + itembonuses.Corrup + spellbonuses.Corrup; }
|
||||
inline virtual int16 GetPhR() const { return PhR; }
|
||||
inline StatBonuses GetItemBonuses() const { return itembonuses; }
|
||||
inline StatBonuses GetSpellBonuses() const { return spellbonuses; }
|
||||
inline StatBonuses GetAABonuses() const { return aabonuses; }
|
||||
@@ -502,6 +499,7 @@ public:
|
||||
bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN);
|
||||
bool RemoveProcFromWeapon(uint16 spell_id, bool bAll = false);
|
||||
bool HasProcs() const;
|
||||
bool IsCombatProc(uint16 spell_id);
|
||||
|
||||
//Logging
|
||||
bool IsLoggingEnabled() const { return(logging_enabled); }
|
||||
@@ -582,7 +580,7 @@ public:
|
||||
void CastOnCurer(uint32 spell_id);
|
||||
void CastOnCure(uint32 spell_id);
|
||||
void CastOnNumHitFade(uint32 spell_id);
|
||||
int SlowMitigation(bool slow_msg=false, Mob *caster = nullptr,int slow_value = 0);
|
||||
void SlowMitigation(Mob* caster);
|
||||
int16 GetCritDmgMob(uint16 skill);
|
||||
int16 GetMeleeDamageMod_SE(uint16 skill);
|
||||
int16 GetMeleeMinDamageMod_SE(uint16 skill);
|
||||
@@ -599,6 +597,8 @@ public:
|
||||
void MeleeLifeTap(int32 damage);
|
||||
bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true);
|
||||
bool ImprovedTaunt();
|
||||
bool TryRootFadeByDamage(int buffslot, Mob* attacker);
|
||||
int16 GetSlowMitigation() const {return slow_mitigation;}
|
||||
|
||||
void ModSkillDmgTaken(SkillUseTypes skill_num, int value);
|
||||
int16 GetModSkillDmgTaken(const SkillUseTypes skill_num);
|
||||
@@ -920,6 +920,7 @@ protected:
|
||||
int16 DR;
|
||||
int16 PR;
|
||||
int16 Corrup;
|
||||
int16 PhR;
|
||||
bool moving;
|
||||
int targeted;
|
||||
bool findable;
|
||||
@@ -945,6 +946,7 @@ protected:
|
||||
int16 petpower;
|
||||
uint32 follow;
|
||||
uint32 follow_dist;
|
||||
bool no_target_hotkey;
|
||||
|
||||
uint8 gender;
|
||||
uint16 race;
|
||||
@@ -990,7 +992,6 @@ protected:
|
||||
float FindGroundZ(float new_x, float new_y, float z_offset=0.0);
|
||||
Map::Vertex UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached);
|
||||
void PrintRoute();
|
||||
void UpdateRuneFlags();
|
||||
|
||||
virtual float GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod);
|
||||
|
||||
@@ -1023,7 +1024,7 @@ protected:
|
||||
Timer attack_dw_timer;
|
||||
Timer ranged_timer;
|
||||
float attack_speed; //% increase/decrease in attack speed (not haste)
|
||||
float slow_mitigation; // Allows for a slow mitigation based on a % in decimal form. IE, 1 = 100% mitigation, .5 is 50%
|
||||
float slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%)
|
||||
Timer tic_timer;
|
||||
Timer mana_timer;
|
||||
|
||||
@@ -1046,6 +1047,12 @@ protected:
|
||||
uint8 bardsong_slot;
|
||||
uint32 bardsong_target_id;
|
||||
|
||||
Timer projectile_timer;
|
||||
uint32 projectile_spell_id[MAX_SPELL_PROJECTILE];
|
||||
uint16 projectile_target_id[MAX_SPELL_PROJECTILE];
|
||||
uint8 projectile_increment[MAX_SPELL_PROJECTILE];
|
||||
float projectile_x[MAX_SPELL_PROJECTILE], projectile_y[MAX_SPELL_PROJECTILE], projectile_z[MAX_SPELL_PROJECTILE];
|
||||
|
||||
float rewind_x;
|
||||
float rewind_y;
|
||||
float rewind_z;
|
||||
@@ -1082,7 +1089,6 @@ protected:
|
||||
bool inWater; // Set to true or false by Water Detection code if enabled by rules
|
||||
bool has_virus; // whether this mob has a viral spell on them
|
||||
uint16 viral_spells[MAX_SPELL_TRIGGER*2]; // Stores the spell ids of the viruses on target and caster ids
|
||||
int16 rooted_mod; //Modifier to root break chance, defined when root is cast on a target.
|
||||
bool offhand;
|
||||
bool has_shieldequiped;
|
||||
bool has_numhits;
|
||||
@@ -1190,11 +1196,6 @@ protected:
|
||||
float tar_vz;
|
||||
float test_vector;
|
||||
|
||||
bool m_hasRune;
|
||||
bool m_hasSpellRune;
|
||||
bool m_hasPartialMeleeRune;
|
||||
bool m_hasPartialSpellRune;
|
||||
bool m_hasDeathSaveChance;
|
||||
uint32 m_spellHitsLeft[38]; // Used to track which spells will have their numhits incremented when spell finishes casting, 38 Buffslots
|
||||
int flymode;
|
||||
bool m_targetable;
|
||||
|
||||
+1
-1
@@ -447,7 +447,7 @@ int main(int argc, char** argv) {
|
||||
if (InterserverTimer.Check()) {
|
||||
InterserverTimer.Start();
|
||||
database.ping();
|
||||
AsyncLoadVariables(dbasync, &database);
|
||||
// AsyncLoadVariables(dbasync, &database);
|
||||
entity_list.UpdateWho();
|
||||
if (worldserver.TryReconnect() && (!worldserver.Connected()))
|
||||
worldserver.AsyncConnect();
|
||||
|
||||
+32
-1
@@ -158,6 +158,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
||||
FR = d->FR;
|
||||
PR = d->PR;
|
||||
Corrup = d->Corrup;
|
||||
PhR = d->PhR;
|
||||
|
||||
STR = d->STR;
|
||||
STA = d->STA;
|
||||
@@ -199,6 +200,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
||||
SetMana(GetMaxMana());
|
||||
|
||||
MerchantType = d->merchanttype;
|
||||
merchant_open = GetClass() == MERCHANT;
|
||||
adventure_template_id = d->adventure_template;
|
||||
org_x = x;
|
||||
org_y = y;
|
||||
@@ -222,6 +224,8 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
||||
p_depop = false;
|
||||
loottable_id = d->loottable_id;
|
||||
|
||||
no_target_hotkey = d->no_target_hotkey;
|
||||
|
||||
primary_faction = 0;
|
||||
SetNPCFactionID(d->npc_faction_id);
|
||||
|
||||
@@ -658,6 +662,9 @@ bool NPC::Process()
|
||||
viral_timer_counter = 0;
|
||||
}
|
||||
|
||||
if(projectile_timer.Check())
|
||||
SpellProjectileEffect();
|
||||
|
||||
if(spellbonuses.GravityEffect == 1) {
|
||||
if(gravity_timer.Check())
|
||||
DoGravityEffect();
|
||||
@@ -1714,6 +1721,22 @@ bool Mob::HasNPCSpecialAtk(const char* parse) {
|
||||
void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
||||
{
|
||||
Mob::FillSpawnStruct(ns, ForWho);
|
||||
|
||||
//Basic settings to make sure swarm pets work properly.
|
||||
if (GetSwarmOwner()) {
|
||||
Client *c = entity_list.GetClientByID(GetSwarmOwner());
|
||||
if(c) {
|
||||
SetAllowBeneficial(1); //Allow client cast swarm pets to be heal/buffed.
|
||||
//This is a hack to allow CLIENT swarm pets NOT to be targeted with F8. Warning: Will turn name 'Yellow'!
|
||||
if (RuleB(Pets, SwarmPetNotTargetableWithHotKey))
|
||||
ns->spawn.IsMercenary = 1;
|
||||
}
|
||||
//NPC cast swarm pets should still be targetable with F8.
|
||||
else
|
||||
ns->spawn.IsMercenary = 0;
|
||||
}
|
||||
|
||||
//Not recommended if using above (However, this will work better on older clients).
|
||||
if (RuleB(Pets, UnTargetableSwarmPet)) {
|
||||
if(GetOwnerID() || GetSwarmOwner()) {
|
||||
ns->spawn.is_pet = 1;
|
||||
@@ -1862,6 +1885,12 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
|
||||
return;
|
||||
}
|
||||
|
||||
if(id == "PhR")
|
||||
{
|
||||
PhR = atoi(val.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if(id == "runspeed")
|
||||
{
|
||||
runspeed = (float)atof(val.c_str());
|
||||
@@ -1975,7 +2004,7 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
|
||||
|
||||
if(id == "slow_mitigation")
|
||||
{
|
||||
slow_mitigation = atof(val.c_str());
|
||||
slow_mitigation = atoi(val.c_str());
|
||||
return;
|
||||
}
|
||||
if(id == "loottable_id")
|
||||
@@ -2058,6 +2087,8 @@ void NPC::CalcNPCResists() {
|
||||
PR = (GetLevel() * 11)/10;
|
||||
if (!Corrup)
|
||||
Corrup = 15;
|
||||
if (!PhR)
|
||||
PhR = 10;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -209,6 +209,10 @@ public:
|
||||
void SetSecSkill(uint8 skill_type) { sec_melee_type = skill_type; }
|
||||
|
||||
uint32 MerchantType;
|
||||
bool merchant_open;
|
||||
inline void MerchantOpenShop() { merchant_open = true; }
|
||||
inline void MerchantCloseShop() { merchant_open = false; }
|
||||
inline bool IsMerchantOpen() { return merchant_open; }
|
||||
void Depop(bool StartSpawnTimer = false);
|
||||
void Stun(int duration);
|
||||
void UnStun();
|
||||
@@ -229,7 +233,7 @@ public:
|
||||
|
||||
uint32 GetMaxDMG() const {return max_dmg;}
|
||||
uint32 GetMinDMG() const {return min_dmg;}
|
||||
float GetSlowMitigation() const {return slow_mitigation;}
|
||||
int16 GetSlowMitigation() const {return slow_mitigation;}
|
||||
float GetAttackSpeed() const {return attack_speed;}
|
||||
bool IsAnimal() const { return(bodytype == BT_Animal); }
|
||||
uint16 GetPetSpellID() const {return pet_spell_id;}
|
||||
@@ -241,6 +245,7 @@ public:
|
||||
void AddLootDrop(const Item_Struct*dbitem, ItemList* itemlistconst, int16 charges, uint8 minlevel, uint8 maxlevel, bool equipit, bool wearchange = false);
|
||||
virtual void DoClassAttacks(Mob *target);
|
||||
void CheckSignal();
|
||||
inline bool IsTargetableWithHotkey() const { return no_target_hotkey; }
|
||||
|
||||
//waypoint crap
|
||||
int GetMaxWp() const { return max_wp; }
|
||||
|
||||
@@ -5898,6 +5898,64 @@ XS(XS_Client_PlayMP3)
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS_Client_ExpeditionMessage); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Client_ExpeditionMessage)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 3)
|
||||
Perl_croak(aTHX_ "Usage: Client::ExpeditionMessage(THIS, ExpdID, Message)");
|
||||
{
|
||||
Client * THIS;
|
||||
int ExpdID = (int)SvUV(ST(1));
|
||||
const char * Message = (const char *)SvPV_nolen(ST(2));
|
||||
dXSTARG;
|
||||
|
||||
if (sv_derived_from(ST(0), "Client")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
THIS = INT2PTR(Client *,tmp);
|
||||
}
|
||||
else
|
||||
Perl_croak(aTHX_ "THIS is not of type Client");
|
||||
if(THIS == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
THIS->ExpeditionSay(Message, ExpdID);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
//Client::SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg)
|
||||
|
||||
XS(XS_Client_SendMarqueeMessage); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Client_SendMarqueeMessage)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 7)
|
||||
Perl_croak(aTHX_ "Usage: Client::SendMarqueeMessage(THIS, type, priority, fade_in, fade_out, duration, msg)");
|
||||
{
|
||||
Client * THIS;
|
||||
uint32 type = (uint32)SvUV(ST(1));
|
||||
uint32 priority = (uint32)SvUV(ST(2));
|
||||
uint32 fade_in = (uint32)SvUV(ST(3));
|
||||
uint32 fade_out = (uint32)SvUV(ST(4));
|
||||
uint32 duration = (uint32)SvUV(ST(5));
|
||||
std::string msg = (std::string)SvPV_nolen(ST(6));
|
||||
dXSTARG;
|
||||
|
||||
if (sv_derived_from(ST(0), "Client")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
THIS = INT2PTR(Client *,tmp);
|
||||
}
|
||||
else
|
||||
Perl_croak(aTHX_ "THIS is not of type Client");
|
||||
if(THIS == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
THIS->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
@@ -6134,6 +6192,8 @@ XS(boot_Client)
|
||||
newXSproto(strcpy(buf, "SilentMessage"), XS_Client_SilentMessage, file, "$$");
|
||||
newXSproto(strcpy(buf, "PlayMP3"), XS_Client_PlayMP3, file, "$;$");
|
||||
newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$");
|
||||
newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$");
|
||||
newXSproto(strcpy(buf, "SendMarqueeMessage"), XS_Client_SendMarqueeMessage, file, "$$$$$$$");
|
||||
XSRETURN_YES;
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -6856,7 +6856,7 @@ XS(XS_Mob_ProjectileAnim); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Mob_ProjectileAnim)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items < 3 || items > 8)
|
||||
if (items < 3 || items > 9)
|
||||
Perl_croak(aTHX_ "Usage: Mob::ProjectileAnim(THIS, mob, item_id, IsArrow?, speed, angle, tilt, arc)");
|
||||
|
||||
{
|
||||
@@ -6868,6 +6868,7 @@ XS(XS_Mob_ProjectileAnim)
|
||||
float angle = 0;
|
||||
float tilt = 0;
|
||||
float arc = 0;
|
||||
char * IDFile = nullptr;
|
||||
|
||||
if (sv_derived_from(ST(0), "Mob")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
@@ -6903,7 +6904,9 @@ XS(XS_Mob_ProjectileAnim)
|
||||
arc = (float)SvNV(ST(7));
|
||||
}
|
||||
|
||||
THIS->ProjectileAnimation(mob, item_id, IsArrow, speed, angle, tilt, arc);
|
||||
if (items > 8) { IDFile = (char *)SvPV_nolen(ST(8)); }
|
||||
|
||||
THIS->ProjectileAnimation(mob, item_id, IsArrow, speed, angle, tilt, arc, IDFile);
|
||||
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
@@ -8389,7 +8392,7 @@ XS(boot_Mob)
|
||||
newXSproto(strcpy(buf, "CheckLoS"), XS_Mob_CheckLoS, file, "$$");
|
||||
newXSproto(strcpy(buf, "CheckLoSToLoc"), XS_Mob_CheckLoSToLoc, file, "$$$$;$");
|
||||
newXSproto(strcpy(buf, "FindGroundZ"), XS_Mob_FindGroundZ, file, "$$$;$");
|
||||
newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$");
|
||||
newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$$");
|
||||
newXSproto(strcpy(buf, "HasNPCSpecialAtk"), XS_Mob_HasNPCSpecialAtk, file, "$$");
|
||||
newXSproto(strcpy(buf, "SendAppearanceEffect"), XS_Mob_SendAppearanceEffect, file, "$$;$$$$");
|
||||
newXSproto(strcpy(buf, "SetFlyMode"), XS_Mob_SetFlyMode, file, "$$");
|
||||
|
||||
+2
-2
@@ -2023,7 +2023,7 @@ XS(XS_NPC_GetSlowMitigation)
|
||||
Perl_croak(aTHX_ "Usage: NPC::GetSlowMitigation(THIS)");
|
||||
{
|
||||
NPC * THIS;
|
||||
float RETVAL;
|
||||
int16 RETVAL;
|
||||
dXSTARG;
|
||||
|
||||
if (sv_derived_from(ST(0), "NPC")) {
|
||||
@@ -2036,7 +2036,7 @@ XS(XS_NPC_GetSlowMitigation)
|
||||
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
|
||||
|
||||
RETVAL = THIS->GetSlowMitigation();
|
||||
XSprePUSH; PUSHn((double)RETVAL);
|
||||
XSprePUSH; PUSHn((UV)RETVAL);
|
||||
}
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
@@ -627,7 +627,6 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) {
|
||||
}
|
||||
}
|
||||
}
|
||||
UpdateRuneFlags();
|
||||
|
||||
//restore their equipment...
|
||||
for(i = 0; i < MAX_WORN_INVENTORY; i++) {
|
||||
|
||||
+102
-12
@@ -484,6 +484,29 @@ void QuestManager::settimerMS(const char *timer_name, int milliseconds) {
|
||||
QTimerList.push_back(QuestTimer(milliseconds, owner, timer_name));
|
||||
}
|
||||
|
||||
void QuestManager::settimerMS(const char *timer_name, int milliseconds, ItemInst *inst) {
|
||||
if (inst) {
|
||||
inst->SetTimer(timer_name, milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::settimerMS(const char *timer_name, int milliseconds, Mob *mob) {
|
||||
std::list<QuestTimer>::iterator cur = QTimerList.begin(), end;
|
||||
|
||||
end = QTimerList.end();
|
||||
while (cur != end) {
|
||||
if (cur->mob && cur->mob == mob && cur->name == timer_name)
|
||||
{
|
||||
cur->Timer_.Enable();
|
||||
cur->Timer_.Start(milliseconds, false);
|
||||
return;
|
||||
}
|
||||
++cur;
|
||||
}
|
||||
|
||||
QTimerList.push_back(QuestTimer(milliseconds, mob, timer_name));
|
||||
}
|
||||
|
||||
void QuestManager::stoptimer(const char *timer_name) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
|
||||
@@ -504,6 +527,25 @@ void QuestManager::stoptimer(const char *timer_name) {
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::stoptimer(const char *timer_name, ItemInst *inst) {
|
||||
if (inst) {
|
||||
inst->StopTimer(timer_name);
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::stoptimer(const char *timer_name, Mob *mob) {
|
||||
std::list<QuestTimer>::iterator cur = QTimerList.begin(), end;
|
||||
|
||||
end = QTimerList.end();
|
||||
while (cur != end) {
|
||||
if (cur->mob && cur->mob == mob && cur->name == timer_name) {
|
||||
QTimerList.erase(cur);
|
||||
return;
|
||||
}
|
||||
++cur;
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::stopalltimers() {
|
||||
QuestManagerCurrentQuestVars();
|
||||
|
||||
@@ -523,6 +565,24 @@ void QuestManager::stopalltimers() {
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::stopalltimers(ItemInst *inst) {
|
||||
if (inst) {
|
||||
inst->ClearTimers();
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::stopalltimers(Mob *mob) {
|
||||
std::list<QuestTimer>::iterator cur = QTimerList.begin(), end, tmp;
|
||||
|
||||
end = QTimerList.end();
|
||||
while (cur != end) {
|
||||
if (cur->mob && cur->mob == mob)
|
||||
cur = QTimerList.erase(cur);
|
||||
else
|
||||
++cur;
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::emote(const char *str) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
if (!owner) {
|
||||
@@ -1217,16 +1277,9 @@ void QuestManager::signalwith(int npc_id, int signal_id, int wait_ms) {
|
||||
if(wait_ms > 0) {
|
||||
STimerList.push_back(SignalTimer(wait_ms, npc_id, signal_id));
|
||||
return;
|
||||
}
|
||||
|
||||
if (npc_id<1)
|
||||
{
|
||||
printf("signal() bad npcid=%i\n",npc_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
//initiator* signalnpc=0;
|
||||
entity_list.SignalMobsByNPCID(npc_id, signal_id);
|
||||
} else {
|
||||
STimerList.push_back(SignalTimer(0, npc_id, signal_id));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1680,8 +1733,8 @@ short QuestManager::get_spawn_condition(const char *zone_short, uint32 instance_
|
||||
}
|
||||
|
||||
//toggle a spawn event
|
||||
void QuestManager::toggle_spawn_event(int event_id, bool enable, bool reset_base) {
|
||||
zone->spawn_conditions.ToggleEvent(event_id, enable, reset_base);
|
||||
void QuestManager::toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base) {
|
||||
zone->spawn_conditions.ToggleEvent(event_id, enable, strict, reset_base);
|
||||
}
|
||||
|
||||
bool QuestManager::has_zone_flag(int zone_id) {
|
||||
@@ -2557,6 +2610,43 @@ void QuestManager::AssignRaidToInstance(uint16 instance_id)
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::RemoveFromInstance(uint16 instance_id)
|
||||
{
|
||||
QuestManagerCurrentQuestVars();
|
||||
if(initiator) {
|
||||
if(database.RemoveClientFromInstance(instance_id, initiator->CharacterID())) {
|
||||
initiator->Message(MT_Say, "Removed client from instance.");
|
||||
} else {
|
||||
initiator->Message(MT_Say, "Failed to remove client from instance.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::RemoveAllFromInstance(uint16 instance_id)
|
||||
{
|
||||
QuestManagerCurrentQuestVars();
|
||||
if(initiator) {
|
||||
std::list<uint32> charid_list;
|
||||
bool removed_all = true;
|
||||
uint16 fail_count = 0;
|
||||
database.GetCharactersInInstance(instance_id,charid_list);
|
||||
auto iter = charid_list.begin();
|
||||
while(iter != charid_list.end()) {
|
||||
if(!database.RemoveClientFromInstance(instance_id, *iter)) {
|
||||
removed_all = false;
|
||||
++fail_count;
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
if (removed_all) {
|
||||
initiator->Message(MT_Say, "Removed all players from instance.");
|
||||
} else {
|
||||
// once the expedition system is in, this message it not relevant
|
||||
initiator->Message(MT_Say, "Failed to remove %i player(s) from instance.", fail_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::MovePCInstance(int zone_id, int instance_id, float x, float y, float z, float heading)
|
||||
{
|
||||
QuestManagerCurrentQuestVars();
|
||||
|
||||
+13
-2
@@ -41,6 +41,7 @@ public:
|
||||
|
||||
void StartQuest(Mob *_owner, Client *_initiator = nullptr, ItemInst* _questitem = nullptr);
|
||||
void EndQuest();
|
||||
bool QuestsRunning() { return !quests_running_.empty(); }
|
||||
|
||||
void Process();
|
||||
|
||||
@@ -66,9 +67,15 @@ public:
|
||||
void addloot(int item_id, int charges = 0, bool equipitem = true);
|
||||
void Zone(const char *zone_name);
|
||||
void settimer(const char *timer_name, int seconds);
|
||||
void settimerMS(const char *timer_name, int milliseconds);
|
||||
void settimerMS(const char *timer_name, int milliseconds);
|
||||
void settimerMS(const char *timer_name, int milliseconds, ItemInst *inst);
|
||||
void settimerMS(const char *timer_name, int milliseconds, Mob *mob);
|
||||
void stoptimer(const char *timer_name);
|
||||
void stoptimer(const char *timer_name, ItemInst *inst);
|
||||
void stoptimer(const char *timer_name, Mob *mob);
|
||||
void stopalltimers();
|
||||
void stopalltimers(ItemInst *inst);
|
||||
void stopalltimers(Mob *mob);
|
||||
void emote(const char *str);
|
||||
void shout(const char *str);
|
||||
void shout2(const char *str);
|
||||
@@ -143,7 +150,7 @@ public:
|
||||
void showgrid(int gridid);
|
||||
void spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id, short new_value);
|
||||
short get_spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id);
|
||||
void toggle_spawn_event(int event_id, bool enable, bool reset_base);
|
||||
void toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base);
|
||||
bool has_zone_flag(int zone_id);
|
||||
void set_zone_flag(int zone_id);
|
||||
void clear_zone_flag(int zone_id);
|
||||
@@ -211,6 +218,10 @@ public:
|
||||
void AssignToInstance(uint16 instance_id);
|
||||
void AssignGroupToInstance(uint16 instance_id);
|
||||
void AssignRaidToInstance(uint16 instance_id);
|
||||
void RemoveFromInstance(uint16 instance_id);
|
||||
//void RemoveGroupFromInstance(uint16 instance_id); //potentially useful but not implmented at this time.
|
||||
//void RemoveRaidFromInstance(uint16 instance_id); //potentially useful but not implmented at this time.
|
||||
void RemoveAllFromInstance(uint16 instance_id);
|
||||
void MovePCInstance(int zone_id, int instance_id, float x, float y, float z, float heading);
|
||||
void FlagInstanceByGroupLeader(uint32 zone, int16 version);
|
||||
void FlagInstanceByRaidLeader(uint32 zone, int16 version);
|
||||
|
||||
+94
-64
@@ -303,11 +303,13 @@ void Spawn2::ForceDespawn()
|
||||
{
|
||||
npcthis->Depop(true);
|
||||
IsDespawned = true;
|
||||
npcthis = nullptr;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
npcthis->Depop(false);
|
||||
npcthis = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -555,6 +557,7 @@ SpawnEvent::SpawnEvent() {
|
||||
action = ActionSet;
|
||||
argument = 0;
|
||||
period = 0xFFFFFFFF;
|
||||
strict = false;
|
||||
memset(&next, 0, sizeof(next));
|
||||
}
|
||||
|
||||
@@ -586,26 +589,28 @@ void SpawnConditionManager::Process() {
|
||||
for(; cur != end; ++cur) {
|
||||
SpawnEvent &cevent = *cur;
|
||||
|
||||
if(!cevent.enabled)
|
||||
continue;
|
||||
if(cevent.enabled)
|
||||
{
|
||||
if(EQTime::IsTimeBefore(&tod, &cevent.next)) {
|
||||
//this event has been triggered.
|
||||
//execute the event
|
||||
if(!cevent.strict || (cevent.strict && cevent.next.hour == tod.hour && cevent.next.day == tod.day && cevent.next.month == tod.month && cevent.next.year == tod.year))
|
||||
ExecEvent(cevent, true);
|
||||
|
||||
if(EQTime::IsTimeBefore(&tod, &cevent.next)) {
|
||||
//this event has been triggered.
|
||||
//execute the event
|
||||
ExecEvent(cevent, true);
|
||||
//add the period of the event to the trigger time
|
||||
EQTime::AddMinutes(cevent.period, &cevent.next);
|
||||
std::string t;
|
||||
EQTime::ToString(&cevent.next, t);
|
||||
_log(SPAWNS__CONDITIONS, "Event %d: Will trigger again in %d EQ minutes at %s.", cevent.id, cevent.period, t.c_str());
|
||||
//save the next event time in the DB
|
||||
UpdateDBEvent(cevent);
|
||||
//find the next closest event timer.
|
||||
FindNearestEvent();
|
||||
//minor optimization, if theres no more possible events,
|
||||
//then stop trying... I dunno how worth while this is.
|
||||
if(EQTime::IsTimeBefore(&next_event, &tod))
|
||||
return;
|
||||
//add the period of the event to the trigger time
|
||||
EQTime::AddMinutes(cevent.period, &cevent.next);
|
||||
std::string t;
|
||||
EQTime::ToString(&cevent.next, t);
|
||||
_log(SPAWNS__CONDITIONS, "Event %d: Will trigger again in %d EQ minutes at %s.", cevent.id, cevent.period, t.c_str());
|
||||
//save the next event time in the DB
|
||||
UpdateDBEvent(cevent);
|
||||
//find the next closest event timer.
|
||||
FindNearestEvent();
|
||||
//minor optimization, if theres no more possible events,
|
||||
//then stop trying... I dunno how worth while this is.
|
||||
if(EQTime::IsTimeBefore(&next_event, &tod))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -619,6 +624,14 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) {
|
||||
return; //unable to find the spawn condition to operate on
|
||||
}
|
||||
|
||||
TimeOfDay_Struct tod;
|
||||
zone->zone_time.getEQTimeOfDay(&tod);
|
||||
if(event.strict && (event.next.hour != tod.hour || event.next.day != tod.day || event.next.month != tod.month || event.next.year != tod.year))
|
||||
{
|
||||
_log(SPAWNS__CONDITIONS, "Event %d: Unable to execute. Condition is strict, and event time has already passed.", event.id);
|
||||
return;
|
||||
}
|
||||
|
||||
SpawnCondition &cond = condi->second;
|
||||
|
||||
int16 new_value = cond.value;
|
||||
@@ -666,10 +679,10 @@ void SpawnConditionManager::UpdateDBEvent(SpawnEvent &event) {
|
||||
len = MakeAnyLenString(&query,
|
||||
"UPDATE spawn_events SET "
|
||||
"next_minute=%d, next_hour=%d, next_day=%d, next_month=%d, "
|
||||
"next_year=%d, enabled=%d "
|
||||
"next_year=%d, enabled=%d, strict=%d "
|
||||
"WHERE id=%d",
|
||||
event.next.minute, event.next.hour, event.next.day, event.next.month,
|
||||
event.next.year, event.enabled?1:0, event.id
|
||||
event.next.year, event.enabled?1:0, event.strict?1:0,event.id
|
||||
);
|
||||
if(!database.RunQuery(query, len, errbuf)) {
|
||||
LogFile->write(EQEMuLog::Error, "Unable to update spawn event '%s': %s\n", query, errbuf);
|
||||
@@ -703,7 +716,7 @@ bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, std:
|
||||
bool ret = false;
|
||||
|
||||
len = MakeAnyLenString(&query,
|
||||
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,zone "
|
||||
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,strict,zone "
|
||||
"FROM spawn_events WHERE id=%d", event_id);
|
||||
if (database.RunQuery(query, len, errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
@@ -721,12 +734,13 @@ bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, std:
|
||||
event.enabled = atoi(row[8])==0?false:true;
|
||||
event.action = (SpawnEvent::Action) atoi(row[9]);
|
||||
event.argument = atoi(row[10]);
|
||||
zone_name = row[11];
|
||||
event.strict = atoi(row[11])==0?false:true;
|
||||
zone_name = row[12];
|
||||
|
||||
std::string t;
|
||||
EQTime::ToString(&event.next, t);
|
||||
_log(SPAWNS__CONDITIONS, "Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d. Will trigger at %s",
|
||||
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, t.c_str());
|
||||
_log(SPAWNS__CONDITIONS, "(LoadDBEvent) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d. Will trigger at %s",
|
||||
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict, t.c_str());
|
||||
|
||||
ret = true;
|
||||
}
|
||||
@@ -794,7 +808,7 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
|
||||
//load spawn events
|
||||
SpawnEvent event;
|
||||
len = MakeAnyLenString(&query,
|
||||
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument "
|
||||
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,strict "
|
||||
"FROM spawn_events WHERE zone='%s'", zone_name);
|
||||
if (database.RunQuery(query, len, errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
@@ -816,10 +830,11 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
|
||||
event.enabled = atoi(row[8])==0?false:true;
|
||||
event.action = (SpawnEvent::Action) atoi(row[9]);
|
||||
event.argument = atoi(row[10]);
|
||||
event.strict = atoi(row[11])==0?false:true;
|
||||
spawn_events.push_back(event);
|
||||
|
||||
_log(SPAWNS__CONDITIONS, "Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d",
|
||||
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument);
|
||||
_log(SPAWNS__CONDITIONS, "(LoadSpawnConditions) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d",
|
||||
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict);
|
||||
}
|
||||
mysql_free_result(result);
|
||||
} else {
|
||||
@@ -847,34 +862,48 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
|
||||
for(; cur != end; ++cur) {
|
||||
SpawnEvent &cevent = *cur;
|
||||
|
||||
if(!cevent.enabled)
|
||||
continue;
|
||||
bool StrictCheck = false;
|
||||
if(cevent.strict &&
|
||||
cevent.next.hour == tod.hour &&
|
||||
cevent.next.day == tod.day &&
|
||||
cevent.next.month == tod.month &&
|
||||
cevent.next.year == tod.year)
|
||||
StrictCheck = true;
|
||||
|
||||
//watch for special case of all 0s, which means to reset next to now
|
||||
if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) {
|
||||
_log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id);
|
||||
memcpy(&cevent.next, &tod, sizeof(cevent.next));
|
||||
//add one period
|
||||
EQTime::AddMinutes(cevent.period, &cevent.next);
|
||||
//save it in the db.
|
||||
UpdateDBEvent(cevent);
|
||||
continue; //were done with this event.
|
||||
}
|
||||
//If event is disabled, or we failed the strict check, set initial spawn_condition to 0.
|
||||
if(!cevent.enabled || !StrictCheck)
|
||||
SetCondition(zone->GetShortName(), zone->GetInstanceID(),cevent.condition_id,0);
|
||||
|
||||
ran = false;
|
||||
while(EQTime::IsTimeBefore(&tod, &cevent.next)) {
|
||||
_log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id);
|
||||
//this event has been triggered.
|
||||
//execute the event
|
||||
ExecEvent(cevent, false);
|
||||
//add the period of the event to the trigger time
|
||||
EQTime::AddMinutes(cevent.period, &cevent.next);
|
||||
ran = true;
|
||||
}
|
||||
//only write it out if the event actually ran
|
||||
if(ran) {
|
||||
//save the event in the DB
|
||||
UpdateDBEvent(cevent);
|
||||
if(cevent.enabled)
|
||||
{
|
||||
//watch for special case of all 0s, which means to reset next to now
|
||||
if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) {
|
||||
_log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id);
|
||||
memcpy(&cevent.next, &tod, sizeof(cevent.next));
|
||||
//add one period
|
||||
EQTime::AddMinutes(cevent.period, &cevent.next);
|
||||
//save it in the db.
|
||||
UpdateDBEvent(cevent);
|
||||
continue; //were done with this event.
|
||||
}
|
||||
|
||||
ran = false;
|
||||
while(EQTime::IsTimeBefore(&tod, &cevent.next)) {
|
||||
_log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id);
|
||||
//this event has been triggered.
|
||||
//execute the event
|
||||
if(!cevent.strict || StrictCheck)
|
||||
ExecEvent(cevent, false);
|
||||
|
||||
//add the period of the event to the trigger time
|
||||
EQTime::AddMinutes(cevent.period, &cevent.next);
|
||||
ran = true;
|
||||
}
|
||||
//only write it out if the event actually ran
|
||||
if(ran) {
|
||||
//save the event in the DB
|
||||
UpdateDBEvent(cevent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -894,14 +923,14 @@ void SpawnConditionManager::FindNearestEvent() {
|
||||
int next_id = -1;
|
||||
for(; cur != end; ++cur) {
|
||||
SpawnEvent &cevent = *cur;
|
||||
|
||||
if(!cevent.enabled)
|
||||
continue;
|
||||
|
||||
//see if this event is before our last nearest
|
||||
if(EQTime::IsTimeBefore(&next_event, &cevent.next)) {
|
||||
memcpy(&next_event, &cevent.next, sizeof(next_event));
|
||||
next_id = cevent.id;
|
||||
if(cevent.enabled)
|
||||
{
|
||||
//see if this event is before our last nearest
|
||||
if(EQTime::IsTimeBefore(&next_event, &cevent.next))
|
||||
{
|
||||
memcpy(&next_event, &cevent.next, sizeof(next_event));
|
||||
next_id = cevent.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(next_id == -1)
|
||||
@@ -1035,7 +1064,7 @@ void SpawnConditionManager::ReloadEvent(uint32 event_id) {
|
||||
FindNearestEvent();
|
||||
}
|
||||
|
||||
void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool reset_base) {
|
||||
void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool strict, bool reset_base) {
|
||||
|
||||
_log(SPAWNS__CONDITIONS, "Request to %s spawn event %d %sresetting trigger time", enabled?"enable":"disable", event_id, reset_base?"":"without ");
|
||||
|
||||
@@ -1048,8 +1077,9 @@ void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool rese
|
||||
|
||||
if(cevent.id == event_id) {
|
||||
//make sure were actually changing something
|
||||
if(cevent.enabled != enabled || reset_base) {
|
||||
if(cevent.enabled != enabled || reset_base || cevent.strict != strict) {
|
||||
cevent.enabled = enabled;
|
||||
cevent.strict = strict;
|
||||
if(reset_base) {
|
||||
_log(SPAWNS__CONDITIONS, "Spawn event %d located in this zone. State set. Trigger time reset (period %d).", event_id, cevent.period);
|
||||
//start with the time now
|
||||
|
||||
+3
-1
@@ -67,6 +67,7 @@ public:
|
||||
|
||||
bool NPCPointerValid() { return (npcthis!=nullptr); }
|
||||
void SetNPCPointer(NPC* n) { npcthis = n; }
|
||||
void SetNPCPointerNull() { npcthis = nullptr; }
|
||||
void SetTimer(uint32 duration) { timer.Start(duration); }
|
||||
uint32 GetKillCount() { return killcount; }
|
||||
protected:
|
||||
@@ -134,6 +135,7 @@ public:
|
||||
bool enabled;
|
||||
Action action;
|
||||
int16 argument;
|
||||
bool strict;
|
||||
|
||||
uint32 period; //eq minutes (3 seconds) between events
|
||||
TimeOfDay_Struct next; //next time this event triggers
|
||||
@@ -148,7 +150,7 @@ public:
|
||||
|
||||
int16 GetCondition(const char *zone_short, uint32 instance_id, uint16 condition_id);
|
||||
void SetCondition(const char *zone_short, uint32 instance_id, uint16 condition_id, int16 new_value, bool world_update = false);
|
||||
void ToggleEvent(uint32 event_id, bool enabled, bool reset_base);
|
||||
void ToggleEvent(uint32 event_id, bool enabled, bool strict, bool reset_base);
|
||||
bool Check(uint16 condition, int16 min_value);
|
||||
void ReloadEvent(uint32 event_id);
|
||||
|
||||
|
||||
@@ -839,6 +839,9 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) {
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
@@ -1085,6 +1088,9 @@ void NPC::RangedAttack(Mob* other)
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
@@ -1227,6 +1233,9 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51
|
||||
invisible_animals = false;
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(hidden || improved_hidden){
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
@@ -1337,7 +1346,7 @@ void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skil
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc) {
|
||||
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile) {
|
||||
|
||||
const Item_Struct* item = nullptr;
|
||||
uint8 item_type = 0;
|
||||
@@ -1371,6 +1380,10 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
|
||||
arc = 50;
|
||||
}
|
||||
|
||||
const char *item_IDFile = item->IDFile;
|
||||
|
||||
if (IDFile && (strncmp(IDFile, "IT", 2) == 0))
|
||||
item_IDFile = IDFile;
|
||||
|
||||
// See SendItemAnimation() for some notes on this struct
|
||||
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SomeItemPacketMaybe, sizeof(Arrow_Struct));
|
||||
@@ -1384,7 +1397,7 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
|
||||
as->item_id = item->ID;
|
||||
as->item_type = item_type;
|
||||
as->skill = 0; // Doesn't seem to have any effect
|
||||
strn0cpy(as->model_name, item->IDFile, 16);
|
||||
strn0cpy(as->model_name, item_IDFile, 16);
|
||||
as->velocity = speed;
|
||||
as->launch_angle = angle;
|
||||
as->tilt = tilt;
|
||||
@@ -1397,7 +1410,6 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
|
||||
|
||||
}
|
||||
|
||||
|
||||
void NPC::DoClassAttacks(Mob *target) {
|
||||
if(target == nullptr)
|
||||
return; //gotta have a target for all these
|
||||
|
||||
+256
-73
@@ -284,20 +284,31 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Percental Heal: %+i (%d%% max)", spell.max[i], effect_value);
|
||||
#endif
|
||||
//im not 100% sure about this implementation.
|
||||
//the spell value forumula dosent work for these... at least spell 3232 anyways
|
||||
int32 val = spell.max[i];
|
||||
int32 val = GetMaxHP() * spell.base[i] / 100;
|
||||
|
||||
if(caster)
|
||||
val = caster->GetActSpellHealing(spell_id, val, this);
|
||||
//This effect can also do damage by percent.
|
||||
if (val < 0) {
|
||||
|
||||
int32 mhp = GetMaxHP();
|
||||
int32 cap = mhp * spell.base[i] / 100;
|
||||
if (-val > spell.max[i])
|
||||
val = -spell.max[i];
|
||||
|
||||
if(cap < val)
|
||||
val = cap;
|
||||
if (caster)
|
||||
val = caster->GetActSpellDamage(spell_id, val, this);
|
||||
|
||||
if(val > 0)
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (val > spell.max[i])
|
||||
val = spell.max[i];
|
||||
|
||||
if(caster)
|
||||
val = caster->GetActSpellHealing(spell_id, val, this);
|
||||
}
|
||||
|
||||
if (val < 0)
|
||||
Damage(caster, -val, spell_id, spell.skill, false, buffslot, false);
|
||||
else
|
||||
HealDamage(val, caster);
|
||||
|
||||
break;
|
||||
@@ -326,7 +337,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
if(inuse)
|
||||
break;
|
||||
|
||||
Heal();
|
||||
int32 val = 0;
|
||||
val = 7500*effect_value;
|
||||
val = caster->GetActSpellHealing(spell_id, val, this);
|
||||
|
||||
if (val > 0)
|
||||
HealDamage(val, caster);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -396,10 +413,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
}
|
||||
|
||||
case SE_Succor:
|
||||
{
|
||||
{
|
||||
|
||||
float x, y, z, heading;
|
||||
const char *target_zone;
|
||||
|
||||
|
||||
x = spell.base[1];
|
||||
y = spell.base[0];
|
||||
z = spell.base[2];
|
||||
@@ -426,6 +444,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
|
||||
if(IsClient())
|
||||
{
|
||||
if(MakeRandomInt(0, 99) < RuleI(Spells, SuccorFailChance)) { //2% Fail chance by default
|
||||
|
||||
if(IsClient()) {
|
||||
CastToClient()->Message_StringID(MT_SpellFailure,SUCCOR_FAIL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Below are the spellid's for known evac/succor spells that send player
|
||||
// to the current zone's safe points.
|
||||
|
||||
@@ -441,10 +467,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
LogFile->write(EQEMuLog::Debug, "Succor/Evacuation Spell In Same Zone.");
|
||||
#endif
|
||||
if(IsClient())
|
||||
CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), x, y, z, heading, 0, EvacToSafeCoords);
|
||||
else
|
||||
GMMove(x, y, z, heading);
|
||||
if(IsClient())
|
||||
CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), x, y, z, heading, 0, EvacToSafeCoords);
|
||||
else
|
||||
GMMove(x, y, z, heading);
|
||||
}
|
||||
else {
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
@@ -457,7 +483,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
|
||||
break;
|
||||
}
|
||||
case SE_YetAnotherGate: //Shin: Used on Teleport Bind.
|
||||
case SE_GateCastersBindpoint: //Shin: Used on Teleport Bind.
|
||||
case SE_Teleport: // gates, rings, circles, etc
|
||||
case SE_Teleport2:
|
||||
{
|
||||
@@ -489,7 +515,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
}
|
||||
}
|
||||
|
||||
if (effect == SE_YetAnotherGate && caster->IsClient())
|
||||
if (effect == SE_GateCastersBindpoint && caster->IsClient())
|
||||
{ //Shin: Teleport Bind uses caster's bind point
|
||||
x = caster->CastToClient()->GetBindX();
|
||||
y = caster->CastToClient()->GetBindY();
|
||||
@@ -830,14 +856,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Fear: %+i", effect_value);
|
||||
#endif
|
||||
//use resistance value for duration...
|
||||
buffs[buffslot].ticsremaining = ((buffs[buffslot].ticsremaining * partial) / 100);
|
||||
|
||||
if(IsClient())
|
||||
{
|
||||
if(buffs[buffslot].ticsremaining > RuleI(Character, MaxFearDurationForPlayerCharacter))
|
||||
buffs[buffslot].ticsremaining = RuleI(Character, MaxFearDurationForPlayerCharacter);
|
||||
}
|
||||
|
||||
|
||||
if(RuleB(Combat, EnableFearPathing)){
|
||||
if(IsClient())
|
||||
@@ -859,7 +883,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_BindAffinity:
|
||||
case SE_BindAffinity: //TO DO: Add support for secondary and tertiary gate abilities
|
||||
{
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Bind Affinity");
|
||||
@@ -991,13 +1015,18 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Gate:
|
||||
case SE_Gate: //TO DO: Add support for secondary and tertiary gate abilities (base2)
|
||||
{
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Gate");
|
||||
#endif
|
||||
if(!spellbonuses.AntiGate)
|
||||
Gate();
|
||||
if(!spellbonuses.AntiGate){
|
||||
|
||||
if(MakeRandomInt(0, 99) < effect_value)
|
||||
Gate();
|
||||
else
|
||||
caster->Message_StringID(MT_SpellFailure,GATE_FAIL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1242,7 +1271,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
#endif
|
||||
effect_value = ApplySpellEffectiveness(caster, spell_id, effect_value);
|
||||
buffs[buffslot].melee_rune = effect_value;
|
||||
SetHasRune(true);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1251,17 +1279,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Spell Absorb Rune: %+i", effect_value);
|
||||
#endif
|
||||
if(effect_value > 0) {
|
||||
buffs[buffslot].magic_rune = effect_value;
|
||||
SetHasSpellRune(true);
|
||||
}
|
||||
if(effect_value > 0)
|
||||
buffs[buffslot].magic_rune = effect_value;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_MitigateMeleeDamage:
|
||||
{
|
||||
buffs[buffslot].melee_rune = GetPartialMeleeRuneAmount(spell_id);
|
||||
SetHasPartialMeleeRune(true);
|
||||
buffs[buffslot].melee_rune = spells[spell_id].max[i];
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1279,8 +1305,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
|
||||
case SE_MitigateSpellDamage:
|
||||
{
|
||||
buffs[buffslot].magic_rune = GetPartialMagicRuneAmount(spell_id);
|
||||
SetHasPartialSpellRune(true);
|
||||
buffs[buffslot].magic_rune = spells[spell_id].max[i];
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1384,7 +1409,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
(
|
||||
spell.base[i],
|
||||
Mob::GetDefaultGender(spell.base[i], GetGender()),
|
||||
spell.base2[i]
|
||||
spell.base2[i],
|
||||
spell.max[i]
|
||||
);
|
||||
if(spell.base[i] == OGRE){
|
||||
SendAppearancePacket(AT_Size, 9);
|
||||
@@ -1560,8 +1586,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
if(spell_id == 2488) //Dook- Lifeburn fix
|
||||
break;
|
||||
|
||||
if(IsClient())
|
||||
CastToClient()->SetFeigned(true);
|
||||
if(IsClient()) {
|
||||
|
||||
if (MakeRandomInt(0, 99) > spells[spell_id].base[i]) {
|
||||
CastToClient()->SetFeigned(false);
|
||||
entity_list.MessageClose_StringID(this, false, 200, 10, STRING_FEIGNFAILED, GetName());
|
||||
}
|
||||
else
|
||||
CastToClient()->SetFeigned(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1631,12 +1664,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
snprintf(effect_desc, _EDLEN, "Root: %+i", effect_value);
|
||||
#endif
|
||||
rooted = true;
|
||||
rooted_mod = 0;
|
||||
|
||||
if (caster){
|
||||
rooted_mod = caster->aabonuses.RootBreakChance +
|
||||
caster->itembonuses.RootBreakChance +
|
||||
caster->spellbonuses.RootBreakChance;
|
||||
buffs[buffslot].RootBreakChance = caster->aabonuses.RootBreakChance +
|
||||
caster->itembonuses.RootBreakChance +
|
||||
caster->spellbonuses.RootBreakChance;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1651,6 +1683,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
{
|
||||
CastToClient()->SummonHorse(spell_id);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1698,19 +1732,25 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
|
||||
// Now we should either be casting this on self or its being cast on a valid group member
|
||||
if(TargetClient) {
|
||||
Corpse *corpse = entity_list.GetCorpseByOwner(TargetClient);
|
||||
if(corpse) {
|
||||
if(TargetClient == this->CastToClient())
|
||||
Message_StringID(4, SUMMONING_CORPSE, TargetClient->CastToMob()->GetCleanName());
|
||||
else
|
||||
Message_StringID(4, SUMMONING_CORPSE_OTHER, TargetClient->CastToMob()->GetCleanName());
|
||||
|
||||
corpse->Summon(CastToClient(), true, true);
|
||||
}
|
||||
else {
|
||||
// No corpse found in the zone
|
||||
Message_StringID(4, CORPSE_CANT_SENSE);
|
||||
if (TargetClient->GetLevel() <= effect_value){
|
||||
|
||||
Corpse *corpse = entity_list.GetCorpseByOwner(TargetClient);
|
||||
if(corpse) {
|
||||
if(TargetClient == this->CastToClient())
|
||||
Message_StringID(4, SUMMONING_CORPSE, TargetClient->CastToMob()->GetCleanName());
|
||||
else
|
||||
Message_StringID(4, SUMMONING_CORPSE_OTHER, TargetClient->CastToMob()->GetCleanName());
|
||||
|
||||
corpse->Summon(CastToClient(), true, true);
|
||||
}
|
||||
else {
|
||||
// No corpse found in the zone
|
||||
Message_StringID(4, CORPSE_CANT_SENSE);
|
||||
}
|
||||
}
|
||||
else
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_LEVEL_REQ);
|
||||
}
|
||||
else {
|
||||
Message_StringID(4, TARGET_NOT_FOUND);
|
||||
@@ -2597,14 +2637,37 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
HealDamage(dmg, caster);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Taunt:
|
||||
{
|
||||
if (IsNPC())
|
||||
caster->Taunt(this->CastToNPC(), false, spell.base[i]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_AttackSpeed:
|
||||
if (spell.base[i] < 100)
|
||||
SlowMitigation(caster);
|
||||
break;
|
||||
|
||||
case SE_AttackSpeed2:
|
||||
if (spell.base[i] < 100)
|
||||
SlowMitigation(caster);
|
||||
break;
|
||||
|
||||
case SE_AttackSpeed3:
|
||||
if (spell.base[i] < 0)
|
||||
SlowMitigation(caster);
|
||||
break;
|
||||
|
||||
case SE_AttackSpeed4:
|
||||
SlowMitigation(caster);
|
||||
break;
|
||||
|
||||
// Handled Elsewhere
|
||||
case SE_ImmuneFleeing:
|
||||
case SE_NegateSpellEffect:
|
||||
@@ -2690,10 +2753,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
case SE_DivineSave:
|
||||
case SE_Accuracy:
|
||||
case SE_Flurry:
|
||||
case SE_AttackSpeed:
|
||||
case SE_AttackSpeed2:
|
||||
case SE_AttackSpeed3:
|
||||
case SE_AttackSpeed4:
|
||||
case SE_ImprovedDamage:
|
||||
case SE_ImprovedHeal:
|
||||
case SE_IncreaseSpellHaste:
|
||||
@@ -3230,7 +3289,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
|
||||
if(caster)
|
||||
effect_value = caster->GetActSpellHealing(spell_id, effect_value);
|
||||
|
||||
HealDamage(effect_value, caster);
|
||||
HealDamage(effect_value, caster, spell_id);
|
||||
//healing aggro would go here; removed for now
|
||||
break;
|
||||
}
|
||||
@@ -3322,9 +3381,36 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
|
||||
}
|
||||
|
||||
case SE_Root: {
|
||||
float SpellEffectiveness = ResistSpell(spells[spell_id].resisttype, spell_id, caster);
|
||||
if(SpellEffectiveness < 25) {
|
||||
BuffFadeByEffect(SE_Root);
|
||||
|
||||
/* Root formula derived from extensive personal live parses - Kayen
|
||||
ROOT has a 70% chance to do a resist check to break.
|
||||
*/
|
||||
|
||||
if (MakeRandomInt(0, 99) < RuleI(Spells, RootBreakCheckChance)){
|
||||
|
||||
float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, 0,0,0,0,true);
|
||||
|
||||
if(resist_check == 100)
|
||||
break;
|
||||
else
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Fear:
|
||||
{
|
||||
if (MakeRandomInt(0, 99) < RuleI(Spells, FearBreakCheckChance)){
|
||||
|
||||
float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster);
|
||||
|
||||
if(resist_check == 100)
|
||||
break;
|
||||
else
|
||||
if(!TryFadeEffect(slot))
|
||||
BuffFadeBySlot(slot);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -3694,8 +3780,8 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
|
||||
|
||||
case SE_Root:
|
||||
{
|
||||
buffs[slot].RootBreakChance = 0;
|
||||
rooted = false;
|
||||
rooted_mod = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3913,6 +3999,15 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
|
||||
|
||||
bool LimitFailure = false;
|
||||
bool LimitInclude[MaxLimitInclude] = { false };
|
||||
/* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells.
|
||||
0/1 SE_LimitResist
|
||||
2/3 SE_LimitSpell
|
||||
4/5 SE_LimitEffect
|
||||
6/7 SE_LimitTarget
|
||||
8/9 SE_LimitSpellGroup:
|
||||
10/11 SE_LimitCastingSkill:
|
||||
Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes
|
||||
*/
|
||||
int FocusCount = 0;
|
||||
|
||||
std::map<uint32, std::map<uint32, AA_Ability> >::const_iterator find_iter = aa_effects.find(aa_ID);
|
||||
@@ -3927,7 +4022,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
|
||||
base1 = iter->second.base1;
|
||||
base2 = iter->second.base2;
|
||||
slot = iter->second.slot;
|
||||
|
||||
|
||||
/*
|
||||
AA Foci's can contain multiple focus effects within the same AA.
|
||||
To handle this we will not automatically return zero if a limit is found.
|
||||
@@ -3981,8 +4076,11 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
|
||||
break;
|
||||
|
||||
case SE_LimitInstant:
|
||||
if(spell.buffduration)
|
||||
if(base1 == 1 && spell.buffduration) //Fail if not instant
|
||||
LimitFailure = true;
|
||||
if(base1 == 0 && (spell.buffduration == 0)) //Fail if instant
|
||||
LimitFailure = true;
|
||||
|
||||
break;
|
||||
|
||||
case SE_LimitMaxLevel:
|
||||
@@ -4030,13 +4128,14 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
|
||||
case SE_LimitMinDur:
|
||||
if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration))
|
||||
LimitFailure = true;
|
||||
break;
|
||||
|
||||
break;
|
||||
|
||||
case SE_LimitEffect:
|
||||
if(base1 < 0){
|
||||
if(IsEffectInSpell(spell_id,-base1)) //Exclude
|
||||
LimitFailure = true;
|
||||
}
|
||||
}
|
||||
else{
|
||||
LimitInclude[4] = true;
|
||||
if(IsEffectInSpell(spell_id,base1)) //Include
|
||||
@@ -4081,10 +4180,11 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
|
||||
break;
|
||||
|
||||
case SE_LimitCombatSkills:
|
||||
if (base1 == 0 && IsCombatSkill(spell_id)) //Exclude Discs
|
||||
if (base1 == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) //Exclude Discs / Procs
|
||||
LimitFailure = true;
|
||||
else if (base1 == 1 && !IsCombatSkill(spell_id)) //Exclude Spells
|
||||
else if (base1 == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) //Exclude Spells
|
||||
LimitFailure = true;
|
||||
|
||||
break;
|
||||
|
||||
case SE_LimitSpellGroup:
|
||||
@@ -4115,7 +4215,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
|
||||
//Do not use this limit more then once per spell. If multiple class, treat value like items would.
|
||||
if (!PassLimitClass(base1, GetClass()))
|
||||
LimitFailure = true;
|
||||
break;
|
||||
break;
|
||||
|
||||
case SE_LimitRace:
|
||||
if (base1 != GetRace())
|
||||
@@ -4376,8 +4476,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
break;
|
||||
|
||||
case SE_LimitInstant:
|
||||
if(spell.buffduration)
|
||||
if(focus_spell.base[i] == 1 && spell.buffduration) //Fail if not instant
|
||||
return 0;
|
||||
if(focus_spell.base[i] == 0 && (spell.buffduration == 0)) //Fail if instant
|
||||
return 0;
|
||||
|
||||
break;
|
||||
|
||||
case SE_LimitMaxLevel:
|
||||
@@ -4482,10 +4585,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
break;
|
||||
|
||||
case SE_LimitCombatSkills:
|
||||
if (focus_spell.base[i] == 0 && IsCombatSkill(spell_id)) //Exclude Disc
|
||||
return 0;
|
||||
else if (focus_spell.base[i] == 1 && !IsCombatSkill(spell_id)) //Include Spells
|
||||
if (focus_spell.base[i] == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) //Exclude Discs / Procs
|
||||
return 0;
|
||||
else if (focus_spell.base[i] == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) //Exclude Spells
|
||||
return 0;
|
||||
|
||||
break;
|
||||
|
||||
case SE_LimitSpellGroup:
|
||||
@@ -5963,3 +6067,82 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){
|
||||
|
||||
/*For mage 'Bolt' line and other various spells.
|
||||
-This is mostly accurate for how the modern clients handle this effect.
|
||||
-It was changed at some point to use an actual projectile as done here (opposed to a particle effect in classic)
|
||||
-The projectile graphic appears to be that of 'Ball of Sunlight' ID 80648 and will be visible to anyone in SoF+
|
||||
-There is no LOS check to prevent a bolt from being cast. If you don't have LOS your bolt simply goes into whatever barrier
|
||||
and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target.
|
||||
-If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles).
|
||||
-The way this is written once a bolt is cast a timer checks the distance from the initial cast to the target repeatedly
|
||||
and calculates at what predicted time the bolt should hit that target in client_process (therefore accounting for any target movement).
|
||||
When bolt hits its predicted point the damage is then done to target.
|
||||
Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant.
|
||||
Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units.
|
||||
Pending Implementation: What this code can not do is prevent damage if the bolt hits a barrier after passing the initial LOS check
|
||||
because the target has moved while the bolt is in motion. (it is rare to actual get this to occur on live in normal game play)
|
||||
*/
|
||||
|
||||
if (!spell_target)
|
||||
return false;
|
||||
|
||||
uint8 anim = spells[spell_id].CastingAnim;
|
||||
int bolt_id = -1;
|
||||
|
||||
//Make sure there is an avialable bolt to be cast.
|
||||
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) {
|
||||
if (projectile_spell_id[i] == 0){
|
||||
bolt_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bolt_id < 0)
|
||||
return false;
|
||||
|
||||
if (CheckLosFN(spell_target)) {
|
||||
|
||||
projectile_spell_id[bolt_id] = spell_id;
|
||||
projectile_target_id[bolt_id] = spell_target->GetID();
|
||||
projectile_x[bolt_id] = GetX(), projectile_y[bolt_id] = GetY(), projectile_z[bolt_id] = GetZ();
|
||||
projectile_increment[bolt_id] = 1;
|
||||
projectile_timer.Start(250);
|
||||
}
|
||||
|
||||
//This will use the correct graphic as defined in the player_1 field of spells_new table. Found in UF+ spell files.
|
||||
if (RuleB(Spells, UseLiveSpellProjectileGFX)) {
|
||||
ProjectileAnimation(spell_target,0, false, 1.5,0,0,0, spells[spell_id].player_1);
|
||||
}
|
||||
|
||||
//This allows limited support for server using older spell files that do not contain data for bolt graphics.
|
||||
else {
|
||||
//Only use fire graphic for fire spells.
|
||||
if (spells[spell_id].resisttype == RESIST_FIRE) {
|
||||
|
||||
if (IsClient()){
|
||||
if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic.
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5);
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5);
|
||||
}
|
||||
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5);
|
||||
|
||||
}
|
||||
|
||||
//Default to an arrow if not using a mage bolt (Use up to date spell file and enable above rules for best results)
|
||||
else
|
||||
ProjectileAnimation(spell_target,0, 1, 1.5);
|
||||
}
|
||||
|
||||
if (spells[spell_id].CastingAnim == 64)
|
||||
anim = 44; //Corrects for animation error.
|
||||
|
||||
DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); //Override the default projectile animation.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+240
-126
@@ -123,21 +123,12 @@ void NPC::SpellProcess()
|
||||
{
|
||||
Mob::SpellProcess();
|
||||
|
||||
if(GetSwarmInfo()){
|
||||
Mob *swp_o = GetSwarmInfo()->GetOwner();
|
||||
if(!swp_o)
|
||||
{
|
||||
if (GetSwarmInfo()) {
|
||||
if (GetSwarmInfo()->duration->Check(false))
|
||||
Depop();
|
||||
}
|
||||
|
||||
if(GetSwarmInfo()->duration->Check(false))
|
||||
{
|
||||
Depop();
|
||||
}
|
||||
|
||||
Mob *targMob = entity_list.GetMob(GetSwarmInfo()->target);
|
||||
if(GetSwarmInfo()->target != 0)
|
||||
{
|
||||
if (GetSwarmInfo()->target != 0) {
|
||||
if(!targMob || (targMob && targMob->IsCorpse()))
|
||||
Depop();
|
||||
}
|
||||
@@ -220,6 +211,9 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
return(false);
|
||||
}
|
||||
|
||||
if (spellbonuses.NegateIfCombat)
|
||||
BuffFadeByEffect(SE_NegateIfCombat);
|
||||
|
||||
if(IsClient() && GetTarget() && IsHarmonySpell(spell_id))
|
||||
{
|
||||
for(int i = 0; i < EFFECT_COUNT; i++) {
|
||||
@@ -563,6 +557,15 @@ uint16 Mob::GetSpecializeSkillValue(uint16 spell_id) const {
|
||||
}
|
||||
|
||||
void Client::CheckSpecializeIncrease(uint16 spell_id) {
|
||||
// These are not active because CheckIncreaseSkill() already does so.
|
||||
// It's such a rare occurance that adding them here is wasted..(ref only)
|
||||
/*
|
||||
if (IsDead() || IsUnconscious())
|
||||
return;
|
||||
if (IsAIControlled())
|
||||
return;
|
||||
*/
|
||||
|
||||
switch(spells[spell_id].skill) {
|
||||
case SkillAbjuration:
|
||||
CheckIncreaseSkill(SkillSpecializeAbjure, nullptr);
|
||||
@@ -586,6 +589,15 @@ void Client::CheckSpecializeIncrease(uint16 spell_id) {
|
||||
}
|
||||
|
||||
void Client::CheckSongSkillIncrease(uint16 spell_id){
|
||||
// These are not active because CheckIncreaseSkill() already does so.
|
||||
// It's such a rare occurance that adding them here is wasted..(ref only)
|
||||
/*
|
||||
if (IsDead() || IsUnconscious())
|
||||
return;
|
||||
if (IsAIControlled())
|
||||
return;
|
||||
*/
|
||||
|
||||
switch(spells[spell_id].skill)
|
||||
{
|
||||
case SkillSinging:
|
||||
@@ -1818,7 +1830,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
}
|
||||
|
||||
// check line of sight to target if it's a detrimental spell
|
||||
if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id))
|
||||
if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id) && spells[spell_id].targettype != ST_TargetOptional)
|
||||
{
|
||||
mlog(SPELLS__CASTING, "Spell %d: cannot see target %s", spell_target->GetName());
|
||||
Message_StringID(13,CANT_SEE_TARGET);
|
||||
@@ -1883,7 +1895,12 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
if (isproc) {
|
||||
SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true);
|
||||
} else {
|
||||
if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) {
|
||||
if (spells[spell_id].targettype == ST_TargetOptional){
|
||||
if (!TrySpellProjectile(spell_target, spell_id))
|
||||
return false;
|
||||
}
|
||||
|
||||
else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) {
|
||||
if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) {
|
||||
// Prevent mana usage/timers being set for beneficial buffs
|
||||
if(casting_spell_type == 1)
|
||||
@@ -1892,6 +1909,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(IsPlayerIllusionSpell(spell_id)
|
||||
&& IsClient()
|
||||
&& CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){
|
||||
@@ -2595,6 +2613,14 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
|
||||
{
|
||||
effect1 = sp1.effectid[i];
|
||||
effect2 = sp2.effectid[i];
|
||||
|
||||
if (spellbonuses.Screech == 1) {
|
||||
if (effect2 == SE_Screech && sp2.base[i] == -1) {
|
||||
Message_StringID(MT_SpellFailure, SCREECH_BUFF_BLOCK, sp2.name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if(effect2 == SE_StackingCommand_Overwrite)
|
||||
{
|
||||
overwrite_effect = sp2.base[i];
|
||||
@@ -2639,7 +2665,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
|
||||
mlog(SPELLS__STACKING, "%s (%d) blocks effect %d on slot %d below %d, but we do not have that effect on that slot. Ignored.",
|
||||
sp1.name, spellid1, blocked_effect, blocked_slot, blocked_below_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mlog(SPELLS__STACKING, "%s (%d) and %s (%d) appear to be in the same line, skipping Stacking Overwrite/Blocking checks",
|
||||
@@ -2960,6 +2986,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
|
||||
buffs[emptyslot].caston_z = 0;
|
||||
buffs[emptyslot].dot_rune = 0;
|
||||
buffs[emptyslot].ExtraDIChance = 0;
|
||||
buffs[emptyslot].RootBreakChance = 0;
|
||||
|
||||
if (level_override > 0) {
|
||||
buffs[emptyslot].UpdateClient = true;
|
||||
@@ -3417,14 +3444,25 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
|
||||
// not all unresistable, so changing this to only check certain spells
|
||||
if(IsResistableSpell(spell_id))
|
||||
{
|
||||
spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust);
|
||||
if (IsCharmSpell(spell_id) || IsMezSpell(spell_id) || IsFearSpell(spell_id))
|
||||
spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust,true);
|
||||
else
|
||||
spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust);
|
||||
|
||||
if(spell_effectiveness < 100)
|
||||
{
|
||||
if(spell_effectiveness == 0 || !IsPartialCapableSpell(spell_id) )
|
||||
{
|
||||
mlog(SPELLS__RESISTS, "Spell %d was completely resisted by %s", spell_id, spelltar->GetName());
|
||||
Message_StringID(MT_SpellFailure, TARGET_RESISTED, spells[spell_id].name);
|
||||
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
|
||||
|
||||
if (spells[spell_id].resisttype == RESIST_PHYSICAL){
|
||||
Message_StringID(MT_SpellFailure, PHYSICAL_RESIST_FAIL,spells[spell_id].name);
|
||||
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
|
||||
}
|
||||
else {
|
||||
Message_StringID(MT_SpellFailure, TARGET_RESISTED, spells[spell_id].name);
|
||||
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
|
||||
}
|
||||
|
||||
if(spelltar->IsAIControlled()){
|
||||
int32 aggro = CheckAggroAmount(spell_id);
|
||||
@@ -4044,7 +4082,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
|
||||
// pvp_resist_base
|
||||
// pvp_resist_calc
|
||||
// pvp_resist_cap
|
||||
float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck)
|
||||
float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck, bool CharmTick, bool IsRoot)
|
||||
{
|
||||
|
||||
if(!caster)
|
||||
@@ -4083,8 +4121,10 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
|
||||
//Check for fear resist
|
||||
bool IsFear = false;
|
||||
if(IsFearSpell(spell_id))
|
||||
{
|
||||
IsFear = true;
|
||||
int fear_resist_bonuses = CalcFearResistChance();
|
||||
if(MakeRandomInt(0, 99) < fear_resist_bonuses)
|
||||
{
|
||||
@@ -4093,7 +4133,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
}
|
||||
|
||||
if (!CharismaCheck){
|
||||
if (!CharmTick){
|
||||
|
||||
//Check for Spell Effect specific resistance chances (ie AA Mental Fortitude)
|
||||
int se_resist_bonuses = GetSpellEffectResistChance(spell_id);
|
||||
@@ -4171,86 +4211,126 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
break;
|
||||
case RESIST_PHYSICAL:
|
||||
{
|
||||
if (IsNPC())
|
||||
target_resist = GetPhR();
|
||||
else
|
||||
target_resist = 0;
|
||||
}
|
||||
default:
|
||||
//This is guessed but the others are right
|
||||
target_resist = (GetSTA() / 4);
|
||||
|
||||
target_resist = 0;
|
||||
}
|
||||
|
||||
//Setup our base resist chance.
|
||||
//Lulls have a slightly higher chance to resist than normal 15/200 or ~ 7.5%
|
||||
int resist_chance;
|
||||
if(IsHarmonySpell(spell_id))
|
||||
{
|
||||
resist_chance = 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
resist_chance = 0;
|
||||
}
|
||||
int resist_chance = 0;
|
||||
int level_mod = 0;
|
||||
|
||||
//Adjust our resist chance based on level modifiers
|
||||
int temp_level_diff = GetLevel() - caster->GetLevel();
|
||||
if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||
if(a > 0)
|
||||
|
||||
//Physical Resists are calclated using their own formula derived from extensive parsing.
|
||||
if (resist_type == RESIST_PHYSICAL) {
|
||||
level_mod = ResistPhysical(temp_level_diff, caster->GetLevel());
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
temp_level_diff = a;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_level_diff = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15)
|
||||
{
|
||||
temp_level_diff = 15;
|
||||
}
|
||||
|
||||
if(IsNPC() && temp_level_diff < -9)
|
||||
{
|
||||
temp_level_diff = -9;
|
||||
}
|
||||
|
||||
int level_mod = temp_level_diff * temp_level_diff / 2;
|
||||
if(temp_level_diff < 0)
|
||||
{
|
||||
level_mod = -level_mod;
|
||||
}
|
||||
|
||||
if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20)
|
||||
{
|
||||
level_mod = 1000;
|
||||
}
|
||||
|
||||
//Even more level stuff this time dealing with damage spells
|
||||
if(IsNPC() && IsDamageSpell(spell_id) && GetLevel() >= 17)
|
||||
{
|
||||
int level_diff;
|
||||
if(GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
level_diff = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||
if(level_diff < 0)
|
||||
int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||
if(a > 0)
|
||||
{
|
||||
level_diff = 0;
|
||||
temp_level_diff = a;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_level_diff = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15)
|
||||
{
|
||||
level_diff = GetLevel() - caster->GetLevel();
|
||||
temp_level_diff = 15;
|
||||
}
|
||||
|
||||
if(IsNPC() && temp_level_diff < -9)
|
||||
{
|
||||
temp_level_diff = -9;
|
||||
}
|
||||
|
||||
level_mod = temp_level_diff * temp_level_diff / 2;
|
||||
if(temp_level_diff < 0)
|
||||
{
|
||||
level_mod = -level_mod;
|
||||
}
|
||||
|
||||
if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20)
|
||||
{
|
||||
level_mod = 1000;
|
||||
}
|
||||
|
||||
//Even more level stuff this time dealing with damage spells
|
||||
if(IsNPC() && IsDamageSpell(spell_id) && GetLevel() >= 17)
|
||||
{
|
||||
int level_diff;
|
||||
if(GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
level_diff = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||
if(level_diff < 0)
|
||||
{
|
||||
level_diff = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
level_diff = GetLevel() - caster->GetLevel();
|
||||
}
|
||||
level_mod += (2 * level_diff);
|
||||
}
|
||||
level_mod += (2 * level_diff);
|
||||
}
|
||||
|
||||
if (CharismaCheck)
|
||||
{
|
||||
//For charm chance to break checks, Default 10 CHA = -1 resist mod.
|
||||
int16 cha_resist_modifier = 0;
|
||||
cha_resist_modifier = caster->GetCHA()/RuleI(Spells, CharismaEffectiveness);
|
||||
resist_modifier -= cha_resist_modifier;
|
||||
/*
|
||||
Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 255 CHA (min ~ 75 cha)
|
||||
Charisma less than ~ 75 gives a postive modifier to resist checks at approximate ratio of -10 CHA = +6 Resist.
|
||||
Mez spells do same initial resist check as a above.
|
||||
Lull spells only check charisma if inital cast is resisted to see if mob will aggro, same modifier/cap as above.
|
||||
Charisma DOES NOT extend charm durations.
|
||||
Fear resist chance is given a -20 resist modifier if CHA is < 100, from 100-255 it progressively reduces the negative mod to 0.
|
||||
Fears verse undead DO NOT apply a charisma modifer. (Note: unknown Base1 values defined in undead fears do not effect duration).
|
||||
*/
|
||||
int16 charisma = caster->GetCHA();
|
||||
|
||||
if (IsFear && (spells[spell_id].targettype != 10)){
|
||||
|
||||
if (charisma < 100)
|
||||
resist_modifier -= 20;
|
||||
|
||||
else if (charisma <= 255)
|
||||
resist_modifier += (charisma - 100)/8;
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
if (charisma >= 75){
|
||||
|
||||
if (charisma > RuleI(Spells, CharismaEffectivenessCap))
|
||||
charisma = RuleI(Spells, CharismaEffectivenessCap);
|
||||
|
||||
resist_modifier -= (charisma - 75)/RuleI(Spells, CharismaEffectiveness);
|
||||
}
|
||||
else
|
||||
resist_modifier += ((75 - charisma)/10) * 6; //Increase Resist Chance
|
||||
}
|
||||
}
|
||||
|
||||
//Lull spells DO NOT use regular resists on initial cast, instead they use a flat +15 modifier. Live parses confirm this.
|
||||
//Regular resists are used when checking if mob will aggro off of a lull resist.
|
||||
if(!CharismaCheck && IsHarmonySpell(spell_id))
|
||||
target_resist = 15;
|
||||
|
||||
//Add our level, resist and -spell resist modifier to our roll chance
|
||||
resist_chance += level_mod;
|
||||
resist_chance += resist_modifier;
|
||||
@@ -4269,6 +4349,26 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
resist_chance = spells[spell_id].MinResist;
|
||||
}
|
||||
|
||||
//Average charm duration agianst mobs with 0% chance to resist on LIVE is ~ 68 ticks.
|
||||
//Minimum resist chance should be caclulated factoring in the RuleI(Spells, CharmBreakCheckChance)
|
||||
if (CharmTick) {
|
||||
|
||||
int min_charmbreakchance = ((100/RuleI(Spells, CharmBreakCheckChance))/66 * 100)*2;
|
||||
|
||||
if (resist_chance < min_charmbreakchance)
|
||||
resist_chance = min_charmbreakchance;
|
||||
}
|
||||
|
||||
//Average root duration agianst mobs with 0% chance to resist on LIVE is ~ 22 ticks (6% resist chance).
|
||||
//Minimum resist chance should be caclulated factoring in the RuleI(Spells, RootBreakCheckChance)
|
||||
if (IsRoot) {
|
||||
|
||||
int min_rootbreakchance = ((100/RuleI(Spells, RootBreakCheckChance))/22 * 100)*2;
|
||||
|
||||
if (resist_chance < min_rootbreakchance)
|
||||
resist_chance = min_rootbreakchance;
|
||||
}
|
||||
|
||||
//Finally our roll
|
||||
int roll = MakeRandomInt(0, 200);
|
||||
if(roll > resist_chance)
|
||||
@@ -4334,6 +4434,43 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
}
|
||||
|
||||
int Mob::ResistPhysical(int level_diff, uint8 caster_level)
|
||||
{
|
||||
/* Physical resists use the standard level mod calculation in
|
||||
conjunction with a resist fall off formula that greatly prevents you
|
||||
from landing abilities on mobs that are higher level than you.
|
||||
After level 12, every 4 levels gained the max level you can hit
|
||||
your target without a sharp resist penalty is raised by 1.
|
||||
Extensive parsing confirms this, along with baseline phyiscal resist rates used.
|
||||
*/
|
||||
|
||||
|
||||
if (level_diff == 0)
|
||||
return level_diff;
|
||||
|
||||
int level_mod = 0;
|
||||
|
||||
if (level_diff > 0) {
|
||||
|
||||
int ResistFallOff = 0;
|
||||
|
||||
if (caster_level <= 12)
|
||||
ResistFallOff = 3;
|
||||
else
|
||||
ResistFallOff = caster_level/4;
|
||||
|
||||
if (level_diff > ResistFallOff || level_diff >= 15)
|
||||
level_mod = ((level_diff * 10) + level_diff)*2;
|
||||
else
|
||||
level_mod = level_diff * level_diff / 2;
|
||||
}
|
||||
|
||||
else
|
||||
level_mod = -(level_diff * level_diff / 2);
|
||||
|
||||
return level_mod;
|
||||
}
|
||||
|
||||
int16 Mob::CalcResistChanceBonus()
|
||||
{
|
||||
int resistchance = spellbonuses.ResistSpellChance + itembonuses.ResistSpellChance;
|
||||
@@ -4829,6 +4966,28 @@ bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::IsCombatProc(uint16 spell_id) {
|
||||
|
||||
if (RuleB(Spells, FocusCombatProcs))
|
||||
return false;
|
||||
|
||||
if(spell_id == SPELL_UNKNOWN)
|
||||
return(false);
|
||||
|
||||
if ((spells[spell_id].cast_time == 0) && (spells[spell_id].recast_time == 0) && (spells[spell_id].recovery_time == 0))
|
||||
{
|
||||
|
||||
for (int i = 0; i < MAX_PROCS; i++){
|
||||
if (PermaProcs[i].spellID == spell_id || SpellProcs[i].spellID == spell_id
|
||||
|| SkillProcs[i].spellID == spell_id || RangedProcs[i].spellID == spell_id){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 base_spell_id) {
|
||||
if(spell_id == SPELL_UNKNOWN)
|
||||
return(false);
|
||||
@@ -5132,51 +5291,6 @@ void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
void Mob::UpdateRuneFlags()
|
||||
{
|
||||
bool Has_SE_Rune = false, Has_SE_AbsorbMagicAtt = false, Has_SE_MitigateMeleeDamage = false, Has_SE_MitigateSpellDamage = false;
|
||||
uint32 buff_count = GetMaxTotalSlots();
|
||||
for (unsigned int i = 0; i < buff_count; ++i)
|
||||
{
|
||||
if (buffs[i].spellid != SPELL_UNKNOWN)
|
||||
{
|
||||
for (int j = 0; j < EFFECT_COUNT; ++j)
|
||||
{
|
||||
switch(spells[buffs[i].spellid].effectid[j])
|
||||
{
|
||||
case SE_Rune:
|
||||
{
|
||||
Has_SE_Rune = true;
|
||||
break;
|
||||
}
|
||||
case SE_AbsorbMagicAtt:
|
||||
{
|
||||
Has_SE_AbsorbMagicAtt = true;
|
||||
break;
|
||||
}
|
||||
case SE_MitigateMeleeDamage:
|
||||
{
|
||||
Has_SE_MitigateMeleeDamage = true;
|
||||
break;
|
||||
}
|
||||
case SE_MitigateSpellDamage:
|
||||
{
|
||||
Has_SE_MitigateSpellDamage = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetHasRune(Has_SE_Rune);
|
||||
SetHasSpellRune(Has_SE_AbsorbMagicAtt);
|
||||
SetHasPartialMeleeRune(Has_SE_MitigateMeleeDamage);
|
||||
SetHasPartialSpellRune(Has_SE_MitigateSpellDamage);
|
||||
}
|
||||
|
||||
int Client::GetCurrentBuffSlots() const
|
||||
{
|
||||
|
||||
+9
-8
@@ -1972,14 +1972,6 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T
|
||||
Task->Activity[ActivityID].GoalCount,
|
||||
ActivityID);
|
||||
|
||||
if(Task->Activity[ActivityID].GoalMethod != METHODQUEST)
|
||||
{
|
||||
char buf[24];
|
||||
snprintf(buf, 23, "%d %d", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID);
|
||||
buf[23] = '\0';
|
||||
parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, c, buf, 0);
|
||||
}
|
||||
|
||||
// Flag the activity as complete
|
||||
ActiveTasks[TaskIndex].Activity[ActivityID].State = ActivityCompleted;
|
||||
// Unlock subsequent activities for this task
|
||||
@@ -1991,6 +1983,15 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T
|
||||
taskmanager->SendSingleActiveTaskToClient(c, TaskIndex, TaskComplete, false);
|
||||
// Inform the client the task has been updated, both by a chat message
|
||||
c->Message(0, "Your task '%s' has been updated.", Task->Title);
|
||||
|
||||
if(Task->Activity[ActivityID].GoalMethod != METHODQUEST)
|
||||
{
|
||||
char buf[24];
|
||||
snprintf(buf, 23, "%d %d", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID);
|
||||
buf[23] = '\0';
|
||||
parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, c, buf, 0);
|
||||
}
|
||||
|
||||
// If this task is now complete, the Completed tasks will have been
|
||||
// updated in UnlockActivities. Send the completed task list to the
|
||||
// client. This is the same sequence the packets are sent on live.
|
||||
|
||||
+9
-10
@@ -1047,6 +1047,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
"npc_types.FR,"
|
||||
"npc_types.PR,"
|
||||
"npc_types.Corrup,"
|
||||
"npc_types.PhR,"
|
||||
"npc_types.mindmg,"
|
||||
"npc_types.maxdmg,"
|
||||
"npc_types.attack_count,"
|
||||
@@ -1098,7 +1099,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
"npc_types.underwater,"
|
||||
"npc_types.emoteid,"
|
||||
"npc_types.spellscale,"
|
||||
"npc_types.healscale";
|
||||
"npc_types.healscale,"
|
||||
"npc_types.no_target_hotkey";
|
||||
|
||||
MakeAnyLenString(&query, "%s FROM npc_types WHERE id=%d", basic_query, id);
|
||||
|
||||
@@ -1143,6 +1145,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
tmpNPCType->FR = atoi(row[r++]);
|
||||
tmpNPCType->PR = atoi(row[r++]);
|
||||
tmpNPCType->Corrup = atoi(row[r++]);
|
||||
tmpNPCType->PhR = atoi(row[r++]);
|
||||
tmpNPCType->min_dmg = atoi(row[r++]);
|
||||
tmpNPCType->max_dmg = atoi(row[r++]);
|
||||
tmpNPCType->attack_count = atoi(row[r++]);
|
||||
@@ -1189,7 +1192,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF) << 8;
|
||||
tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF);
|
||||
tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0;
|
||||
|
||||
|
||||
int i;
|
||||
if (armor_tint_id > 0)
|
||||
{
|
||||
@@ -1270,7 +1273,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
tmpNPCType->see_improved_hide = atoi(row[r++])==0?false:true;
|
||||
tmpNPCType->ATK = atoi(row[r++]);
|
||||
tmpNPCType->accuracy_rating = atoi(row[r++]);
|
||||
tmpNPCType->slow_mitigation = atof(row[r++]);
|
||||
tmpNPCType->slow_mitigation = atoi(row[r++]);
|
||||
tmpNPCType->maxlevel = atoi(row[r++]);
|
||||
tmpNPCType->scalerate = atoi(row[r++]);
|
||||
tmpNPCType->private_corpse = atoi(row[r++]) == 1 ? true : false;
|
||||
@@ -1279,7 +1282,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
tmpNPCType->emoteid = atoi(row[r++]);
|
||||
tmpNPCType->spellscale = atoi(row[r++]);
|
||||
tmpNPCType->healscale = atoi(row[r++]);
|
||||
|
||||
tmpNPCType->no_target_hotkey = atoi(row[r++]) == 1 ? true : false;
|
||||
|
||||
// If NPC with duplicate NPC id already in table,
|
||||
// free item we attempted to add.
|
||||
if (zone->npctable.find(tmpNPCType->npc_id) != zone->npctable.end())
|
||||
@@ -2654,13 +2658,8 @@ void ZoneDatabase::LoadBuffs(Client *c) {
|
||||
buffs[slot_id].caston_y = caston_y;
|
||||
buffs[slot_id].caston_z = caston_z;
|
||||
buffs[slot_id].ExtraDIChance = ExtraDIChance;
|
||||
buffs[slot_id].RootBreakChance = 0;
|
||||
buffs[slot_id].UpdateClient = false;
|
||||
if(IsRuneSpell(spell_id)) {
|
||||
c->SetHasRune(true);
|
||||
}
|
||||
else if(IsMagicRuneSpell(spell_id)) {
|
||||
c->SetHasSpellRune(true);
|
||||
}
|
||||
|
||||
}
|
||||
mysql_free_result(result);
|
||||
|
||||
+3
-1
@@ -75,6 +75,7 @@ struct NPCType
|
||||
int16 PR;
|
||||
int16 DR;
|
||||
int16 Corrup;
|
||||
int16 PhR;
|
||||
uint8 haircolor;
|
||||
uint8 beardcolor;
|
||||
uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye?
|
||||
@@ -110,7 +111,7 @@ struct NPCType
|
||||
int accuracy_rating; //10 = 1% accuracy
|
||||
bool findable; //can be found with find command
|
||||
bool trackable;
|
||||
float slow_mitigation; // Slow mitigation % in decimal form.
|
||||
int16 slow_mitigation;
|
||||
uint8 maxlevel;
|
||||
uint32 scalerate;
|
||||
bool private_corpse;
|
||||
@@ -119,6 +120,7 @@ struct NPCType
|
||||
uint32 emoteid;
|
||||
float spellscale;
|
||||
float healscale;
|
||||
bool no_target_hotkey;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user