Merge pull request #136 from KayenEQ/master

Spell Effect Updates.
This commit is contained in:
Michael Cook 2014-03-28 02:42:22 -04:00
commit 12f8357373
12 changed files with 122 additions and 37 deletions

View File

@ -1,5 +1,12 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 03/27/2014 ==
Kayen: SE_Gate will now use have a fail chance as defined by its base value in the spell data.
Kayen: SE_Succor will now have a baseline fail chance of (2%). Rule added to adjust this as needed.
Kayen: SE_FeignDeath will now have a fail chance as defined by its base value in the spell data.
Optional SQL: utils/sql/git/optional/2014_03_27_SuccorFailRule.sql
== 03/22/2014 ==
Uleat: Moved the existing 'load_bots' and 'drop_bots' sqls into the emu git repository for the time being. Look to the
/utils/sql/git/bots/ folder to find them. The 'load_bots' sql has been updated to include the below fix, as well as

View File

@ -302,6 +302,7 @@ RULE_BOOL ( Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest
RULE_BOOL ( Spells, BuffLevelRestrictions, true) //Buffs will not land on low level toons like live
RULE_INT ( Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick.
RULE_INT ( Spells, FearBreakCheckChance, 70) //Determines chance for a fear break check to occur each buff tick.
RULE_INT ( Spells, SuccorFailChance, 2) //Determines chance for a succor spell not to teleport an invidual player
RULE_CATEGORY_END()
RULE_CATEGORY( Combat )

View File

@ -159,8 +159,8 @@ typedef enum {
#define SE_WIS 9 // implemented
#define SE_CHA 10 // implemented - used as a spacer
#define SE_AttackSpeed 11 // implemented
#define SE_Invisibility 12 // implemented
#define SE_SeeInvis 13 // implemented
#define SE_Invisibility 12 // implemented - TO DO: Implemented Invisiblity Levels
#define SE_SeeInvis 13 // implemented - TO DO: Implemented See Invisiblity Levels
#define SE_WaterBreathing 14 // implemented
#define SE_CurrentMana 15 // implemented
//#define SE_NPCFrenzy 16 // not used
@ -172,7 +172,7 @@ typedef enum {
#define SE_Charm 22 // implemented
#define SE_Fear 23 // implemented
#define SE_Stamina 24 // implemented - Invigor and such
#define SE_BindAffinity 25 // implemented
#define SE_BindAffinity 25 // implemented - TO DO: Implement 2nd and 3rd Recall (value 2,3 ect). Sets additional bind points.
#define SE_Gate 26 // implemented - Gate to bind point
#define SE_CancelMagic 27 // implemented
#define SE_InvisVsUndead 28 // implemented
@ -211,7 +211,7 @@ typedef enum {
#define SE_Identify 61 // implemented
//#define SE_ItemID 62 // not used
#define SE_WipeHateList 63 // implemented
#define SE_SpinTarget 64 // implemented
#define SE_SpinTarget 64 // implemented - TO DO: Not sure stun portion is working correctly
#define SE_InfraVision 65 // implemented
#define SE_UltraVision 66 // implemented
#define SE_EyeOfZomm 67 // implemented
@ -241,7 +241,7 @@ typedef enum {
#define SE_SummonCorpse 91 // implemented
#define SE_InstantHate 92 // implemented - add hate
#define SE_StopRain 93 // implemented - Wake of Karana
#define SE_NegateIfCombat 94 // *not implemented? - Works client side but there is comment todo in spell effects...Component of Spirit of Scale
#define SE_NegateIfCombat 94 // implemented
#define SE_Sacrifice 95 // implemented
#define SE_Silence 96 // implemented
#define SE_ManaPool 97 // implemented
@ -456,7 +456,7 @@ typedef enum {
//#define SE_ArmyOfTheDead 306 // *not implemented NecroAA - This ability calls up to five shades of nearby corpses back to life to serve the necromancer. The soulless abominations will mindlessly fight the target until called back to the afterlife some time later. The first rank summons up to three shades that serve for 60 seconds, and each additional rank adds one more possible shade and increases their duration by 15 seconds
//#define SE_Appraisal 307 // *not implemented Rogue AA - This ability allows you to estimate the selling price of an item you are holding on your cursor.
#define SE_SuspendMinion 308 // not implemented as bonus
#define SE_YetAnotherGate 309 // implemented
#define SE_GateCastersBindpoint 309 // implemented - Gate to casters bind point
#define SE_ReduceReuseTimer 310 // implemented
#define SE_LimitCombatSkills 311 // implemented - Excludes focus from procs (except if proc is a memorizable spell)
//#define SE_Sanctuary 312 // *not implemented

View File

@ -0,0 +1 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:SuccorFailChance', '2', 'Determines chance for a succor spell not to teleport an invidual player.');

View File

@ -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.
@ -208,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.
@ -253,6 +255,7 @@
#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!

View File

@ -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));

View File

@ -1358,16 +1358,27 @@ 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;
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
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 +1388,24 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_AttackSpeed4:
{
if (effect_value > 0) {
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) {
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);
newbon->inhibitmelee = new_effect_value;
SlowMitigation(true,caster);
}
}
else if (effect_value > newbon->inhibitmelee) {
newbon->inhibitmelee = effect_value;
newbon->inhibitmelee = effect_value;
}
}
break;
}
@ -2568,6 +2585,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
newbon->AbsorbMagicAtt[1] = buffslot;
}
break;
case SE_NegateIfCombat:
newbon->NegateIfCombat = true;
}
}
}

View File

@ -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;
@ -6640,6 +6643,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);

View File

@ -344,6 +344,7 @@ struct StatBonuses {
int16 FrenziedDevastation; // base1= AArank(used) base2= chance increase spell criticals + all DD spells 2x mana.
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
// AAs
int8 Packrat; //weight reduction for items, 1 point = 10%

View File

@ -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;

View File

@ -396,10 +396,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 +427,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 +450,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 +466,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 +498,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();
@ -857,7 +866,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");
@ -989,13 +998,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;
}
@ -1378,7 +1392,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);
@ -1554,8 +1569,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;
}
@ -1691,19 +1713,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);

View File

@ -211,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++) {