SAC Posted January 4, 2021 Posted January 4, 2021 For some reason which I don't understand yet, some NPC don't "play along" with my scripts. While still looking into why this happens, I am hoping to circumvent the issue by "doppelganging" them, meaning creating a clone without packages, unique and essential flags, factions or other attributes, but preserving (only) the appearance and outfit. placeactoratme(target.getactorbase()) ^^this is not a solution, it just propagates the (still unknown) issues which block the original NPC from responding to my scripts. Is there a mod or function / library which enables this? For FO4, AAF has a number of API functions to duplicate appearance / outfit / decals, is there something similar for skyrim? TYVM
SAC Posted January 12, 2021 Author Posted January 12, 2021 Coming back to this thread, what is the proper way to clone an NPC? Meaning: clone = target.placeatme(target.getactorbase()) as actor works fine for unique NPCs, but how to clone an actor coming from a leveled list? It should be placeatme(target.getleveledactorbase()) however the CK wiki warns that "Creating an actor in script from a temporary ActorBase will cause a CTD when the temporary ActorBase is garbage collected. To make a copy of an actor, use GetActorBase() instead." I don't know what garbage collection is, but I don't want to introduce random CTDs. So, how can I do it? Thanks cc @zaira LE: found an implementation in PAHE (basically a brute force repeated placeatme(original_actor.getactorbase()) until the placed actor matches the original), have contacted @CliftonJD to get permission to use it.
Mister X Posted January 12, 2021 Posted January 12, 2021 Why is PlaceActorAtMe() no solution for you? You explicitely get send to that function for Leveled Actors from PlaceAtMe() Edit: I think I've found the problem. The most simple solution I can think of is to create a global variable or property in your script and fill it with your ActorBase once when running your function. Afterwards only use said variable instead of any function to get your ActorBase.This hopefully should keep the ActorBase persistent. Not tested though.
SAC Posted January 12, 2021 Author Posted January 12, 2021 4 hours ago, Mister X said: Why is PlaceActorAtMe() no solution for you? You explicitely get send to that function for Leveled Actors from PlaceAtMe() Edit: I think I've found the problem. The most simple solution I can think of is to create a global variable or property in your script and fill it with your ActorBase once when running your function. Afterwards only use said variable instead of any function to get your ActorBase.This hopefully should keep the ActorBase persistent. Not tested though. The problem is that actorbase(), in the case of generic characters which spawn from leveled lists, does not return the same actor which was randomly instantiated initially by the game engine when entering the cell, but another random actor from the same leveled list. Basically you can get a male hunter instead of a female hunter, or different faces or equipment combinations, depending on the extent of the underlying leveled lists / leveled item lists.
CliftonJD Posted January 12, 2021 Posted January 12, 2021 5 hours ago, Mister X said: Why is PlaceActorAtMe() no solution for you? You explicitely get send to that function for Leveled Actors from PlaceAtMe() Edit: I think I've found the problem. The most simple solution I can think of is to create a global variable or property in your script and fill it with your ActorBase once when running your function. Afterwards only use said variable instead of any function to get your ActorBase.This hopefully should keep the ActorBase persistent. Not tested though. original pahcore from layam used that encounter zone with its own property in the script then attaches the clone produced by it to another property in the script using force ref to, however the reason to try to use the leveled actorbase for this is to avoid copying the naked actor (or any other new outfit assigned) to all respawns of that actor base. however if you're only trying to hang the clone, would you be trying to strip the clone, or would the original actor outfit suffice? assuming you don't care what the clone is wearing as it hangs there, then its best to use that method with an exclusion on the property reference the clone is attached to---do Not choose to allow saving data or again you'll run into the saved data effecting the actor base. for example original pahcore and blabla's final sl extension both saved the data creating the friendly bandits bug on all new spawns of that actor base, basically that occurs with the cleaned factions of the clone getting cleaned of the original base. 6 hours ago, SAC said: it should be placeatme(target.getleveledactorbase()) however the CK wiki warns that "Creating an actor in script from a temporary ActorBase will cause a CTD when the temporary ActorBase is garbage collected. To make a copy of an actor, use GetActorBase() instead." I don't know what garbage collection is, but I don't want to introduce random CTDs. yes, i went thru extensive testing on that when it was said to cure the naked bandits bug. basically the garbage collector happens everytime you save/quit//reload. the garbage collector changes the resulting clone of the leveled actor base to something else. sometimes it changes the sex of the npc, other times it changes the appearance or the voicetype, sometimes it deletes it entirely, while most commonly it will change into something completely different such as a sabrecat from a human. the result can range from unpredictable to completely unstable save loads. 6 hours ago, SAC said: LE: found an implementation in PAHE (basically a brute force repeated placeatme(original_actor.getactorbase()) until the placed actor matches the original), the leveled actorbase toggle in pahe at best reduces the odds of encountering the same naked actor base with a bastardized mixture of leveled actor base clone, leveled encounter zone clone, and base actor encounter zone. its an attempt at stabilizing the leveled actor base, but since it still involves the original actorbase, it doesn't completely solve the naked bandit bug if/when you still encounter that same actor base the basic reason for that to be attached to a toggle with it default de-activated is that it often fails to find a valid clone more frequently as the user adds more npc variant mods. for example, some of the random wandering wenches and majority of obis bandits will fail valid enslavement and produce none object slaves edit: at some later point in the future i'd like to revamp that so that if/when it fails to achieve a valid slave, the result is completely discarded and recloned using the default encounterzone
zaira Posted January 12, 2021 Posted January 12, 2021 7 hours ago, SAC said: Coming back to this thread, what is the proper way to clone an NPC? Game.ForceThirdPerson() PlayerRef.SetAlpha(0.0) MariasUtils.PlaceInFrontOf(PlayerRef,block.GetRef(),20) clone = PlayerRef.PlaceActorAtMe(Game.GetForm(0x7) as ActorBase) MariasUtils.PlaceBehind(clone,block.GetRef(),20) clone.RemoveAllItems() clone.SetAlpha(1.0) clone.EquipItem(PrisonerCuffsPlayer,true) This is what I did in ME 1.0 for players head chop scene - so to avoid the kill reload I clone the player. For generic NPC I would simply restrict this "feature" to unique npcs
zaira Posted January 12, 2021 Posted January 12, 2021 Or do a simple trick: Place an execution hood onto the victim (maybe only for non-unique ones) and clone the outfit. So the actor must not be identical and nobody will recognize it (at least for the same races) Or do a 1:1 copy by hand - unify races and nifs. RaceMenu on Nexus contains a plugin which clones appearance - good place to grab some inspirations
zaira Posted January 12, 2021 Posted January 12, 2021 Maybe you give Form.TempClone() a try - but this clone don't persists.
SAC Posted January 13, 2021 Author Posted January 13, 2021 Thank you @CliftonJD @zaira , definitely making solid progress actor clone = target.placeatme(target.getactorbase()) as actor while actor_base_is_similar(target.getleveledactorbase(), clone.getleveledactorbase()) == false clone.delete() clone = target.placeatme(target.getactorbase()) as actor endwhile This works perfectly fine, it visibly shuffles through a few clones before settling on the proper one. The process takes 1-2 seconds at most. actor_base_is_similar(actorbase1, actorbase2) is a function which I've taken copy-paste from PAHE (with permission), I won't post it here, anyone can see it in the PAHE scripts / contact @CliftonJD to comment on it. Basically it checks that the original and clone bases match in terms of gender and a number of other criteria. Stuff to do next: - add a safety counter into the while loop so it doesn't get stuck for some reason (PAHE has 50 retries before giving up, as a safety) - fine tune the process with actor.setalpha() and/or spawn the clones somewhere else and bring back just the "matching winner" so the user doesn't see the cloning selection process - manually re-adding a schlong (base form xx000D77) or search the original actor for slot 52 for male actors, the unequipped clone somehow loses the schlong in the process (but it might be my unequip script is not subtle enough yet) - subtleties like overlays replication which should require deep diving into racemenu and/or slavetats APIs, as @zaira mentioned (actually, FO4 AAF has a cloning API function based on Racemenu, maybe at some point I'll try to replicate it, assuming Racemenu SSE has the same APIs) - maybe play more with replicating some equipment, but might not be really needed - Form.TempClone() is a new thing to me which I'll have to look into; I guess since I respawn them at every cell visit I don't need persistence anymore, TBD So, perfect, thank you, for now I have all the core information I need, I'll proceed to finetuning and follow-up if needed. Cheers!!
CliftonJD Posted January 13, 2021 Posted January 13, 2021 8 hours ago, SAC said: subtleties like overlays replication which should require deep diving into racemenu and/or slavetats APIs, as @zaira mentioned (actually, FO4 AAF has a cloning API function based on Racemenu, maybe at some point I'll try to replicate it, assuming Racemenu SSE has the same APIs) part of the switchActors function: Spoiler If (Game.GetModByName("SlaveTats.esp") != 255) If JContainers.isInstalled() && SlaveTats.Version() != "" original.enableAI(false) int array = JArray.object() JValue.retain(array) If !SlaveTats.query_applied_tattoos(original, 0, array) int index = JArray.count(array) debug.trace("[PAHCore] SlavTats: " + index + " entries") if index > 0 while index > 0 index -= 1 SlaveTats.add_tattoo(clone, JArray.getObj(array, index), silent = true) endWhile SlaveTats.synchronize_tattoos(clone, true) EndIf JValue.release(array) EndIf EndIf EndIf
SAC Posted January 15, 2021 Author Posted January 15, 2021 Hit a snag which @zaira mentioned to me that it might happen - spawning actors with HDT (physics) hair CTDs the game. Understand this is known from LE, it does happen in SSE too. Is there a workaround?
zaira Posted January 15, 2021 Posted January 15, 2021 1 hour ago, SAC said: Is there a workaround? Execution Hood
SAC Posted January 15, 2021 Author Posted January 15, 2021 4 hours ago, zaira said: Execution Hood True, although I'm not sure the game even has the time to equip the hood between placeatme and equip hood without crashing first due to the hair. But that would mean equipping the hood on everyone, since I don't have a way to pre-check for HDT before spawning the clone, which is not the way I intend the mod to work, esthetically. I'll do something else, I'll ask the player to choose if they have any HDT haired NPC mods installed when they install my mod + add an MCM option, and program the script to skip the cloning and just havok the original bodies if the user selects this option. LE: Nevermind, I'll try spawn => equip hood => wait some => remove hood, see how it goes
SAC Posted January 15, 2021 Author Posted January 15, 2021 4 hours ago, SAC said: LE: Nevermind, I'll try spawn => equip hood => wait some => remove hood, see how it goes Bingo, seems to work
Recommended Posts
Archived
This topic is now archived and is closed to further replies.