invisible_rodent Posted April 27, 2023 Posted April 27, 2023 Hi All, I'm working on a small mod (my first) to add Giantess/shrinking content to SE. The gameplay loop is various events shrink the player and paying money to the follower restores height. What's working is a quest form for giantess-themed Misc topic info content and the a dialogue quest to regain height. The shrink/growth mechanism was originally based on an old script I found on the GiantessCity forum (my scripting skills are rudimentary). I first tried this in a new "Shrinking Disease" assigned to different Races but on play-testing it caused huge script lag and eventually crashed the game. This is the script: Scriptname mgtsShrink extends ActiveMagicEffect Import Utility float Property TargetScale Auto {The scale towards which the target should grow (e.g. 1.5 is 50% base size)} float Property GrowthPerEffect Auto {The amount of size change per effect, negative value causes shrinking (e.g. if .2, a target with scale .5 grows to .6 (.2 * .5 = .1 increase)} Event OnEffectStart(Actor akTarget, Actor akCaster) float currentScale = akTarget.GetScale() float nextScale = currentScale + currentScale * GrowthPerEffect float loopScale = currentScale float changePerLoop = 0.003 int numberOfLoops = 0 ; figure out if scale is already being effected Wait(0.2) float changedScale = akTarget.GetScale() if currentScale == changedScale if (GrowthPerEffect > 0) if nextScale > TargetScale nextScale = TargetScale EndIf numberOfLoops = Math.Floor(Math.abs(currentScale - nextScale)/changePerLoop) Debug.Notification("Entering Grow...currentScale=" + currentScale + ", nextScale=" + nextScale) While numberOfLoops > 0 loopScale+=changePerLoop akTarget.SetScale(loopScale) numberOfLoops-=1 EndWhile Else if nextScale < TargetScale nextScale = TargetScale EndIf numberOfLoops = Math.Floor(Math.abs(currentScale - nextScale)/changePerLoop) ;Debug.Notification("Entering Shrink...currentScale=" + currentScale + ", nextScale=" + nextScale) While numberOfLoops > 0 loopScale-=changePerLoop akTarget.SetScale(loopScale) numberOfLoops-=1 EndWhile EndIf if currentScale == akTarget.GetScale() Debug.Notification("This spell isn't powerful enough to change the target's size") Else ;Debug.Notification("Exiting Grow/Shrink...currentScale=" + akTarget.GetScale()) EndIf Else Debug.Notification("The script is already running!") EndIf EndEvent I then made a "Shrinking Spell" with a shrinking MagicEffect based on this script and called it in Papyrus End Fragments in Dialogue Topics ("shrinkspell.Cast(game.getplayer())"). This seems to avoid the script lag but the game is crashing regularly, probably because of the known issues with SetScale() (see the creation kit wiki). I believe NetImmerse can avoid this problem (see this reddit post). Can anyone recommend how to replace the SetScale() command in the script with NetImmerse instead, if this is indeed the problem? Cheers!
traison Posted April 28, 2023 Posted April 28, 2023 (edited) NetImmerse.psc:27 ; Sets the scale of a particular Nif node float Function GetNodeScale(ObjectReference ref, string node, bool firstPerson) native global Function SetNodeScale(ObjectReference ref, string node, float scale, bool firstPerson) native global Not sure why the reddit post passes a nif file name as the node name. I would do something like this instead: NetImmerse.SetNodeScale(player, "Scene Root", 0.95, false) Note however that testing this in NifSkope does not actually resize the mesh, so this may not work; or maybe that's what passing the nif name is for - some sort of workaround. Edited April 28, 2023 by traison
invisible_rodent Posted May 2, 2023 Author Posted May 2, 2023 Thank you! From the original post, it looks the script overload error was from some poser hotkeys mod. I guess I need to setup a sanitized development environment for Creation Kit and Skyrim for play-testing. Do people used Mod Organizer for development? I'm on linux as well, so running Creation Kit in a Win 8 virtual machine which has its problems.
invisible_rodent Posted June 2, 2023 Author Posted June 2, 2023 Hi all, I've improved this script as part of a work-in-progress giantess mod. Would anyone in the LL community mind checking it for any potential memory errors or other problems that could affect performance? It's been pretty robust in testing as there's some fail-safes to make sure it can't be called too often but I'm running it in a limited dev environment without many other LL mods. In the mod, this script is attached to potion alchemy effects, targeted damage spell effects, self healing spells, race power attacks, and NPC dialogue topic infos. The script only affects the player. The general idea for the mod is a male PC using SkyFem in a Skyrim world that is hostile to men, dependent on a female follower. Note that the mod is incompatible with SexLab because it affects PC height. The rest of the mod consists of conditional topic infos for NPCs/Followers based on PC height and a simple follower pay-for-size restoration mechanism. I would still like to add a mechanism to permanently reduce PC experience points and skills at max shrink. Also, switching over to using NetImmerse instead of GetScale() for changing height. Any thoughts or ideas are appreciated! Scriptname mgtsRandomEffect extends ActiveMagicEffect {[mgts] v2 : This script creates a magical effect that randomly shrinks or grows the user. Typically attached to potions in the rest of the mod.} Import Utility ; amount to grow/shrink the player, typical range is from ~ .1 to 2 ;float Property GrowthPerEffect Auto float Property MaxGrowth Auto float Property MaxShrink Auto Event OnEffectStart(Actor akTarget, Actor akCaster) float currentScale = akTarget.GetScale() ; # Setup maximum positive and negative values for grow/shrink range ;float randommax = Math.abs(GrowthPerEffect) ;float randommin = (GrowthPerEffect * -1) float GrowthPerEffect = Utility.RandomFloat(MaxShrink, MaxGrowth) ConsoleUtil.PrintMessage("GrowthPerEffect is " + GrowthPerEffect + " chosen between "+MaxShrink+" and "+MaxGrowth) GrowthPerEffect = Math.floor(GrowthPerEffect * 100) GrowthPerEffect = GrowthPerEffect / 100 ; ConsoleUtil.PrintMessage("GrowthPerEffect is now " + GrowthPerEffect) bool scalechanged = false float TargetScale = 0 ; # Main if..then to determine if values in range of min/max limits if (currentScale != 1.0 && GrowthPerEffect > 0) || (currentScale != 0.1 && GrowthPerEffect < 0) If (GrowthPerEffect > 0) TargetScale = 1.0 ElseIf (GrowthPerEffect < 0) TargetScale = 0.1 ; normal minimum is .1 EndIf float nextScale = currentScale + currentScale * GrowthPerEffect float loopScale = currentScale float changePerLoop = 0.01 ; TODO original was .003, seemed laggy int numberOfLoops = 0 float fovdefault = 80 ; default at size 1 float newfov = 0 akTarget = Game.GetPlayer() ; prevent NPCs from being affected? Wait(0.25) ; TODO balance float changedScale = akTarget.GetScale() ; # Only do anything if scale is not being affected from previous script run or other source if (currentScale == changedScale) ConsoleUtil.PrintMessage("[mgts] ======== Potion/Magic Effect Randomizer ========") ConsoleUtil.PrintMessage("[mgts] Player scale to be adjusted by " + GrowthPerEffect + " towards a maximum of " + TargetScale + " from "+ currentScale) ; ## Growing: Growth is positive and akTarget scale is still less than maximum if ((GrowthPerEffect > 0) && (changedScale < TargetScale)) If (nextScale > TargetScale) nextScale = TargetScale EndIf Debug.Notification("You feel your strength and powers return as you grow larger in size.") numberOfLoops = Math.Floor(Math.abs(currentScale - nextScale)/changePerLoop) ConsoleUtil.PrintMessage("[mgts] Grow from size " + currentScale + ", to " + nextScale) While (numberOfLoops > 0) loopScale+=changePerLoop akTarget.SetScale(loopScale) ; ### Decrease player FOV per loop newfov = (1 - loopScale) * 40 newfov = newfov + fovdefault ConsoleUtil.ExecuteCommand("fov " + newfov) numberOfLoops-=1 EndWhile scalechanged = true EndIf ; ## Shrinking : Growth is negative and akTarget scale is greater than maximum if ((GrowthPerEffect < 0) && (changedScale > TargetScale)) if (nextScale < TargetScale) nextScale = TargetScale EndIf Debug.Notification("You feel yourself getting weaker and more helpless as you shrink in size.") numberOfLoops = Math.Floor(Math.abs(currentScale - nextScale)/changePerLoop) ConsoleUtil.PrintMessage("[mgts] Shrink from from size " + currentScale + ", to " + nextScale) While (numberOfLoops > 0) loopScale-=changePerLoop akTarget.SetScale(loopScale) ; ### Increase player FOV per loop newfov = (1 - loopScale) * 40 newfov = newfov + fovdefault ConsoleUtil.ExecuteCommand("fov " + newfov) numberOfLoops-=1 EndWhile scalechanged = true EndIf ; ## Set player health based on percentage scale value of base health if (scalechanged == true) ;float PlayerScale = akTarget.GetScale() float PlayerScale = nextScale PlayerScale = Math.floor(PlayerScale * 100) ; trim big floats! http://www.gamesas.com/outputting-number-decimal-places-t290446.html PlayerScale = PlayerScale / 100 ; not sure why this can't go on the previous line ;ConsoleUtil.PrintMessage("[mgts] For player scale at " + PlayerScale) float shrunkHealth = PlayerScale * akTarget.GetBaseActorValue("Health") akTarget.ForceActorValue("Health", shrunkHealth) ConsoleUtil.PrintMessage(" => Health set to " + shrunkHealth) float shrunkMagicka = PlayerScale * akTarget.GetBaseActorValue("Magicka") akTarget.ForceActorValue("Magicka", shrunkMagicka) ConsoleUtil.PrintMessage(" => Magicka set to " + shrunkMagicka) float shrunkStamina = PlayerScale * akTarget.GetBaseActorValue("Stamina") akTarget.ForceActorValue("Stamina", shrunkStamina) ConsoleUtil.PrintMessage(" => Stamina set to " + shrunkStamina) float shrunkCarryWeight = PlayerScale * akTarget.GetBaseActorValue("CarryWeight") shrunkCarryWeight = shrunkCarryWeight - 20 ; TODO balance If (shrunkCarryWeight < 5) shrunkCarryWeight = 5 ; set minimum carry weight EndIf akTarget.ForceActorValue("CarryWeight", shrunkCarryWeight) ConsoleUtil.PrintMessage(" => Carry Weight set to " + shrunkCarryWeight) float shrunkattackdamage = PlayerScale * akTarget.GetBaseActorValue("AttackDamageMult") akTarget.ForceActorValue("AttackDamageMult", shrunkattackdamage) ConsoleUtil.PrintMessage(" => Attack Damage Multi set to " + shrunkattackdamage) ; Does mass do anything? float shrunkmass = PlayerScale * akTarget.GetBaseActorValue("Mass") akTarget.ForceActorValue("Mass", shrunkmass) ConsoleUtil.PrintMessage(" => Mass set to " + shrunkmass) ; sneak goes up as size goes down float shrunksneak = 1 - PlayerScale float playersneak = akTarget.GetBaseActorValue("Sneak") shrunksneak = (shrunksneak * 40) + playersneak ; TODO balance akTarget.ForceActorValue("Sneak", shrunksneak) ConsoleUtil.PrintMessage(" => Sneak set to " + shrunksneak) ConsoleUtil.PrintMessage(" => FOV set to " + newfov) If (GrowthPerEffect > 0 && nextScale >= TargetScale) ; test with or without floats akTarget.SetScale(TargetScale) ConsoleUtil.PrintMessage("[mgts] The maximum growth target at " + TargetScale + " has been reached. Resetting player attributes...") akTarget.ForceActorValue("Health", akTarget.GetBaseActorValue("Health") ) ConsoleUtil.PrintMessage(" => Health reset to " + akTarget.GetBaseActorValue("Health") ) akTarget.ForceActorValue("Magicka", akTarget.GetBaseActorValue("Magicka") ) ConsoleUtil.PrintMessage(" => Magicka reset to " + akTarget.GetBaseActorValue("Magicka") ) akTarget.ForceActorValue("Stamina", akTarget.GetBaseActorValue("Stamina") ) ConsoleUtil.PrintMessage(" => Stamina reset to " + akTarget.GetBaseActorValue("Stamina") ) akTarget.ForceActorValue("CarryWeight", akTarget.GetBaseActorValue("CarryWeight") ) ConsoleUtil.PrintMessage(" => Carry Weight reset to " + akTarget.GetBaseActorValue("CarryWeight") ) akTarget.ForceActorValue("AttackDamageMult", 1) ConsoleUtil.ExecuteCommand("fov " + fovdefault) ConsoleUtil.PrintMessage(" => FOV reset to " + fovdefault) EndIf Else ConsoleUtil.PrintMessage("[mgts] Randomized values are out of range. No effect.") EndIf Else ConsoleUtil.PrintMessage("[mgts] The mgtsShrink Script is still processing. No effect.") EndIf Else ConsoleUtil.PrintMessage("[mgts] Player is already at max/min of " + currentScale + " with an adjustment of " + GrowthPerEffect+ ".") EndIf EndEvent
traison Posted June 2, 2023 Posted June 2, 2023 I don't think I have time for any testing, however I'll throw you some hints in case you're not aware of them: Papyrus Profiler ReSaver I have never had a need for the profiler myself. Usually all I do is a sanity check in ReSaver and lots of gameplay testing. Ie. in ReSaver you'd be interested in how many script instances you have running (belonging to your mod) and whether they should be there or not. For instance, if you have a script distributed to nearby actors with a cloak (or SPID) and you know it should be a one-off thing (something that runs say only for less than a second) yet you see 10 instances of this in ReSaver - that could mean the script gets stuck on the actors.
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