In this tutorial I will describe how to exactly set up a Mod that allows detection of objects that are not actor, via Hotkey. The knowledge you gain here does not have to be applied to a Hotkey, you could transport it to a spell, a referencealias, activator or whatever else comes to mind. I'm using Hotkeys due to personal preference. First let's talk about the problems when trying to applie shaders to objects that are not actors. There's various reasons why you can't easily use a detection spell to allow for detection of not living objects. I'll list some:
1. Particle shaders, responsible for all detection spells in game/CK, can only be applied to Actors.
2. Effect shaders do not allow for simple exchange of meshes that have the Z-TestBuffer enabled for membrane shaders. Which basically means you can't use 'shine through' effect on membrane shaders, only particle shaders allows doing this.
3. You can't simply apply image space modifiers and detect life shaders to actors/player to mimc detection events. Both elements are well coordinated in the detection MGEF's.
Ok, so now I'm going to show you how to circumvent those problems. I strongly recommend making the Mod based on all DLC's in order to gain full access to all available files, though the whole thingy is probably feasible with only using Skyrim.esm, I assume. First here's what you need in your Mod:
1. One FormList build from scratch
2. One Magic Effect duplicated of the 'DetectLifeEnemyInteriorSelfFF' MGEF
3. One Spell duplicated of the 'DetectLife' Spell
4. One Actor duplicated of the 'DLC2DetectLifeBoxActor'
5. One Quest build from scratch
6. One Effect Shader duplicated or build from scratch, whatever you like
Make sure to prefix all of your instances, for example, if you would build a arrow detection Mod via Hotkey, then name the files FAH_DetectArrowSpell (FAH = Find Arrow Hotkey). Save your Mod with the corresponding prefix. Now let's do the easy things first:
1. Go to your duplicated actor instance and change the height from 1.00 to 0.10 (smallets size available dependend on the mesh). You do this in order to place the Detection Dummy close to the object you want to detect, so you can't see it.
2. Now open your FormList and put every single instance you want to detect later in game, into the FormList, in my case I've put all playable arrows into the Formlist
3. Now go to your MGEF, remove all conditions then add this condition...
Subject -> GetIsID -> YourDetectorDummy == 1.00 AND
...remove everything else you feel isn't needed inside the MGEF. Life for example sound, and other Visual Effects then 'LifeDetectedFriend' and 'DetectLife01IMod'.
4. Now open your Spell and remove all effects adding only your custom made effect. Also remove all auto calculations, but leave 'Area Effect Ignores LOS' ticked.
5. Now it's a good time to save your Mod again.
6. Now Open your effect shader and turn off the particle shader, and trun on the membrane shader. Remove the palette texture and copy this to your fill texture...
Effects\GhostShaderRedGrad.dds
...for color, I recommend using orange colors all the way. Everything else is open for you to experiment. For example, my membrane shader is light blue with a white glow.
Ok, so now this was the membrane shader that gets applied to the items you want to detect, in my case arrows, here's a picture on how it looks in game:
http://www.nexusmods.com/skyrim/Images/448720/?
The problem is, you still can't detect the arrows under all the grass and from far away. This is why we now need to script the core of this tutorial inside our quest.
1. Set up the quest for SkyUI MCM and SKSE Hotkey feature, aka Start Game Enabled/Run Once and a PlayerAlias with the SKI_PlayerLoadGameAlias script attached
2. Attach a script to your quest similar to the following code:
Scriptname FAH_FindArrowQuestScript extends SKI_ConfigBaseActor Property PlayerREF AutoActorBase Property FAH_DetectorDummy AutoFormList Property FAH_ArrowFormList AutoEffectShader Property FAH_MembraneShader AutoSpell Property FAH_DetectArrowSpell AutoInt KeyFindArrowInt KeyArrowEvent OnConfigInit() RestoreValues()EndEventEvent OnGameReload() Parent.OnGameReload() RestoreValues()EndEventEvent OnPageReset(string Page) If (Page == "") Return Else UnloadCustomContent() EndIf If (Page == "Hotkey") SetCursorFillMode(TOP_TO_BOTTOM) SetCursorPosition(0) AddHeaderOption("Find Arrow Hotkey") KeyFindArrow = AddKeyMapOption("Fidn Arrow Hotkey", KeyArrow) EndIfEndEventEvent OnOptionHighlight(int Option) If (Option == KeyFindArrow) SetInfoText("Set your find Arrow Hotkey!") EndIfEndEventEvent OnOptionKeyMapChange(int a_option, int a_keyCode, string a_conflictControl, string a_conflictName) If a_option == KeyFindArrow bool Continue = true If (a_conflictControl != "") string msg If (a_conflictName != "") msg = "This key is already mapped to:\n'" + a_conflictControl + "'\n(" + a_conflictName + ")\n\nAre you sure you want to continue?" Else msg = "This key is already mapped to:\n'" + a_conflictControl + "'\n\nAre you sure you want to continue?" EndIf Continue = ShowMessage(msg, true, "Yes", "No") EndIf If (Continue) KeyArrow = a_keyCode SetKeymapOptionValue(a_option, a_keyCode) RegisterForKey(KeyArrow) EndIf EndIfEndEventEvent OnKeyDown(Int aiKeyCode) If aiKeyCode == KeyArrow && !Utility.IsInMenuMode() UnregisterForKey(KeyArrow) ;Remove dummy detectors when key is pressed again Block DetectorRef.Disable() DetectorRef.Delete() DetectorRef2.Disable() DetectorRef2.Delete() DetectorRef3.Disable() DetectorRef3.Delete() DetectorRef4.Disable() DetectorRef4.Delete() DetectorRef5.Disable() DetectorRef5.Delete() DetectorRef6.Disable() DetectorRef6.Delete() DetectorRef7.Disable() DetectorRef7.Delete() DetectorRef8.Disable() DetectorRef8.Delete() DetectorRef9.Disable() DetectorRef9.Delete() DetectorRef10.Disable() DetectorRef10.Delete() DetectorRef11.Disable() DetectorRef11.Delete() DetectorRef12.Disable() DetectorRef12.Delete() DetectorRef13.Disable() DetectorRef13.Delete() DetectorRef14.Disable() DetectorRef14.Delete() DetectorRef15.Disable() DetectorRef15.Delete() DetectorRef16.Disable() DetectorRef16.Delete() DetectorRef17.Disable() DetectorRef17.Delete() DetectorRef18.Disable() DetectorRef18.Delete() DetectorRef19.Disable() DetectorRef19.Delete() DetectorRef20.Disable() DetectorRef20.Delete() ;Registering Arrows on the ground Block ObjectReference ArrowFloor = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor2 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor3 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor4 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor5 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor6 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor7 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor8 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor9 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor10 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor11 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor12 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor13 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor14 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor15 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor16 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor17 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor18 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor19 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ObjectReference ArrowFloor20 = Game.FindRandomReferenceOfAnyTypeInListFromRef(FAH_ArrowFormList, PlayerREF, 3000.0) ;Applie membrane shader to arrows found Block FAH_MembraneShader.Play(ArrowFloor, 30.0) FAH_MembraneShader.Play(ArrowFloor2, 30.0) FAH_MembraneShader.Play(ArrowFloor3, 30.0) FAH_MembraneShader.Play(ArrowFloor4, 30.0) FAH_MembraneShader.Play(ArrowFloor5, 30.0) FAH_MembraneShader.Play(ArrowFloor6, 30.0) FAH_MembraneShader.Play(ArrowFloor7, 30.0) FAH_MembraneShader.Play(ArrowFloor8, 30.0) FAH_MembraneShader.Play(ArrowFloor9, 30.0) FAH_MembraneShader.Play(ArrowFloor10, 30.0) FAH_MembraneShader.Play(ArrowFloor11, 30.0) FAH_MembraneShader.Play(ArrowFloor12, 30.0) FAH_MembraneShader.Play(ArrowFloor13, 30.0) FAH_MembraneShader.Play(ArrowFloor14, 30.0) FAH_MembraneShader.Play(ArrowFloor15, 30.0) FAH_MembraneShader.Play(ArrowFloor16, 30.0) FAH_MembraneShader.Play(ArrowFloor17, 30.0) FAH_MembraneShader.Play(ArrowFloor18, 30.0) FAH_MembraneShader.Play(ArrowFloor19, 30.0) FAH_MembraneShader.Play(ArrowFloor20, 30.0) ;Place dummy detector at arrows Block ObjectReference DetectorRef = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef2 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef3 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef4 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef5 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef6 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef7 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef8 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef9 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef10 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef11 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef12 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef13 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef14 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef15 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef16 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef17 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef18 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef19 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ObjectReference DetectorRef20 = ArrowFloor.PlaceActorAtMe(FAH_DetectorDummy) ;Cast detection spell on dummy detector Block FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef2) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef3) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef4) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef5) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef6) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef7) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef8) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef9) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef10) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef11) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef12) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef13) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef14) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef15) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef16) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef17) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef18) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef19) FAH_DetectArrowSpell.Cast(PlayerREF, DetectorRef20) Utility.Wait(5) ;Remove previously placed dummy detectors Block DetectorRef.Disable() DetectorRef.Delete() DetectorRef2.Disable() DetectorRef2.Delete() DetectorRef3.Disable() DetectorRef3.Delete() DetectorRef4.Disable() DetectorRef4.Delete() DetectorRef5.Disable() DetectorRef5.Delete() DetectorRef6.Disable() DetectorRef6.Delete() DetectorRef7.Disable() DetectorRef7.Delete() DetectorRef8.Disable() DetectorRef8.Delete() DetectorRef9.Disable() DetectorRef9.Delete() DetectorRef10.Disable() DetectorRef10.Delete() DetectorRef11.Disable() DetectorRef11.Delete() DetectorRef12.Disable() DetectorRef12.Delete() DetectorRef13.Disable() DetectorRef13.Delete() DetectorRef14.Disable() DetectorRef14.Delete() DetectorRef15.Disable() DetectorRef15.Delete() DetectorRef16.Disable() DetectorRef16.Delete() DetectorRef17.Disable() DetectorRef17.Delete() DetectorRef18.Disable() DetectorRef18.Delete() DetectorRef19.Disable() DetectorRef19.Delete() DetectorRef20.Disable() DetectorRef20.Delete() RegisterForKey(KeyArrow) EndIfEndEventFunction RestoreValues() RegisterForKey(KeyArrow)EndFunction
So, ok, now you can save your Mod, go into the game, define a hotkey shoot some arrows, and see what happens if you press the hotkey. Shold be looking similar to this:
http://www.nexusmods.com/skyrim/Images/448730/?
You probably ask yourslef why I've made so many instances of all properties? Well, I could have done this via ReferenceAlias too, but those are extremely demanding in terms of polling, so using it via hotkeys is much more reliefing for your game. The disable/delete blocks make sure your savegame doesn't get bloated with instances of actors and also removes the dummy detector if you press the key again. The reason for so many instances is simpy because Bethesda didn't provided us with a...
FindAnyReferenceOfAnyTypeInListFromRef
...function... Maybe the guys from SKSE can do something?