View Single Post
  #3  
Old 10-06-2006, 07:04 PM
KLS
Administrator
 
Join Date: Sep 2006
Posts: 1,348
Default

Yeah it seemed very odd when I first caught it, something so small causes such a large issue. I've been looking at spell stacking more and took the liberty to tinker with some of it because I felt it was lacking somewhat.

-I changed how I solved the index problem, with just taking the formula and reducing by 201 instead of 200 because I saw no reason not too, and the value is plugged into a function below it which also works in a similar way.
-I let dots of the same resist type stack so long as they aren't the same spell, I logged into live to check this and this is indeed how they work now.
-I implemented dots of the same type stacking between different players.
-I made it so a beneficial effect and a detrimental of an effect don't conflict. Ex: snare will not overwrite sow any more and slow will not overwrite haste
-I made it so beneficial songs will stack with beneficial spells, ex: bard regen will stack with enchanter regen but bard slow will not stack on a mob with enchanter slow.
-I made it so you can snare things while they're rooted(why this is even in there /boggle)

The only large code change is to Mob::CheckStackConflict() (spells.cpp approx line 175, change the function to:
Code:
int Mob::CheckStackConflict(int16 spellid1, int caster_level1, int16 spellid2, int caster_level2, Mob* caster1, Mob* caster2)
{
	const SPDat_Spell_Struct &sp1 = spells[spellid1];
	const SPDat_Spell_Struct &sp2 = spells[spellid2];
	
	int i, effect1, effect2, sp1_value, sp2_value;
	int blocked_effect, blocked_below_value, blocked_slot;
	int overwrite_effect, overwrite_below_value, overwrite_slot;

	/*
	One of these is a bard song and one isn't and they're both beneficial so they should stack.
	*/
	if(IsBardSong(spellid1) != IsBardSong(spellid2)) 
	{
		if(!IsDetrimentalSpell(spellid1) && !IsDetrimentalSpell(spellid2))
		{
			mlog(SPELLS__STACKING, "%s and %s are beneficial, no action needs to be taken", sp1.name, sp2.name);
			return (0);
		}
	}


	// solar: check for special stacking block command in spell1 against spell2
	for(i = 0; i < EFFECT_COUNT; i++)
	{
		effect1 = sp1.effectid[i];
		if(effect1 == SE_StackingCommand_Block)
		{
			/*
			The logic here is if you're comparing the same spells they can't block each other
			from refreshing
			*/
			if(spellid1 == spellid2)
				continue;

			blocked_effect = sp1.base[i];
			blocked_slot = sp1.formula[i] - 201;
			blocked_below_value = sp1.max[i];
			
			if(sp2.effectid[blocked_slot] == blocked_effect)
			{
				sp2_value = CalcSpellEffectValue(spellid2, blocked_slot, caster_level2);
				
				mlog(SPELLS__STACKING, "%s (%d) blocks effect %d on slot %d below %d. New spell has value %d on that slot/effect. %s.",
					sp1.name, spellid1, blocked_effect, blocked_slot+1, blocked_below_value, sp2_value, (sp2_value < blocked_below_value)?"Blocked":"Not blocked");

				if(sp2_value < blocked_below_value)
					return -1;	// blocked
			} else {
				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+1, blocked_below_value);
			}
		}
	}

	// check for special stacking overwrite in spell2 against effects in spell1
	for(i = 0; i < EFFECT_COUNT; i++)
	{
		effect2 = sp2.effectid[i];
		if(effect2 == SE_StackingCommand_Overwrite)
		{
			overwrite_effect = sp2.base[i];
			overwrite_slot = sp2.formula[i] - 201;
			overwrite_below_value = sp2.max[i];
			if(sp1.effectid[overwrite_slot] == overwrite_effect)
			{
				sp1_value = CalcSpellEffectValue(spellid1, overwrite_slot, caster_level1);

				mlog(SPELLS__STACKING, "%s (%d) overwrites existing spell if effect %d on slot %d is below %d. Old spell has value %d on that slot/effect. %s.",
					sp2.name, spellid2, overwrite_effect, overwrite_slot+1, overwrite_below_value, sp1_value, (sp1_value < overwrite_below_value)?"Overwriting":"Not overwriting");
				
				if(sp1_value < overwrite_below_value)
					return 1;			// overwrite spell if its value is less
			} else {
				mlog(SPELLS__STACKING, "%s (%d) overwrites existing spell if effect %d on slot %d is below %d, but we do not have that effect on that slot. Ignored.",
					sp2.name, spellid2, overwrite_effect, overwrite_slot+1, overwrite_below_value);

			}
		}
	}
	
	bool sp1_detrimental = IsDetrimentalSpell(spellid1);
	bool sp2_detrimental = IsDetrimentalSpell(spellid2);
	bool sp_det_mismatch;

	if(sp1_detrimental == sp2_detrimental)
		sp_det_mismatch = false;
	else
		sp_det_mismatch = true;
	
	// now compare matching effects
	// abitrartion takes place if 2 spells have the same effect at the same
	// effect slot, otherwise they're stackable, even if it's the same effect
	bool will_overwrite = false;
	for(i = 0; i < EFFECT_COUNT; i++)
	{
		if(IsBlankSpellEffect(spellid1, i))
			continue;

		effect1 = sp1.effectid[i];
		effect2 = sp2.effectid[i];

		/*
		Quick check, are the effects the same, if so then
		keep going else ignore it for stacking purposes.
		*/
		if(effect1 != effect2)
			continue;

		/*
		If target is a npc and caster1 and caster2 exist
		If Caster1 isn't the same as Caster2 and the effect is a DoT then ignore it.
		*/
		if(IsNPC())
		{
			if(caster1 && caster2)
			{
				if(caster1 != caster2)
				{
					if(effect1 == SE_CurrentHP)
					{
						if(sp1_detrimental && sp2_detrimental)
						{
							continue;
							mlog(SPELLS__STACKING, "Both casters exist and are not the same, the effect is a detrimental dot, moving on");
						}
					}
				}
			}
		}

		/*
		If the effects are the same and
		sp1 = beneficial & sp2 = detrimental or
		sp1 = detrimental & sp2 = beneficial
		Then this effect should be ignored for stacking purposes.
		*/
		if(sp_det_mismatch)
		{
			mlog(SPELLS__STACKING, "The effects are the same but the spell types are not, passing the effect");
			continue;
		}
		
		/*
		If the spells aren't the same
		and the effect is a dot we can go ahead and stack it
		*/
		if(spellid1 != spellid2)
		{
			if(effect1 == SE_CurrentHP)
			{
				if(sp1_detrimental && sp2_detrimental)
				{
					mlog(SPELLS__STACKING, "The spells are not the same and it is a detrimental dot, passing");
					continue;
				}
			}
		}

		sp1_value = CalcSpellEffectValue(spellid1, i, caster_level1);
		sp2_value = CalcSpellEffectValue(spellid2, i, caster_level2);
		
		// some spells are hard to compare just on value.  attack speed spells
		// have a value that's a percentage for instance
		if
		(
			effect1 == SE_AttackSpeed ||
			effect1 == SE_AttackSpeed2 ||
			effect1 == SE_AttackSpeed3
		)
		{
			sp1_value -= 100;
			sp2_value -= 100;
		}
		
		if(sp1_value < 0)
			sp1_value = 0 - sp1_value;
		if(sp2_value < 0)
			sp2_value = 0 - sp2_value;
		
		if(sp2_value < sp1_value) {
			mlog(SPELLS__STACKING, "Spell %s (value %d) is not as good as %s (value %d). Rejecting %s.",
				sp2.name, sp2_value, sp1.name, sp1_value, sp2.name);
			return -1;	// can't stack
		}
		//we dont return here... a better value on this one effect dosent mean they are
		//all better...

		mlog(SPELLS__STACKING, "Spell %s (value %d) is not as good as %s (value %d). We will overwrite %s if there are no other conflicts.",
			sp1.name, sp1_value, sp2.name, sp2_value, sp1.name);
		will_overwrite = true;
	}
	
	//if we get here, then none of the values on the new spell are "worse"
	//so now we see if this new spell is any better, or if its not related at all
	if(will_overwrite) {
		mlog(SPELLS__STACKING, "Stacking code decided that %s should overwrite %s.", sp2.name, sp1.name);
		return(1);
	}
	
	mlog(SPELLS__STACKING, "Stacking code decided that %s is not affected by %s.", sp2.name, sp1.name);
	return 0;
}
Note that the declaration of the function in mob.h has changed, change it to:
Code:
int CheckStackConflict(int16 spellid1, int caster_level1, int16 spellid2, int caster_level2, Mob* caster1 = NULL, Mob* caster2 = NULL);
then in Mob::AddBuff() you'll find the following line:
Code:
ret = CheckStackConflict(curbuf.spellid, curbuf.casterlevel, spell_id, caster_level);
Just change it to:
Code:
ret = CheckStackConflict(curbuf.spellid, curbuf.casterlevel, spell_id, caster_level, entity_list.GetMobID(curbuf.casterid), caster);
Then in spells.cpp at approx line 2610 you'll find:
Code:
		//this is a crap load of work to say "a movement speed increase cannot land if your rooted."
		int8 buffslot = GetBuffSlotFromType(SE_MovementSpeed);
		if((FindType(SE_Root) && IsEffectInSpell(spell_id, SE_MovementSpeed)) 
			|| (buffslot!=255 && IsValidSpell(buffs[buffslot].spellid)
			&& IsDetrimentalSpell(buffs[buffslot].spellid) && IsBeneficialSpell(spell_id)))
		{
			caster->Message_StringID(MT_Shout,CANNOT_AFFECT_PC);
			return true;
		}
That is a lot of work to be doing something that's not live like behavior at all, just remove it.

I tested this stuff and it appears to work, if you see any problems with it please point them out to me.
Reply With Quote