Jump to content

casting spells on an activator


cfh85

Recommended Posts

So you want a script to run when an activator is hit by a specific spell?

 

OnMagicEffectHit is the only reliable easy way to detect a hit by a certain type of spell. If compatibility is a concern, you could swap out a custom activator that looks identical to the existing one.

 

If it needs to be a specific spell, that would be much harder. Just pulling ideas out of the air... you could place a trigger zone around the player to capture the reference of any missiles coming from the player. Once the activator is hit, it uses GetActionRef to determine whether it's the same reference as what the player cast. GetProjectile is another function worth investigating.

Link to comment
Share on other sites

Okay. Full story. I'm TRYING to create a spell to replace dark welkynd stones with a PC friendly one for a limited amount of time. I'll probably have to end up putting the script on the stones.

Though, even then I only want it to respond to a specific spell, not effect.

Link to comment
Share on other sites

Sounds to me like a scripted effect spell with the exchange taking place in the spell script itself could do the trick here. Though I admit scripting the exchange will be the hard part.

 

The spell script will have to check if it's running on such a dark welkynd stone or not.

One such way could be using GetBaseObject and then comparing the returned value with the EditorIDs of the traps you're intending to replace.

E.g.:

if GetBaseObject == ARTrapEvilStone01

Or you could use GetIsID for the same comparing purpose.

E.g.:

if GetIsID ARTrapEvilStone01

As there are already 9 different generic and 1 quest-related (though this one shoots only on a quest NPC and seems to be of no relevance to your idea here) "types" of those traps in the Vanilla game alone, and only The Nine know how many custom ones in mods, it will be difficult to catch them all, but I think you can catch "most" of them via the NIF file used for their model instead, by using CompareModelPath for example.

if CompareModelPath "ARTrapEvilStone01"

And you would already catch all of the above... though you as well catch ones not intended to be caught with this solution.

Still not caught would be those with a custom model used for the same purpose, but I think now we're getting too far away from your original intention anyways.

 

I had a short look into their scripts and it seems they're either "triggered" by something else (a trigger plate maybe or an invisible trigger zone) (ARTrapEvilStone01, ARTrapEvilStone02, ARTrapEvilStoneFAST01), or they're the kind that shoots on its own while the player is in range (ARTrapEvilStoneAUTOFIRE01, ...02, ARTrapEvilStoneAUTOFIREFAR01, ...02). They only react to the player (hardcoded target). And they're all firing through means of their object scripts.

 

In retrospect only the last 4 seem to be relevant and the way of identification through EditorIDs seems to be very feasible.

 

I for one wouldn't mess with their scripts to turn them inactive, although this would be a feasible attempt as well.

 

The most simple approach to get rid of them "temporarily" would be to "move them away" or "disable" them and place a placeholder at their place.

This could all be handled in one single spell script... if all works out as intended that is.

begin ScriptEffectStart
   ;the identification part:
   if (GetIsID ARTrapEvilStoneAUTOFIRE01) || (GetIsID ARTrapEvilStoneAUTOFIRE02) || (GetIsID ARTrapEvilStoneAUTOFIREFAR01) || (GetIsID ARTrapEvilStoneAUTOFIREFAR02)
       disable ; will disappear and should no longer be able to react on player or shoot at player
   endif
end

begin ScriptEffectFinish
   enable ; should reappear after time runs out and also be able to react on player and shoot at player again automatically
end

If we're lucky, everything will work fine already and the running out of the spell will turn everything back to normal.

But it could be calling "disable" on the object will also disable the continuation of this spell or its script execution. In this case we'll have to put the disable and enable part into the object script of the replacing object we put in its place as well as an actual timer counting down.

 

I'd say try this attempt first and come back telling if it works or not. In case it really won't work that way at all due to the repercussions of "disable", I could go more into detail on what to do otherwise.

 

You can use MoveTo to put something else into the trap's place without risking savegame bloat from PlaceAtMe, but then it will only work for a fix number of traps at once, as you'll need to have a fix number of replacements with "persistent reference id" ready from inside a holding cell or something. And you'll need some scripted trickery taking care of which of them to take and when not to act at all due to no more replacements being available at this time... and all such things.

...

ref self

...

set self to GetSelf ; now we have a reference to the target to work with other than the default reference automatically used in spell scripts

...

TemporaryReplacementStone01.MoveTo self ; move replacement 01 from holding cell to target
disable ; disable target

...

TemporaryReplacementStone01.MoveTo HoldingCellID ; move rep. 01 back to holding cell when time is up

...

"TemporaryReplacementStone01" and so on have to be "persistent reference id"s for replacement objects you placed into the holding cell with EditorID "HoldingCellID".

 

In case MoveTo doesn't lead to the replacements being placed at exactly the same location, use GetPos and SetPos, for X, Y and Z respectively, and appropriate "float" variables, to store the location of "self" and set the replacement's location exactly after using MoveTo to get the replacements at least into the same cell and nearby.

 

In case the disable-enable-based approach above doesn't work out, you could try using following approach instead.

...

short active
float timer
float pos
ref target
ref self

...

begin GameMode
   if active == 1 && timer > 0
       set timer to timer - GetSecondsPassed
   elseif active == 1 && target != 0
       set active to 0
       set self to GetSelf
       target.MoveTo self
       set pos to self.GetPos X
       target.SetPos X pos
       set pos to self.GetPos Y
       target.SetPos Y pos
       set pos to self.GetPos Z
       target.SetPos Z pos
       target.enable
       self.disable
       self.MoveTo HoldingCellID
       set target to 0
   endif
end

And do following when "moving away" the traps inside the spell script:

...

set self to GetSelf ; obtain a usable reference to the spell target
TemporaryReplacementStone01.MoveTo self ; get the replacement at least into the same cell and nearby
set pos to GetPos X
TemporaryReplacementStone01.SetPos X pos
set pos to GetPos Y
TemporaryReplacementStone01.SetPos Y pos
set pos to GetPos Z
TemporaryReplacementStone01.SetPos Z pos ; get position absolutely exact
TemporaryReplacementStone01.enable
disable
MoveTo HoldingCellID
set TemporaryReplacementStone01.target to self ; at least if replacement is an Activator, too, this should work, set target variable of rep to target
set TemporaryReplacementStone01.timer to 10 ; set countdown of 10 seconds over in the script of rep.
set TemporaryReplacementStone01.active to 1 ; initiate

...

Make sure all variables are properly defined inside the spell script ("self" of type "ref", "pos" of type "float").

 

Phew, that ought to have given some pointers. I hope it'll all work, as I can't test it at the moment.

Edited by DrakeTheDragon
Link to comment
Share on other sites

what I already tried is this

 

Scriptname CFHDarkWelkyndDefenceScript

ref Target

Float Defended

Begin ScripteffectStart
Set Target to GetSelf
If (Target == ARTrapEvilStone01) || (Target == ARTrapEvilStone02) || (Target == ARTrapEvilStoneAUTOFIRE01) || (Target == ARTrapEvilStoneAUTOFIRE02) || (Target == ARTrapEvilStoneAUTOFIREFAR01) || (Target == ARTrapEvilStoneAUTOFIREFAR02) || (Target == ARTrapEvilStoneAUTOFIRETest) || (Target == ARTrapEvilStoneFast01)
	CFHARTrapEvilStonePCDefenceRef.MoveTo Target
	CFHARTrapEvilStonePCDefenceRef.Enable
	Target.Disable
	Set Defended to 1
EndIf
End

Begin ScriptEffectFinish
If Defended == 1
	CFHARTrapEvilStonePCDefenceRef.Disable
	Target.Enable
	Set Defended to 0
EndIf
End

 

but it doesn't do anything. It seems as if it's not recognising the activator as a target. :wallbash: just realised I didn't remove the script from my replacement stone. Just tried it and still doesn't work.

I'm going to try using GetIsID now. Will reply soon

 

Edit.

just tried

Scriptname CFHDarkWelkyndDefenceScript

ref Target

Float Defended

Begin ScripteffectStart
Set Target to GetSelf
If (Target.GetIsID ARTrapEvilStone01) || (Target.GetIsID ARTrapEvilStone02) || (Target.GetIsID ARTrapEvilStoneAUTOFIRE01) || (Target.GetIsID ARTrapEvilStoneAUTOFIRE02) || (Target.GetIsID ARTrapEvilStoneAUTOFIREFAR01) || (Target.GetIsID ARTrapEvilStoneAUTOFIREFAR02) || (Target.GetIsID ARTrapEvilStoneAUTOFIRETest) || (Target.GetIsID ARTrapEvilStoneFast01)
	CFHARTrapEvilStonePCDefenceRef.MoveTo Target
	CFHARTrapEvilStonePCDefenceRef.Enable
	Target.Disable
	Set Defended to 1
EndIf
End

Begin ScriptEffectFinish
If Defended == 1
	CFHARTrapEvilStonePCDefenceRef.Disable
	Target.Enable
	Set Defended to 0
EndIf
End

 

still no luck. I've set the spell duration to 3000 and removed the script from my replacement stone.

It just seems to not want to recognise the dark welkynd stone as a target

Edited by cfh85
Link to comment
Share on other sites

I added a lot to my post after you posted this, as I wasn't told you posted something already by then.

 

For some reason "Target" seems to be reserved, as the script highlighter in my browser highlights it. Either try "target" instead, as ObScript is case-sensitive in some regards, or a totally different variable name even.

 

"==" on something obtained in a spell script via "GetSelf" in this case will not work, as you're now comparing apples with bananas, an actual "reference" with a BaseObject's EditorID.

 

Try using "GetIsID" without a reference, exactly like I used it. In a spell script it could be troublesome to use "GetSelf" where it's not necessary.

 

But most important of all, let's first check if spells even work on activators.

 

Use this simple script in your spell to find out:

begin ScriptEffectStart
   MessageBox "Hit something!"
end

Then cast this onto random things and see if it does pop up at all.

Edited by DrakeTheDragon
Link to comment
Share on other sites

I tried using the messagebox. Works on doors & containers but not activators.

I'm wondering if there is a function to call an array of all the objects in a cell. If I can find one then I can just (I think) check if they're dark welkynd stones, check their distance from the PC and replace all the stones within distance.

 

I'll post back once I've had a good look.

 

Also, thanks for the help.

 

So far I've found

GetCrosshairRef - may work but will on would within activate distance. This would make the spell very short range. Not the end of the world if it works.

 

That's all I can find under get. I'm going to have a look at some mods that auto harvest ingredients. I'm sure there is some way to find what items are in a cell

 

GetFirstRef and GetNextRef . Scans all the references in the cell when used together. Using the code 18, so GetFirstRef 18, will only return activators. I'll try this as soon as I feel awake enough to write the rest of the code

Edited by cfh85
Link to comment
Share on other sites

:biggrin:

 

Got it to work. It works by scanning all activators in the cell and checking for Dark Welkynd Stones. Because you can not actually target the stones it has to remove all stones from the current cell (otherwise it might not remove the one your after) but it has a limit. I've put 5 replacers in game, so I can only disable 5 at a time.

Some changes are needed though.

1 problem may arise from multiple casts before the first has finished. I had considered using a quest or global to prevent this however, I'm going to try using getincell on #1 stone replacer. If it's not in the storage cell then it'll prevent a second spell from running.

1 other upgrade would be to use comparemodelpath and another to set x,y,z positions. - Thanks DrakeTheDragon for the pointers

Also, disable may not of been the way to go, as the scripts still run. I don't know if that means they could still cast or not, but I figured it's best to be safe.

 

I'm hoping to have a trial mod released very soon to test it out

 

anyway, my current script

 

Scriptname CFHSpellDarkWelkyndDefenceScript

Ref TempRef
Ref Disabled1Ref
Ref Disabled2Ref
Ref Disabled3Ref
Ref Disabled4Ref
Ref Disabled5Ref

Short Counter
Short DisabledCount

Begin ScripteffectStart
Set DisabledCount to 0
Set Counter to (GetNumRefs 18 0)
Set TempRef to (GetFirstRef 18 0)
While Counter > 0
	Let Counter -= 1
	If (TempRef.GetIsID ARTrapEvilStone01) || (TempRef.GetIsID ARTrapEvilStone02) || (TempRef.GetIsID ARTrapEvilStoneAUTOFIRE01) || (TempRef.GetIsID ARTrapEvilStoneAUTOFIRE02) || (TempRef.GetIsID ARTrapEvilStoneAUTOFIREFAR01) || (TempRef.GetIsID ARTrapEvilStoneAUTOFIREFAR02) || (TempRef.GetIsID ARTrapEvilStoneAUTOFIRETest) || (TempRef.GetIsID ARTrapEvilStoneFast01)
		If DisabledCount == 0
			CFHARTrapEvilStonePCDefence1Ref.MoveTo TempRef
			Set Disabled1Ref to TempRef
			Disabled1Ref.MoveTo CFHDarkWelkyndStorageRef
			Set DisabledCount to 1
		ElseIf DisabledCount == 1
			CFHARTrapEvilStonePCDefence2Ref.MoveTo TempRef
			Set Disabled2Ref to TempRef
			Disabled2Ref.MoveTo CFHDarkWelkyndStorageRef
			Set DisabledCount to 2
		ElseIf DisabledCount == 2
			CFHARTrapEvilStonePCDefence3Ref.MoveTo TempRef
			Set Disabled3Ref to TempRef
			Disabled3Ref.MoveTo CFHDarkWelkyndStorageRef
			Set DisabledCount to 3
		ElseIf DisabledCount == 3
			CFHARTrapEvilStonePCDefence4Ref.MoveTo TempRef
			Set Disabled4Ref to TempRef
			Disabled4Ref.MoveTo CFHDarkWelkyndStorageRef
			Set DisabledCount to 4
		ElseIf DisabledCount == 4
			CFHARTrapEvilStonePCDefence5Ref.MoveTo TempRef
			Set Disabled5Ref to TempRef
			Disabled5Ref.MoveTo CFHDarkWelkyndStorageRef
			Set DisabledCount to 5
		EndIf
	EndIf
	Set TempRef to GetNextRef
Loop

If DisabledCount > 0
	Message "DarkStones Disabled"
EndIf
End

Begin ScriptEffectFinish
If DisabledCount == 0
	Return
EndIf
If DisabledCount >=1
	Disabled1Ref.MoveTo CFHARTrapEvilStonePCDefence1Ref
	CFHARTrapEvilStonePCDefence1Ref.MoveTo CFHDarkWelkyndStorageRef
EndIf
If DisabledCount >=2
	Disabled2Ref.MoveTo CFHARTrapEvilStonePCDefence2Ref
	CFHARTrapEvilStonePCDefence2Ref.MoveTo CFHDarkWelkyndStorageRef
EndIf
If DisabledCount >=3
	Disabled3Ref.MoveTo CFHARTrapEvilStonePCDefence3Ref
	CFHARTrapEvilStonePCDefence3Ref.MoveTo CFHDarkWelkyndStorageRef
EndIf
If DisabledCount >=4
	Disabled4Ref.MoveTo CFHARTrapEvilStonePCDefence4Ref
	CFHARTrapEvilStonePCDefence4Ref.MoveTo CFHDarkWelkyndStorageRef
EndIf
If DisabledCount >=5
	Disabled5Ref.MoveTo CFHARTrapEvilStonePCDefence5Ref
	CFHARTrapEvilStonePCDefence5Ref.MoveTo CFHDarkWelkyndStorageRef
EndIf
End

Link to comment
Share on other sites

okay. My plan to prevent it from being cast twice didn't work.

I can either just settle for posting a warning or put it as a scroll spell, and re add a scroll to inventory on scripteffectfinish... probably another way.

It didn't seem to cause a problem when I cast it twice, although that was in my teshinghall with only 1 darkstone. Perhaps it's not an issues - I don't know how scripted spells work. I suspect that as the script is attached to a spell, assigned to a player that it doesn't act as a separate instance of the script each time I cast it, to prevent spell stacking.

 

Also, I couldn't get comparemodel path to work

Link to comment
Share on other sites

Every time a Scripted Effect spell is cast a new instance of the script is created and run. They don't know each other and can't access each other's variables. All variables start at uninitialized value every time a spell is cast.

 

What I was concerned of in regards to a limited number of spells active at the same time was the number of the replacement stones "only". As you're only having fixed number, 5 stones, trying to swap out 6 stones the first placeholder will vanish when the last stone gets exchanged, as it will be moved to the last stone's position. You can only have 5 replacement stones in place at once, so one of the dark stones will be missing its replacement.

 

But this was only for when it was still a spell to exchange a single dark stone only. Now that it loops through all dark stones in range storing "DisabledCount" in the spell script where it's at "0" again for every new spell you cast, maybe a more "global" storage method should be considered. As it is right now, there will be dark stones without replacements in place when the number of stones replaced (in sum over all spells running at the same time) exceeds 5.

 

Where is the spell cast at now anyways? If it's now On-Self, I think you're good to go, as I've never seen a single spell being in effect twice or more times on an actor so far. There could be an internal prevention in place here already.

 

What's the issue with CompareModelPath? Can you show me some lines of the scripts?

Link to comment
Share on other sites

  • Recently Browsing   0 members

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