Jump to content

Make one MagicEffect Dispel Another?


Recommended Posts

@glowplug

 

Is it possible to also use "OnMagicEffectHit" somehow? Is it possible to set event handler for multiple effects? If I write these commands

SetEventHandler "OnMagicEffectHit" FnPlayerDispelLight first::PlayerRef second::"NEYE"
SetEventHandler "OnMagicEffectHit" FnPlayerDispelNEye first::PlayerRef second::"LGHT"

will event handlers be set and work?

 

However, FnDispelConflicts still needs exact editor IDs of spells, I think my approach (although much more complex and with bigger overhead) gives better result.

Link to comment
Share on other sites

You're certainly on the right track RomanR, I was forgetting about Dispel by MagicEffect. To be honest, I did not think things through. Unfortunately OnMagicEffectHit doesn't stack, each SetEventHandler "OnMagicEffectHit" removes the previous. The other problem is that the event double fires and fails to interpret refActor.Dispel refSpell.

The reason there is no 'elseif' is to ensure all conflicts Dispel.
Note: the actual spell is not cast till after the EventHandler - it won't self dispel if it has conflicting effects.
Note: MagicItemHasEffectCode passed by long - MagicItemHasEffect passed by string_var won't compile in 'if' statement.

scn QstMagicEffectHandlersScript
;Quest script

Begin GameMode

  if GetGameLoaded

    PlayerRef.SetEventHandler OnSpellCast, FnOnSpellCastActor
    
  endif
  
End
scn FnOnSpellCastActor
; elseif not used to ensure all desired Dispel occur.
; the actual spell is cast after this EventHandler (it won't self dispel)

ref refActor
ref refSpell

Begin Function { refActor, refSpell }

  if MagicItemHasEffect LGHT, refSpell

    call FnActorDispelByMagicEffectCode refActor, (MECodeFromChars "NEYE")

  endif

  if MagicItemHasEffect NEYE, refSpell

    call FnActorDispelByMagicEffectCode refActor, (MECodeFromChars "LGHT")

  endif
  

  if MagicItemHasEffect WABR, refSpell

    call FnActorDispelByMagicEffectCode refActor, (MECodeFromChars "WAWA")

  endif

  if MagicItemHasEffect WAWA, refSpell

    call FnActorDispelByMagicEffectCode refActor, (MECodeFromChars "WABR")

  endif
  
End
scn FnActorDispelByMagicEffectCode
;Generic function to loop through whichever Actor's spells
;NOTE: MagicItemHasEffectCode using long - MagicItemHasEffect using string_var won't compile

int iIndex
ref refSpell
array_var arrSpells

ref refActor
long lEffect

Begin Function { refActor, lEffect }

  let arrSpells := refActor.GetSpells

  while iIndex < ar_Size arrSpells

    let refSpell := arrSpells[iIndex]
    
    if MagicItemHasEffectCode lEffect, refSpell

      refActor.Dispel refSpell
      
    endif

    let iIndex += 1

  loop

End

 

 

Link to comment
Share on other sites

PS I accidentally clicked Cancel on another 'No Quotes' warning then got no warning later, it should be...

PlayerRef.SetEventHandler "OnSpellCast", FnOnSpellCastActor

...it worked without the quotes but better to get it right.

 

I've tested it by repeating variations of the following with a custom spell called LightNight that has Light+Night-Eye...

  • Night-Eye then Light - Night-Eye Dispelled, Light is cast
  • Light then Night-Eye - Light Dispelled, Night-Eye is cast
  • LightNight then Light - LightNight Dispelled, Light is cast
  • LightNight then Night-Eye - LightNight Dispelled, Night-Eye is cast
  • Light then LightNight - Light Dispelled, LightNight is cast
  • Night-Eye then LightNight - Night-Eye Dispelled, LightNight is cast

...suffice to say that 'elseif' would be wrong and Cast is running after the EventHandler has compeleted.

Link to comment
Share on other sites

That's a shame, but I see another event, which seems interesting "OnMagicApply". It looks little overheading, because is invoked on each effect, but documentation says is working for all things which could put some effect on you (spell, enchantment...). I have some free time for a weekend, so I think I will test it.

Link to comment
Share on other sites

Remove any of the quotes breaks this...

SetEventHandler "OnMagicApply", FnOnMagicApply, "ref"::"LGHT", "ref"::"Player"

The parameters have to be ref.
Yes, it filters to self but LGHT is ignored (fires for any Self) the entire spell is passed yet it fires for each magic effect.

 

OnMagicCast the same but only fires once for the spell.

 

Edit: I'm guessing that all of the event handlers have the same "add same removes previous" issue.

 

Having an event handler for each effect means the scattered across as many scripts as there are Magic Effect required - if their code needs changing then we've got to make sure to find change and save all of them - I try to avoid that.

 

If OnSpellCast is required for additional/different code then what I put on FnOnSpellCastActor should be factored out.

Link to comment
Share on other sites

The "onMagicApply" seems the right choice for this and after some testing I can present you practicaly complete framework.

 

First init part:

scn RRSetHandlerQuestScript
short restart
short good
short init
int size
int old_size
int count
array_var dispelPrefs

begin GameMode
	; is prefs in order ?
	let size := ar_Size dispelPrefs
	if size != old_size
		set init to 0
	endif
	; init part
	if init == 0
		if size == -1
			let dispelPrefs := ar_Construct array
		else
			ar_Resize dispelPrefs 0
		endif
		let dispelPrefs[0] := "NEYE" ; effect we want to check
		let dispelPrefs[1] := "+" ; block of counter effects begin here
		let dispelPrefs[2] := "LGHT" ; setting counter effect
		let dispelPrefs[3] := "-" ; end of counter effects block
		let dispelPrefs[4] := "LGHT"
		let dispelPrefs[5] := "+"
		let dispelPrefs[6] := "NEYE"
		let dispelPrefs[7] := "-"
		let size := ar_Size dispelPrefs
		set old_size to size 
		set init to 1
	endif
	; setting an event
	set restart to 0
	if GetGameRestarted
		set restart to 1
	endif
	if restart
		;print "Restart detected in game mode."
		let good := SetEventHandler "OnMagicApply" RRMagicHandlerScript
		if good == 0
			Message "Counter Effect Mod: Creation of event handler failed."
		endif
		; adding token to catch long-term effects before this mod was active (player only)
		set count to player.GetItemCount RRDispelEffectToken
		if count == 0 
			player.AddItemNS RRDispelEffectToken 1
		endif
		set restart to 0
	endif 
end

begin MenuMode
	; is prefs in order ?
	let size := ar_Size dispelPrefs
	if size != old_size
		set init to 0
	endif
	; init part
	if init == 0
		if size == -1
			let dispelPrefs := ar_Construct array
		else
			ar_Resize dispelPrefs 0
		endif
		let dispelPrefs[0] := "NEYE" ; effect we want to check
		let dispelPrefs[1] := "+" ; block of counter effects begin here
		let dispelPrefs[2] := "LGHT" ; setting counter effect
		let dispelPrefs[3] := "-" ; end of counter effect block
		let dispelPrefs[4] := "LGHT"
		let dispelPrefs[5] := "+"
		let dispelPrefs[6] := "NEYE"
		let dispelPrefs[7] := "-"
		let size := ar_Size dispelPrefs
		set old_size to size 
		set init to 1
	endif
	; setting an event
	set restart to 0
	if GetGameRestarted
		set restart to 1
	endif
	if restart
		;print "Restart detected in menu mode."
		let good := SetEventHandler "OnMagicApply" RRMagicHandlerScript
		if good == 0
			Message "Counter Effect Mod: Creation of event handler failed."
		endif
		; adding token to catch long-term effects before this mod was active (player only)
		set count to player.GetItemCount RRDispelEffectToken
		if count == 0 
			player.AddItemNS RRDispelEffectToken 1
		endif
		set restart to 0
	endif
end

It's set to always running quest with small priority. I also added token to deal with things like enchantments from rings, so if you're wearing them, the opposite effects from spells will not apply to you until you unequip them.

 

Script for handler:

scn RRMagicHandlerScript
ref magic_item
ref caster
ref target
short found
short argument
short counter_on
short long_term
int size 
int index
int magic_code
int count
string_var ascii_code
string_var check_effect
string_var dispel_effect

begin function { magic_item, caster }
	if magic_item != 0
		set target to GetSelf
		if target  == player
			;print "There was aplied magic effect on "+$player+" now."	
			if RRSetHandlerQuest.init != 0
				let size := ar_Size RRSetHandlerQuest.dispelPrefs
			endif
			if size > 0
				set index to 0
				let check_effect := ""
				let dispel_effect := ""
				while index < size					
					let ascii_code := RRSetHandlerQuest.dispelPrefs[index]
					if eval (ascii_code != "+") && eval (ascii_code != "-")
						set argument to 1
					else 
						set argument to 0
					endif
					if eval (ascii_code == "+")
						set counter_on to 1
					endif
					if eval (ascii_code == "-" )
						set counter_on to 0
						set found to 0
						let dispel_effect := ""
					endif
					if argument != 0 && counter_on == 0
						let check_effect := ascii_code
					endif 
					if argument != 0 && counter_on == 0 && found == 0
						let magic_code := MECodeFromChars $check_effect
						let found := MagicItemHasEffectCode magic_code magic_item
						if found
							;print "Desired effect "+$check_effect+" found aplied on "+$target+"."
							; adding token to deal with long-term effects (enchant from apparel, ability etc.)
							if target.IsActor 
								;print $target+" is actor."
								set count to target.GetItemCount RRDispelEffectToken
								if count == 0 
									target.AddItemNS RRDispelEffectToken 1
									;print "Adding token."
								endif
							endif
						endif
					endif
					if argument != 0 && counter_on != 0 && found != 0
						let dispel_effect := ascii_code
						;print "Trying to find and dispel "+$dispel_effect+"."
						Call RRFnDispelMagicEffect target dispel_effect
					endif
					set index to index + 1
				loop
			endif
		endif
	endif			
	sv_Destruct check_effect dispel_effect ascii_code
end

Quite standard, it goes through dispelPrefs field defined in quest and calls function to dispel defined oppsite effect(s). It also adds a token.

 

Script of function responsible for dispeling effects:

scn RRFnDispelMagicEffect
array_var activeeffects
string_var ascii_code
string_var dispel_code
int size
int index
int eff_code
int magic_type
int temp
ref caster
ref target
short dispel_it

begin function { target, dispel_code }
	let size := ar_Size activeeffects
	if size == -1
		let activeeffects := ar_Construct array
	endif
	if target != 0
		let activeeffects := target.GetActiveEffectCodes
		let size := ar_Size activeeffects
	endif
	if size > 0
		set index to 0
		while index < size
			let eff_code := activeeffects[index]
			let ascii_code := GetMagicEffectCharsC eff_code
			;print $ascii_code
			if eval (ascii_code == dispel_code)
				set dispel_it to 0
				set caster to 0
				let caster := target.GetNthActiveEffectMagicItem index
				if caster != 0
					set magic_type to 0
					set temp to 0
					let magic_type := GetMagicItemType caster
					if magic_type != 0
						if magic_type == 3 || magic_type == 4 ; alchemy item or ingredient
							;print "Alchemy or ingredient."
							set dispel_it to 1
						endif
						if dispel_it == 0 && magic_type == 2 ; enchantment
							let temp := GetEnchantmentType caster
							if temp != 3 ; dispel scrolls, weapons and staffs
								;print $temp
								;print "Enchantment."
								set dispel_it to 1
							endif
						endif
						if dispel_it == 0 && magic_type == 1 ; we want disable spells
							let magic_type := GetSpellType caster
							if magic_type == 0 || magic_type == 3 ;disable only normal spell or lesser power
								;print "Spell or lesser power."									
								set dispel_it to 1
							endif
						endif
					endif
				endif				
				if dispel_it
					;print "The opposite effect "+$dispel_code+" will be removed from "+$target+"."
					target.DispelNthActiveEffect index
				endif
			endif
			set index to index + 1
		loop
	endif
	; cleanup
	sv_Destruct ascii_code dispel_code
	ar_Resize activeeffects 0
	let activeeffects := ar_Null
end

There are checks to not dispel all effects mindlessly, but it leaves enchantments on apparel, greater powers, diseases and abilities alone.

 

And for token:

scn RRDispelEffectTokenScript
ref actor
ref item
int size
int prefs_size
int index
int prefs_index
int code
int magic_type
int temp
short remove
short argument 
short block_on
short found
string_var ascii_code
string_var check_code
string_var dispel_code
array_var effects

begin GameMode
	set remove to 0
	let size := ar_Size effects
	if size == -1
		let effects := ar_Construct array
	endif
	if actor == 0
		set actor to GetContainer
	endif
	if actor != 0 && RRSetHandlerQuest.init != 0
		let prefs_size := ar_Size RRSetHandlerQuest.dispelPrefs
		let effects := actor.GetActiveEffectCodes
		let size := ar_Size effects
		if prefs_size > 0 && size > 0
			;print "Checking..."
			set remove to 1
			set prefs_index to 0
			while prefs_index < prefs_size
				let ascii_code := RRSetHandlerQuest.dispelPrefs[prefs_index]
				if eval (ascii_code != "+") && eval (ascii_code != "-")
					set argument to 1
				else
					set argument to 0
				endif
				if eval (ascii_code == "+")
					set block_on to 1
				endif
				if eval (ascii_code == "-")
					set block_on to 0
					set found to 0
					let dispel_code := ""	
				endif
				if argument != 0 && block_on == 0
					;print "Token> There is setting for "+$ascii_code+"."
					set index to 0
					while index < size
						let code := effects[index] 
						let check_code := GetMagicEffectCharsC code
						if eval (check_code == ascii_code)
							; now we check if source of effect is enchantment from apparel (ring, amulet, clothes ...)
							; or ability, disease or greater power
							;print "Effect found on index "+$index+"."
							set item to 0
							let item := actor.GetNthActiveEffectMagicItem index
							if item != 0
								set magic_type to 0
								set temp to 0
								let magic_type := GetMagicItemType item
								if magic_type != 0
									if magic_type == 2 ; enchantment
										let temp := GetEnchantmentType item
										if temp == 3 ; apparel
											;print "Enchatment from apparel."
											set remove to 0
											set found to 1
										endif
									endif
									if found == 0 && magic_type == 1 ; spell
										let temp := GetSpellType item
										if temp != 0 && temp != 3 ;ignore normal spell or lesser power
											;print "Power, disease or ability."									
											set remove to 0
											set found to 1
										endif
									endif
								endif
							endif
						endif
						set index to index + 1
					loop
				endif
				if argument && block_on && found
					let dispel_code := ascii_code
					;print "Trying to remove opposite effect "+$dispel_code+" from "+$actor+"."
					Call RRFnDispelMagicEffect actor dispel_code 
				endif
				set prefs_index to prefs_index + 1
			loop
		endif
	else
		set remove to 1
	endif
	if remove != 0
		;print "Token> No effect coming from long-term source found, so I will remove."
		RemoveMe
	endif
	sv_Destruct ascii_code check_code dispel_code
	ar_Resize effects 0
	let effects := ar_Null
end

As it was made to deal with greater powers, enchantments and such it will stay in target's inventory until the last effect of this type is active. After that it will remove itself.

 

Now you can play with it all you like. I hope it will make you happy.

Link to comment
Share on other sites

Seems for the last one I forgot to add cleaning commands before RemoveMe command and I also cleaned some weirdness for remove variable. Sorry about that, here's new version:

scn RRDispelEffectTokenScript
ref actor
ref item
int size
int prefs_size
int index
int prefs_index
int code
int magic_type
int temp
short remove
short argument 
short block_on
short found
string_var ascii_code
string_var check_code
string_var dispel_code
array_var effects

begin GameMode
	set remove to 1
	let size := ar_Size effects
	if size == -1
		let effects := ar_Construct array
	endif
	if actor == 0
		set actor to GetContainer
	endif
	if actor != 0 && RRSetHandlerQuest.init != 0
		let prefs_size := ar_Size RRSetHandlerQuest.dispelPrefs
		let effects := actor.GetActiveEffectCodes
		let size := ar_Size effects
		if prefs_size > 0 && size > 0
			;print "Checking..."
			set remove to 1
			set prefs_index to 0
			while prefs_index < prefs_size
				let ascii_code := RRSetHandlerQuest.dispelPrefs[prefs_index]
				if eval (ascii_code != "+") && eval (ascii_code != "-")
					set argument to 1
				else
					set argument to 0
				endif
				if eval (ascii_code == "+")
					set block_on to 1
				endif
				if eval (ascii_code == "-")
					set block_on to 0
					set found to 0
					let dispel_code := ""	
				endif
				if argument != 0 && block_on == 0
					;print "Token> There is setting for "+$ascii_code+"."
					set index to 0
					while index < size
						let code := effects[index] 
						let check_code := GetMagicEffectCharsC code
						if eval (check_code == ascii_code)
							; now we check if source of effect is enchantment from apparel (ring, amulet, clothes ...)
							; or ability, disease or greater power
							;print "Effect found on index "+$index+"."
							set item to 0
							let item := actor.GetNthActiveEffectMagicItem index
							if item != 0
								set magic_type to 0
								set temp to 0
								let magic_type := GetMagicItemType item
								if magic_type != 0
									if magic_type == 2 ; enchantment
										let temp := GetEnchantmentType item
										if temp == 3 ; apparel
											;print "Enchatment from apparel."
											set remove to 0
											set found to 1
										endif
									endif
									if found == 0 && magic_type == 1 ; spell
										let temp := GetSpellType item
										if temp != 0 && temp != 3 ;ignore normal spell or lesser power
											;print "Power, disease or ability."									
											set remove to 0
											set found to 1
										endif
									endif
								endif
							endif
						endif
						set index to index + 1
					loop
				endif
				if argument && block_on && found
					let dispel_code := ascii_code
					;print "Trying to remove opposite effect "+$dispel_code+" from "+$actor+"."
					Call RRFnDispelMagicEffect actor dispel_code 
				endif
				set prefs_index to prefs_index + 1
			loop
		endif
	endif
	if remove != 0
		;print "Token> No effect coming from long-term source found, so I will remove."
		sv_Destruct ascii_code check_code dispel_code
		ar_Resize effects 0
		let effects := ar_Null
		RemoveMe
	endif
	sv_Destruct ascii_code check_code dispel_code
	ar_Resize effects 0
	let effects := ar_Null
end
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...