Mousetick Posted December 16, 2021 Posted December 16, 2021 Discussion topic: SPID for Footprints by reza9892 Wiki Link Uses SPID to distribute the footprints spells from the footprints mod, instead of the cloak spell of the original mod. This replaces the plugin and most of the scripts of the original Footprints mod, and adds a footprint spell distribution configuration for SPID. I've been using it for a while and only noticed now that it had already been suggested by @Mercury71 so I'm posting this to potentially gather more attention and feedback. Dependencies/Requirements: The original Footprints mod. MCM Helper (an SKSE plugin + ESPFE plugin), used for the MCM UI. SPID. Installation: after the original Footprints mod, so that this mod overwrites the Footprints.esp plugin and scripts. Pluses: Greatly simplified scripts compared to original. Doesn't require the starting quest (MQ101 aka Unbound) to have reached a certain stage to start working. So it can be installed mid-game, and should be compatible with other alternate start mods than ASLAL. Minuses: There are a few reports in the comments section of player footprints not working after a while. I haven't encountered this issue myself in the few hours that I've been using this mod. Comparison of a couple of scripts between original mod and SPID for Footprints: Spoiler Original footprintsPlayerScript.psc ;/ Decompiled by Champollion V1.0.1 Source : footprintsPlayerScript.psc Modified : 2016-12-22 22:58:55 Compiled : 2021-02-14 19:41:13 User : Jon Computer : DESKTOP-HME808C /; scriptName footprintsPlayerScript extends ReferenceAlias ;-- Properties -------------------------------------- keyword property ActorTypeNPC auto quest property MQ101 auto formlist property footprintsSpellsFLST auto spell property footprintsCloakDispelAbility auto formlist property footprintsIgnoredWorldsFLST auto formlist property footprintsIgnoredLocationsFLST auto actor property PlayerRef auto keyword property footprintsFootstepsEffect auto spell property footprintsCloakAbility auto formlist property footprintsCustomPlayerSpellsFLST auto globalvariable property fp_IsFootprintsDisabled auto globalvariable property fp_UseCustomFootprints auto footprintsrefcountscript property footprintsQuest auto spell[] property FootprintsSpellsV1 auto globalvariable property fp_FootprintsRefCount auto race[] property FootprintsRacesV1 auto spell property footprintsFootstepsAbilityPlayer auto footprintsvcscript property footprintsVCQuest auto ;-- Variables --------------------------------------- Bool ignoreCurrentLocation = false ;-- Functions --------------------------------------- function OnRaceSwitchComplete() utility.Wait(1.00000) if PlayerRef.HasMagicEffectWithKeyword(footprintsFootstepsEffect) self.ResetPlayer() else self.ReapplySpell() endIf endFunction function ReapplySpell() race playerRace = PlayerRef.GetRace() if fp_UseCustomFootprints.GetValue() Int iCustomFLSTSize = footprintsCustomPlayerSpellsFLST.GetSize() while iCustomFLSTSize iCustomFLSTSize -= 1 spell CustomSpell = footprintsCustomPlayerSpellsFLST.GetAt(iCustomFLSTSize) as spell if CustomSpell as Bool && PlayerRef.AddSpell(CustomSpell, false) if PlayerRef.HasMagicEffectWithKeyword(footprintsFootstepsEffect) return else self.DispelRemove(CustomSpell) endIf endIf endWhile endIf if playerRace.HasKeyword(ActorTypeNPC) if self.DispelRemoveAdd(footprintsFootstepsAbilityPlayer) return endIf endIf Int i = FootprintsRacesV1.find(playerRace, 0) if i >= 0 if self.DispelRemoveAdd(FootprintsSpellsV1[i]) return endIf endIf endFunction Bool function DispelRemoveAdd(spell akSpell) self.DispelRemove(akSpell) utility.Wait(0.500000) if PlayerRef.AddSpell(akSpell, false) return true endIf return false endFunction function RemoveAllFootsteps() if !self.RemoveFootstepsInList(footprintsSpellsFLST) self.RemoveFootstepsInList(footprintsCustomPlayerSpellsFLST) endIf endFunction ; Skipped compiler generated GotoState Bool function DispelRemove(spell akSpell) if PlayerRef.DispelSpell(akSpell) PlayerRef.RemoveSpell(akSpell) return true endIf return false endFunction function ResetPlayer() if PlayerRef.HasMagicEffectWithKeyword(footprintsFootstepsEffect) self.RemoveAllFootsteps() utility.Wait(0.500000) endIf self.ReapplySpell() endFunction function OnLocationChange(Location akOldLoc, Location akNewLoc) ignoreCurrentLocation = false if footprintsIgnoredLocationsFLST.HasForm(akNewLoc as form) self.DispelRemove(footprintsCloakAbility) ignoreCurrentLocation = true elseIf footprintsIgnoredWorldsFLST.HasForm(PlayerRef.GetWorldSpace() as form) self.DispelRemove(footprintsCloakAbility) ignoreCurrentLocation = true endIf endFunction function OnPlayerLoadGame() if !MQ101.IsStageDone(250) self.GotoState("Waiting") return endIf Bool newGame = self.Init() if !newGame debug.Trace("~~~~~ Footprints: Running Maintenance ~~~~~", 0) self.Maintenance() endIf endFunction Bool function Init() debug.Trace("~~~~~ Footprints: Initializing ~~~~~", 0) Bool result = false if !footprintsVCQuest.IsVCInstalled() debug.Trace("~~~~~ Footprints: Installing VC ~~~~~", 0) footprintsVCQuest.Start() footprintsVCQuest.VersionCheckStart() result = true endIf self.UnregisterForUpdate() utility.Wait(0.500000) self.ResetPlayer() self.RegisterForSingleUpdate(4.00000) return result endFunction ; Skipped compiler generated GetState function Maintenance() if fp_IsFootprintsDisabled.GetValue() debug.Trace("~~~~~ Footprints: Reinstalling ~~~~~", 0) fp_IsFootprintsDisabled.SetValue(0 as Float) endIf footprintsVCQuest.VersionCheckStart() utility.Wait(1.00000) endFunction Bool function PauseCloaking() return ignoreCurrentLocation as Bool || PlayerRef.IsInInterior() || footprintsQuest.RefCountLimitReached() endFunction Bool function RemoveFootstepsInList(formlist flst) Int iSpellIndex = flst.GetSize() Bool spellRemoved = false while iSpellIndex > 0 iSpellIndex -= 1 spell currSpell = flst.GetAt(iSpellIndex) as spell if PlayerRef.DispelSpell(currSpell) PlayerRef.RemoveSpell(currSpell) spellRemoved = true endIf endWhile return spellRemoved endFunction function OnUpdate() PlayerRef.DispelSpell(footprintsCloakAbility) if fp_IsFootprintsDisabled.GetValue() debug.Trace("~~~~~ Footprints: Main Script Disabling ~~~~~", 0) self.DispelRemove(footprintsCloakAbility) self.GotoState("Dead") return endIf if self.PauseCloaking() else PlayerRef.AddSpell(footprintsCloakAbility, false) utility.Wait(0.300000) self.DispelRemove(footprintsCloakAbility) endIf self.RegisterForSingleUpdate(4.00000) endFunction function OnInit() if !MQ101.IsStageDone(250) self.GotoState("Waiting") return endIf self.Init() endFunction ;-- State ------------------------------------------- state Waiting function OnPlayerLoadGame() ; Empty function endFunction function onBeginState() debug.Trace("~~~~~ Footprints: Begin Waiting ~~~~~", 0) self.RegisterForSingleUpdate(10.0000) endFunction function Maintenance() ; Empty function endFunction function ReapplySpell() ; Empty function endFunction function OnRaceSwitchComplete() ; Empty function endFunction function OnLocationChange(Location akOldLoc, Location akNewLoc) ; Empty function endFunction function OnUpdate() if MQ101.IsStageDone(250) self.GotoState("") self.Init() else self.RegisterForSingleUpdate(10.0000) endIf endFunction endState ;-- State ------------------------------------------- state Dead function OnPlayerLoadGame() ; Empty function endFunction function onBeginState() debug.Trace("~~~~~ Footprints: Main Script Disabled ~~~~~", 0) self.RegisterForSingleUpdate(5.00000) self.RemoveAllFootsteps() if footprintsQuest.GetRefCount() > 0 PlayerRef.AddSpell(footprintsCloakDispelAbility, false) utility.Wait(0.500000) PlayerRef.RemoveSpell(footprintsCloakDispelAbility) endIf footprintsQuest.ResetCounts() endFunction function Maintenance() ; Empty function endFunction function ReapplySpell() ; Empty function endFunction function OnRaceSwitchComplete() ; Empty function endFunction function OnLocationChange(Location akOldLoc, Location akNewLoc) ; Empty function endFunction function OnUpdate() if !fp_IsFootprintsDisabled.GetValue() debug.Trace("~~~~~ Footprints: Re-enabling ~~~~~", 0) self.RegisterForSingleUpdate(1.00000) self.GotoState("") self.ReapplySpell() return else self.RegisterForSingleUpdate(5.00000) endIf endFunction endState This mod's version ;/ Decompiled by Champollion V1.0.1 Source : footprintsPlayerScript.psc Modified : 2016-12-22 22:58:55 Compiled : 2021-02-14 19:41:13 User : Jon Computer : DESKTOP-HME808C /; scriptName footprintsPlayerScript extends ReferenceAlias ;-- Properties -------------------------------------- quest property MQ101 auto formlist property footprintsIgnoredWorldsFLST auto formlist property footprintsIgnoredLocationsFLST auto actor property PlayerRef auto footprintsrefcountscript property footprintsQuest auto globalvariable property fp_IgnoreCurrentLocation auto spell property footprintsFootstepsAbilityPlayer auto ;-- Functions --------------------------------------- function OnInit() PlayerRef.addspell(footprintsFootstepsAbilityPlayer, False) endFunction Function OnLocationChange(Location akOldLoc, Location akNewLoc) fp_IgnoreCurrentLocation.setvalue(0 as float) if footprintsIgnoredLocationsFLST.HasForm(akNewLoc as form) fp_IgnoreCurrentLocation.setvalue(1 as float) elseIf footprintsIgnoredWorldsFLST.HasForm(PlayerRef.GetWorldSpace() as form) fp_IgnoreCurrentLocation.setvalue(1 as float) endIf endFunction Spoiler Original footprintsFootstepsScriptPlayer.psc ;/ Decompiled by Champollion V1.0.1 Source : footprintsFootstepsScriptPlayer.psc Modified : 2021-02-14 19:12:29 Compiled : 2021-02-14 19:41:08 User : Jon Computer : DESKTOP-HME808C /; scriptName footprintsFootstepsScriptPlayer extends ActiveMagicEffect ;-- Properties -------------------------------------- Actor property playerRef auto footprintsvcscript property footprintsVCQuest auto impactdataset[] property LeftIPDS auto impactdataset[] property RightIPDS auto {0 - Run, 1 - Walk, 2 - Sprint, 3 - Sneak, 4 - StrafeL, 5 - StrafeR 6 - Run, 7 - Walk, 8 - Sprint, 9 - Sneak, 10 - StrafeL, 11 - StrafeR (Barefoot)} spell property ParentSpell auto globalvariable property fp_IsSKSEInstalled auto globalvariable property fp_IsFootprintsDisabled auto ;-- Variables --------------------------------------- Int SNEAK = 3 Int WALK = 1 ImpactDataSet CurrLeftIPDS Int SPRINT = 2 Int L_STRAFE = 4 Armor CurrentBoots Int RUN = 0 Int strafeDir Int R_STRAFE = 5 Float fDirection Actor selfRef Armor PreviousBoots Int BootOffset = 0 ImpactDataSet CurrRightIPDS ;-- Functions --------------------------------------- function OnAnimationEvent(objectreference akSelfRef, String e) if e == "FootLeft" && !selfRef.IsSprinting() || e == "FootSprintLeft" akSelfRef.PlayImpactEffect(CurrLeftIPDS, "NPC L Calf [LClf]", 0 as Float, 0 as Float, -1 as Float, 128 as Float, false, false) elseIf e == "FootRight" && !selfRef.IsSprinting() || e == "FootSprintRight" akSelfRef.PlayImpactEffect(CurrRightIPDS, "NPC R Calf [RClf]", 0 as Float, 0 as Float, -1 as Float, 128 as Float, false, false) elseIf e == "JumpUp" || e == "JumpDown" akSelfRef.PlayImpactEffect(CurrLeftIPDS, "NPC L Thigh [LThg]", 0 as Float, 0 as Float, -1 as Float, 128 as Float, false, false) akSelfRef.PlayImpactEffect(CurrRightIPDS, "NPC R Thigh [RThg]", 0 as Float, 0 as Float, -1 as Float, 128 as Float, false, false) endIf endFunction function OnEffectFinish(Actor akTarget, Actor akCaster) ; Empty function endFunction Int function strafeDir() fDirection = selfRef.GetAnimationVariableFloat("Direction") if fDirection == 0.250000 return 1 elseIf fDirection == 0.750000 return -1 endIf return 0 endFunction function OnPlayerLoadGame() self.UnregisterForUpdate() if self as Bool && selfRef as Bool && ParentSpell as Bool selfRef.RemoveSpell(ParentSpell) endIf endFunction function OnRaceSwitchComplete() self.UnregisterForUpdate() self.GotoState("Dead") endFunction function ResetIPDS() CurrLeftIPDS = LeftIPDS[0] CurrRightIPDS = RightIPDS[0] endFunction function OnEffectStart(Actor target, Actor caster) if !target || target != playerRef || fp_IsFootprintsDisabled.GetValue() as Bool self.GotoState("Dead") return endIf selfRef = target if self.RegisterForAnimationEvent(selfRef as objectreference, "FootLeft") && self.RegisterForAnimationEvent(selfRef as objectreference, "FootRight") && self.RegisterForAnimationEvent(selfRef as objectreference, "FootSprintLeft") && self.RegisterForAnimationEvent(selfRef as objectreference, "FootSprintRight") && self.RegisterForAnimationEvent(selfRef as objectreference, "JumpUp") && self.RegisterForAnimationEvent(selfRef as objectreference, "JumpDown") CurrLeftIPDS = LeftIPDS[0] CurrRightIPDS = RightIPDS[0] self.GotoState("alive") endIf endFunction function SetCurrIPDS() Int offset = 0 strafeDir = self.strafeDir() if strafeDir == 0 if selfRef.IsSprinting() offset = SPRINT elseIf selfRef.IsRunning() offset = RUN elseIf selfRef.IsSneaking() offset = SNEAK else offset = WALK endIf elseIf !selfRef.IsRunning() && selfRef.IsWeaponDrawn() CurrLeftIPDS = LeftIPDS[WALK + BootOffset] CurrRightIPDS = RightIPDS[R_STRAFE + BootOffset] return elseIf strafeDir == -1 offset = L_STRAFE elseIf strafeDir == 1 offset = R_STRAFE endIf offset += BootOffset CurrLeftIPDS = LeftIPDS[offset] CurrRightIPDS = RightIPDS[offset] endFunction ; Skipped compiler generated GetState Bool function BootCheck() ; Empty function endFunction ; Skipped compiler generated GotoState ;-- State ------------------------------------------- state alive_skse function OnObjectEquipped(Form akBaseObject, objectreference akReference) if !fp_IsSKSEInstalled.GetValue() self.GotoState("alive") return endIf if akBaseObject as Armor self.BootCheck() endIf endFunction function OnUpdate() if !fp_IsSKSEInstalled.GetValue() self.GotoState("alive") return endIf fDirection = selfRef.GetAnimationVariableFloat("Direction") self.SetCurrIPDS() self.RegisterForSingleUpdate(0.500000) endFunction Bool function BootCheck() CurrentBoots = selfRef.GetWornForm(128) as Armor if CurrentBoots as Bool && CurrentBoots == PreviousBoots return false endIf if !CurrentBoots BootOffset = 6 else BootOffset = 0 endIf PreviousBoots = CurrentBoots self.SetCurrIPDS() return true endFunction function onBeginState() self.BootCheck() self.RegisterForSingleUpdate(2.00000) endFunction function OnObjectUnequipped(Form akBaseObject, objectreference akReference) if !fp_IsSKSEInstalled.GetValue() self.GotoState("alive") return endIf if akBaseObject as Armor self.BootCheck() endIf endFunction endState ;-- State ------------------------------------------- state Dead function OnAnimationEvent(objectreference akSelfRef, String e) ; Empty function endFunction function OnEffectFinish(Actor akTarget, Actor akCaster) ; Empty function endFunction function OnUpdate() ; Empty function endFunction function onBeginState() if self as Bool && selfRef as Bool && ParentSpell as Bool selfRef.RemoveSpell(ParentSpell) endIf endFunction endState ;-- State ------------------------------------------- state alive function OnUpdate() fDirection = selfRef.GetAnimationVariableFloat("Direction") self.SetCurrIPDS() self.RegisterForSingleUpdate(1.00000) endFunction function onBeginState() if fp_IsSKSEInstalled.GetValue() debug.Trace("~~~~~ Footprints: Switching to SKSE Footprints, SKSE Installed ~~~~~", 0) self.GotoState("alive_skse") return endIf self.RegisterForSingleUpdate(2.00000) endFunction endState This mod's version ;/ Decompiled by Champollion V1.0.1 Source : footprintsFootstepsScriptPlayer.psc Modified : 2021-02-14 19:12:29 Compiled : 2021-02-14 19:41:08 User : Jon Computer : DESKTOP-HME808C /; scriptName footprintsFootstepsScriptPlayer extends ActiveMagicEffect ;-- Properties -------------------------------------- impactdataset[] property LeftIPDS auto impactdataset[] property RightIPDS auto {0 - Run, 1 - Walk, 2 - Sprint, 3 - Sneak, 4 - StrafeL, 5 - StrafeR} spell property parentSpell auto spell property footprintsAddFootstepsSpell auto ;-- Variables --------------------------------------- Int SNEAK = 3 Int WALK = 1 ImpactDataSet CurrLeftIPDS Int SPRINT = 2 Int L_STRAFE = 4 Int RUN = 0 Int strafeDir Int R_STRAFE = 5 Float fDirection Actor selfRef ImpactDataSet CurrRightIPDS ;-- Functions --------------------------------------- function OnAnimationEvent(objectreference akSelfRef, String e) if e == "FootLeft" || e == "FootSprintLeft" akSelfRef.PlayImpactEffect(CurrLeftIPDS, "NPC L Calf [LClf]", 0 as Float, 0 as Float, -1 as Float, 128 as Float, false, false) elseIf e == "FootRight" || e == "FootSprintRight" akSelfRef.PlayImpactEffect(CurrRightIPDS, "NPC R Calf [RClf]", 0 as Float, 0 as Float, -1 as Float, 128 as Float, false, false) elseIf e == "JumpUp" || e == "JumpDown" akSelfRef.PlayImpactEffect(CurrLeftIPDS, "NPC L Thigh [LThg]", 0 as Float, 0 as Float, -1 as Float, 128 as Float, false, false) akSelfRef.PlayImpactEffect(CurrRightIPDS, "NPC R Thigh [RThg]", 0 as Float, 0 as Float, -1 as Float, 128 as Float, false, false) endIf endFunction Int function strafeDir() fDirection = selfRef.GetAnimationVariableFloat("Direction") if fDirection == 0.250000 return 1 elseIf fDirection == 0.750000 return -1 endIf return 0 endFunction function ResetIPDS() CurrLeftIPDS = LeftIPDS[0] CurrRightIPDS = RightIPDS[0] endFunction function OnEffectStart(Actor target, Actor caster) selfRef = target self.RegisterForAnimationEvent(selfRef as objectreference, "FootLeft") && self.RegisterForAnimationEvent(selfRef as objectreference, "FootRight") && self.RegisterForAnimationEvent(selfRef as objectreference, "FootSprintLeft") && self.RegisterForAnimationEvent(selfRef as objectreference, "FootSprintRight") && self.RegisterForAnimationEvent(selfRef as objectreference, "JumpUp") && self.RegisterForAnimationEvent(selfRef as objectreference, "JumpDown") CurrLeftIPDS = LeftIPDS[0] CurrRightIPDS = RightIPDS[0] self.GotoState("alive_skse") endFunction function SetCurrIPDS() Int offset = 0 strafeDir = self.strafeDir() if strafeDir == 0 if selfRef.IsSprinting() offset = SPRINT elseIf selfRef.IsRunning() offset = RUN elseIf selfRef.IsSneaking() offset = SNEAK else offset = WALK endIf elseIf !selfRef.IsRunning() && selfRef.IsWeaponDrawn() CurrLeftIPDS = LeftIPDS[WALK] CurrRightIPDS = RightIPDS[R_STRAFE] return elseIf strafeDir == -1 offset = L_STRAFE elseIf strafeDir == 1 offset = R_STRAFE endIf CurrLeftIPDS = LeftIPDS[offset] CurrRightIPDS = RightIPDS[offset] endFunction function OnRaceSwitchComplete() footprintsAddFootstepsSpell.cast(self.GetTargetActor()) self.GetTargetActor().removespell(parentSpell) endFunction ;-- State ------------------------------------------- state alive_skse function OnUpdate() fDirection = selfRef.GetAnimationVariableFloat("Direction") self.SetCurrIPDS() self.RegisterForSingleUpdate(0.500000) endFunction function onBeginState() self.RegisterForSingleUpdate(2.00000) endFunction endState
Mercury71 Posted December 16, 2021 Posted December 16, 2021 Tested for some time now and it works just fine so far i think.
gamingsrc Posted December 16, 2021 Posted December 16, 2021 (edited) In my opinion Footprint is a mod that leave dirty traces in saves. Just look at the official page where the author advise to disable but not completely remove the mod in a game. Add to this his odd decision to start the mod when "MQ101 Stage 250 is done". He could obviously just select the condition MQ101.GetStage()>= 250. Also SPID is the kind of DLL that add stuffs in a scripted way to the game (you have no option to contrôle what changes it made to the game) in a generic way. I avoid to install it in my game. Edited December 16, 2021 by gamingsrc Spelling corrections (I do a lot).
Mousetick Posted December 16, 2021 Author Posted December 16, 2021 38 minutes ago, gamingsrc said: Add to this his odd decision to start the mod when "MQ101 Stage 250 is done". He could obviously just select the condition MQ101.GetStage()>= 250. This mod here (SPID for Footprints) eliminates all that MQ101 nonsense. Read the scripts I posted if you want proof.
DoubleYou Posted December 16, 2021 Posted December 16, 2021 I have been testing this mod and I recommend it.
z929669 Posted December 19, 2021 Posted December 19, 2021 On 12/16/2021 at 2:58 PM, DoubleYou said: I have been testing this mod and I recommend it. Ditto. Marked Accepted, created mod page, added to ModList, and updated Changelog
Mousetick Posted February 8, 2022 Author Posted February 8, 2022 It took me a while to find a solution so this may be helpful to others. The problem: footprints don't work on a new game starting as a new character with the preset equipment. They work on NPCs, but not on the player. The solution: unequip shoes/boots and re-equip them. The issue is specific to this mod. It doesn't occur with the original Footprints mod used alone. Other factors may be involved, like using ASLAL to start - I don't know.
soupdragon Posted July 10, 2022 Posted July 10, 2022 Its a bit slow to apply footprints to new npc's it also works fine on the player you've got to change location at least once for it to pick up the changes both are to related to the way SPID work afaik. Also some people report that if you change race i.e. into a werewolf or vampire lord you have to re-apply the spell via console when you revert back to human form or the footprints stop working though that may be fixed by a location change again I imagine. theres also this mod released which claims to eliminate the papyrus etc log spam this mod generates. So thats a fix for a fix for an original mod which could probably do with a rewrite at this point.
Mousetick Posted July 10, 2022 Author Posted July 10, 2022 3 hours ago, soupdragon said: theres also this mod released which claims to eliminate the papyrus etc log spam this mod generates. So thats a fix for a fix for an original mod which could probably do with a rewrite at this point. The jury is still out on whether that SPID for Footprints fix mod actually fixes anything. The MA has yet to answer the simple question that was asked in the comments section: "What in-game issue(s) does this fix?" I've looked at the changes in xEdit, and I don't see any actual functional fix. It does correct some script property bindings which in turn eliminates Papyrus warnings spam, that is always good. It's nice to have, and it certainly doesn't hurt to use it, but it's not a must-have IMHO. I agree Footprints is long in the tooth and has become an ugly maintenance mess due to closed permissions.
DoubleYou Posted July 10, 2022 Posted July 10, 2022 I think I heard a rumor that someone was making a new footprints mod. That will hopefully fix the messy situation of Footprints.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now