tomahawk6633 Posted April 15 Share Posted April 15 (edited) Hello. I finally got some time to work on a mod that i'm very excited about. It's basically a mod that counts the player as a suspect of a crime if they were seen before and/or after committing a crime. Here is a schematic explaining how it works: Schematic The idea is to check whether if the player is being spotted before and after a crime is committed. Why? well, in vanilla Skyrim, the player is only found guilty of a crime if they are detected in the act. But what if there is a way to consider the player suspect of a crime moments before and/or after a crime is committed? This will force the player to be cautious and sneaky before attempting some dirty stuff. In the vanilla game you can enter a house, their owners see you and even address to you, you go to their bedrooms and as soon as you can get hidden you can start stealing or murdering without any consequences as long as nobody detects you. With this mod, if people sees you before or after you commit a crime, you will become a suspect and justice will come to you eventually if you are not careful enough. Now i've made three quests: One for the aliases to get the witness and/or the victim, one that controls the system retrieving the aliases and working with the actions the player does and calling the ambush mechanic when needed, and a third one that manages the ambush. So far the mod works as intended, and it's a game changer. Currently i need help with these: 1- About the ambush mechanic, is there a way to spawn the characters behind the player? also out of their sight? i searched for a method but couldn't find any, well maybe there are some functions that could do the trick but i couldn't find any examples. Here is the code that i use for the ambush: Scriptname AmbushQuestScript extends Quest Actor Property PlayerRef Auto GlobalVariable Property AmbushTimer Auto GlobalVariable Property MurderBounty Auto GlobalVariable Property StolenGoodsValue Auto GlobalVariable Property BountyValue Auto GlobalVariable Property WillGetAmbush Auto Location Property EastmarchHoldLocation Auto Location Property FalkreathHoldLocation Auto Location Property HaafingarHoldLocation Auto Location Property HjaalmarchHoldLocation Auto Location Property PaleHoldLocation Auto Location Property ReachHoldLocation Auto Location Property RiftHoldLocation Auto Location Property WhiterunHoldLocation Auto Location Property WinterholdHoldLocation Auto Location Property DLC2SolstheimLocation Auto ActorBase Property MercSolitude Auto ActorBase Property GuardSolitude Auto GlobalVariable Property ATimerShort Auto GlobalVariable Property MurderCount Auto GlobalVariable Property TheftCount Auto SPELL Property Invisibility Auto Faction Property CrimeFactionWhiterun Auto Faction Property CrimeFactionHaafingar Auto Faction Property CrimeFactionHjaalmarch Auto Faction Property CrimeFactionReach Auto Faction Property CrimeFactionEastmarch Auto Faction Property CrimeFactionWinterhold Auto Faction Property CrimeFactionFalkreath Auto Faction Property CrimeFactionRift Auto Faction Property CrimeFactionPale Auto Faction Property DLC2CrimeRavenRockFaction Auto GlobalVariable Property ATShortMin Auto GlobalVariable Property ATShortMax Auto Actor Property InvSolitude Auto Actor Property InvWhiterun Auto Actor Property InvMorthal Auto Actor Property InvMarkarth Auto Actor Property InvSolstheim Auto Actor Property InvWindhelm Auto Actor Property InvWinterhold Auto Actor Property InvFalkreath Auto Actor Property InvRiften Auto Actor Property InvDawnstar Auto ActorBase Property MercDawnstar Auto ActorBase Property MercFalkreath Auto ActorBase Property MercMarkarth Auto ActorBase Property MercMorthal Auto ActorBase Property MercRiften Auto ActorBase Property MercSolstheim Auto ActorBase Property MercWhiterun Auto ActorBase Property MercWindhelm Auto ActorBase Property MercWinterhold Auto ActorBase Property GuardDawnstar Auto ActorBase Property GuardFalkreath Auto ActorBase Property GuardMarkarth Auto ActorBase Property GuardMorthal Auto ActorBase Property GuardRiften Auto ActorBase Property GuardWhiterun Auto ActorBase Property GuardWindhelm Auto ActorBase Property GuardWinterhold Auto ActorBase Property GuardSolstheim Auto GlobalVariable Property AmbushGoing Auto GlobalVariable Property GotAmbushed Auto Event OnInit() int Timer = AmbushTimer.GetValueInt() Self.RegisterForSingleUpdate(60) Debug.Notification("Eyes are watching you!") endEvent Event OnUpdate() int Timer = AmbushTimer.GetValueInt() int TimerShort = Utility.RandomInt(ATShortMin.GetValueInt(), ATShortMax.GetValueInt()) If WillGetAmbush.GetValue() == 1 Debug.Notification("Checking locationl!") If PlayerRef.IsInLocation(EastmarchHoldLocation) CrimeFactionEastmarch.modcrimegold(CrimeFactionEastmarch.GetCrimeGold() + StolenGoodsValue.GetValueInt() + BountyValue.GetValueInt(), true) Ambush(InvWindhelm, MercWindhelm, GuardWindhelm) ElseIf PlayerRef.IsInLocation(FalkreathHoldLocation) CrimeFactionFalkreath.modcrimegold(CrimeFactionFalkreath.GetCrimeGold() + StolenGoodsValue.GetValueInt() + BountyValue.GetValueInt(), true) Ambush(InvFalkreath, MercFalkreath, GuardFalkreath) ElseIf PlayerRef.IsInLocation(HaafingarHoldLocation) CrimeFactionHaafingar.modcrimegold(CrimeFactionHaafingar.GetCrimeGold() + StolenGoodsValue.GetValueInt() + BountyValue.GetValueInt(), true) Ambush(InvSolitude, MercSolitude, GuardSolitude) ElseIf PlayerRef.IsInLocation(HjaalmarchHoldLocation) CrimeFactionHjaalmarch.modcrimegold(CrimeFactionHjaalmarch.GetCrimeGold() + StolenGoodsValue.GetValueInt() + BountyValue.GetValueInt(), true) Ambush(InvMorthal, MercMorthal, GuardMorthal) ElseIf PlayerRef.IsInLocation(PaleHoldLocation) CrimeFactionPale.modcrimegold(CrimeFactionPale.GetCrimeGold() + StolenGoodsValue.GetValueInt() + BountyValue.GetValueInt(), true) Ambush(InvDawnstar, MercDawnstar, GuardDawnstar) ElseIf PlayerRef.IsInLocation(ReachHoldLocation) CrimeFactionReach.modcrimegold(CrimeFactionReach.GetCrimeGold() + StolenGoodsValue.GetValueInt() + BountyValue.GetValueInt(), true) Ambush(InvMarkarth, MercMarkarth, GuardMarkarth) ElseIf PlayerRef.IsInLocation(RiftHoldLocation) CrimeFactionRift.modcrimegold(CrimeFactionRift.GetCrimeGold() + StolenGoodsValue.GetValueInt() + BountyValue.GetValueInt(), true) Ambush(InvRiften, MercRiften, GuardRiften) ElseIf PlayerRef.IsInLocation(WhiterunHoldLocation) CrimeFactionWhiterun.modcrimegold(CrimeFactionWhiterun.GetCrimeGold() + StolenGoodsValue.GetValueInt() + BountyValue.GetValueInt(), true) Ambush(InvWhiterun, MercWhiterun, GuardWhiterun) ElseIf PlayerRef.IsInLocation(WinterholdHoldLocation) CrimeFactionWinterhold.modcrimegold(CrimeFactionWinterhold.GetCrimeGold() + StolenGoodsValue.GetValueInt() + BountyValue.GetValueInt(), true) Ambush(InvWinterhold, MercWinterhold, GuardWinterhold) ElseIf PlayerRef.IsInLocation(DLC2SolstheimLocation) DLC2CrimeRavenRockFaction.modcrimegold(DLC2CrimeRavenRockFaction.GetCrimeGold() + StolenGoodsValue.GetValueInt() + BountyValue.GetValueInt(), true) Ambush(InvSolstheim, MercSolstheim, GuardSolstheim) EndIf EndIf WillGetAmbush.SetValue(0) EndEvent Function Ambush(Actor Investigator, ActorBase Bodyguard, ActorBase Guard) Debug.Notification("Function processing!") ObjectReference myGuard = PlayerRef.PlaceAtMe(Guard) myGuard.MoveTo(PlayerRef, 500.0 * Math.Sin(PlayerRef.GetAngleZ()), 500.0 * Math.Cos(PlayerRef.GetAngleZ()), PlayerRef.GetHeight()) ObjectReference myGuard2 = PlayerRef.PlaceAtMe(Guard) myGuard2.MoveTo(PlayerRef, 500.0 * Math.Sin(PlayerRef.GetAngleZ()), 500.0 * Math.Cos(PlayerRef.GetAngleZ()), PlayerRef.GetHeight()) ObjectReference myGuard3 = PlayerRef.PlaceAtMe(Guard) myGuard3.MoveTo(PlayerRef, 500.0 * Math.Sin(PlayerRef.GetAngleZ()), 500.0 * Math.Cos(PlayerRef.GetAngleZ()), PlayerRef.GetHeight()) ObjectReference myBodyGuard = PlayerRef.PlaceAtMe(Bodyguard) myBodyGuard.MoveTo(PlayerRef, 500.0 * Math.Sin(PlayerRef.GetAngleZ()), 500.0 * Math.Cos(PlayerRef.GetAngleZ()), PlayerRef.GetHeight()) Investigator.MoveTo(PlayerRef, 500.0 * Math.Sin(PlayerRef.GetAngleZ()), 500.0 * Math.Cos(PlayerRef.GetAngleZ()), PlayerRef.GetHeight()) Invisibility.cast(Investigator, myBodyguard) Invisibility.cast(Investigator, myGuard) Invisibility.cast(Investigator, myGuard2) Invisibility.cast(Investigator, myGuard3) Invisibility.cast(Investigator, InvSolitude) (myGuard as Actor).PathToReference(PlayerRef, 0.5) (myGuard2 as Actor).PathToReference(PlayerRef, 0.5) (myGuard3 as Actor).PathToReference(PlayerRef, 0.5) (myBodyGuard as Actor).PathToReference(PlayerRef, 0.5) Investigator.PathToReference(PlayerRef, 0.5) StolenGoodsValue.SetValue(0) BountyValue.SetValue(0) GotAmbushed.SetValue(1) utility.wait(1.0) EndFunction What this script does is the following: It spawns a delta force team of one investigator (unique actor level 40 spellsword), one bodyguard (leveled actor, level 20-30) and three leveled guards, all of them with the same factions as the guards from the corresponding holds. If after committing a crime (after crossing a certain threshold) the player gets ambushed with a 60 seconds timer (i plan to add a random timer later but right now i'm still testing). I kinda get how the "moveto" function works, but my question basically is if there is a better way to do this? currently the npcs spawn correctly and i add an invisibility effect to make it more... palatable, but still you can see them spawning mid air for a split second so it looks kinda clunky. 2- Committing any crime doesn't matter as long as the system takes note of it. If i steal something before and/or after being watched i become suspect of theft (SuspectTheft = 1), same if i get detected before and/or after killing someone (SuspectMurder = 1). I add a misc non-playable item (DeadToken) and check if the corpse of the npc i just killed has it, in which case it will be ignored afterwards. I could add more aliases for bodies and witnesses but that feels wrong, like how many? should i add 5 more? ten more just in case? it would be nice if a more organic and simpler solution is found, and here comes the question: is there a reliable way to detect exactly whenever the player kills someone? Otherwise what i do is to check every second for corpses of npcs that the player has killed. But if there is a way i would appreciate you give me a hand because this would improve my mod a lot. The stealing part is covered as i can detect whenever the player adds a stolen item onto their inventory, trap its value and store it in "StolenGoodsValue", piling them up, only if the player has been seen though, otherwise the vanilla system works. 3- Another weak spot in my mod is that if i kill a witness to try to avoid becoming a suspect, it still counts as if i was seen so it's useless to do it. I wonder if there is a way to track the npc that saw me? maybe store it somewhere until it dies? 4- Another thing my mod omits, for convenience, is the steal of money, gold, as there would be too many checks for let's say stealing 60 gold at once from a chest. My mod would need to check every single damn coin added and stolen. That's too much. So i let the vanilla game deal with that. It seem obvious but i like to play with a mod that takes the stolen tag away for items under a certain threshold value as long as i'm undetected. The question would be if there is a way to implement this in this particular case where the tag is taken away from items? if a player doesn't play with a mod like that and relies in the vanilla stealing system then there is no need to keep track of the value of stolen goods as these are tracked by the vanilla game. Still something minor to consider. I could be more specific but don't want to overcharge the thread with scripts and explanations. My mod works as intended, but i need help optimizing some functions. In general any suggestions/insights/corrections would be appreciated. If you have any questions just ask. Edited April 15 by tomahawk6633 forgot to add some tags Link to comment Share on other sites More sharing options...
dylbill Posted April 15 Share Posted April 15 PlaceAtMe has an abInitiallyDisabled parameter. Set this to true. This will make the placed reference invisible until you want it to be visible. Then use .enable() (after moveto) to make them visible. Link to comment Share on other sites More sharing options...
xkkmEl Posted April 15 Share Posted April 15 On 4/15/2025 at 12:00 AM, tomahawk6633 said: is there a reliable way to detect exactly whenever the player kills someone? Otherwise what i do is to check every second for corpses of npcs that the player has killed. But if there is a way i would appreciate you give me a hand because this would improve my mod a lot. Expand @dylbill has uncharacteristically missed an opportunity to publicize his magic SKSE plugin which (I assume) allows you to receive all OnDeath( akKiller) events. Filtering for akKiller == playerRef should be a simple matter. Link to comment Share on other sites More sharing options...
dylbill Posted April 15 Share Posted April 15 On 4/15/2025 at 2:13 AM, xkkmEl said: @dylbill has uncharacteristically missed an opportunity to publicize his magic SKSE plugin which (I assume) allows you to receive all OnDeath( akKiller) events. Filtering for akKiller == playerRef should be a simple matter. Expand Lol true. I will admit I only skimmed the original comment, but you can use my papyrus functions mod to do this: Event OnInit() ;register this script (self) for when the player kills anyone DbSkseEvents.RegisterFormForGlobalEvent("OnDeathGlobal", self, Game.GetPlayer(), 1) EndEvent Event OnDeathGlobal(Actor Victim, Actor Killer) Debug.Notification(Killer.GetDisplayName() + " killed " + Victim.GetDisplayName()) EndEvent Link to comment Share on other sites More sharing options...
tomahawk6633 Posted April 15 Author Share Posted April 15 On 4/15/2025 at 12:46 AM, dylbill said: PlaceAtMe has an abInitiallyDisabled parameter. Set this to true. This will make the placed reference invisible until you want it to be visible. Then use .enable() (after moveto) to make them visible. Expand Ah, i missed that, it happens whe you just copy the examples and don't read the arguments. One issue solved. On 4/15/2025 at 3:38 AM, dylbill said: Lol true. I will admit I only skimmed the original comment, but you can use my papyrus functions mod to do this: Event OnInit() ;register this script (self) for when the player kills anyone DbSkseEvents.RegisterFormForGlobalEvent("OnDeathGlobal", self, Game.GetPlayer(), 1) EndEvent Event OnDeathGlobal(Actor Victim, Actor Killer) Debug.Notification(Killer.GetDisplayName() + " killed " + Victim.GetDisplayName()) EndEvent Expand This certainly looks too good to be real. Short and elegant. Will certainly give the fight to implement it. Thanks. Link to comment Share on other sites More sharing options...
dylbill Posted April 15 Share Posted April 15 On 4/15/2025 at 5:11 AM, tomahawk6633 said: Ah, i missed that, it happens whe you just copy the examples and don't read the arguments. One issue solved. This certainly looks too good to be real. Short and elegant. Will certainly give the fight to implement it. Thanks. Expand No problem if you need another example my mod NPC Death Alerts uses that event. Happy modding! Link to comment Share on other sites More sharing options...
Recommended Posts