Make children grow to teens in Skyrim
I have no idea if a mod is already doing the stuff, but I designed one from a talk with some friends.
The thing is: there are children in Skyrim. Good. How to make them grow to teens?
So the thing becomes: keep the original children, but find a way to change them in teens at some moment.
DISCLAIMER:
the provided code is functionnal
I tested with Camilia Valerius, who got a child, then a teen I named Cimalia Valerius
but the follwing code must be adapted to any need
1. TES5EDIT
I made a script for TES5Edit (you need MXPF). Its goal is to consolidate any interesting NPC as a teen version.
So, if one is unique, the related teen would not.
Teens are copies, not overrides, of their parent.
And their FormId is exactly the same as their parent, but in the load order of the resulting mod/patch.
The only limitation is FormId collision. However, I got none with Legendary Edition.
I stuck myself in the name "Teens.esp" to build the database of base actors.
First, load all the mods containing actors you think they should have a teen version.
Click on any of them and Apply Script > MakeTeens
Then, when this script asks for a file name, enter "Teens".
MakeTeens.pas
{ Make Teens } unit MakeTeens; uses 'lib\mxpf'; function Initialize: Integer; var i, fid, cur: integer; rec: IInterface; s: String; begin DefaultOptionsMXPF; InitializeMXPF; PatchFileByAuthor('TeenMaker'); SetExclusions(mxHardcodedDatFiles); AddMasterIfMissing(mxPatchFile, 'Update.esm'); cur := GetLoadOrder(mxPatchFile); cur := cur shl 24; LoadRecords('NPC_'); for i := MaxRecordIndex downto 0 do begin rec := GetRecord(i); s := EditorID(LinksTo(ElementByPath(rec, 'RNAM'))); if (s = 'ImperialRaceChild') or (s = 'RedguardRaceChild') or (S ='NordRaceChild') or (s = 'BretonRaceChild') or (s = 'BretonRaceChildVampire') or (s = 'DefaultRace') then RemoveRecord(i) else if (geev(rec, 'DATA/Flags/Child') = '1') or (geev(rec, 'ACBS/Flags/Invulnerable') = '1') or (geev(rec, 'ACBS/Flags/Respawn') = '1') then RemoveRecord(i) else if (geev(rec, 'FULL - Name') = '') or (geev(rec, 'FULL - Name') = 'Prisoner') then RemoveRecord(i) else CopyRecordToPatch(i) end; // we remove the flags, the packages, the factions, the inventory, the scripts // we change level to 1 // we keep the outfit for i := 0 to MaxPatchRecordIndex do begin rec := GetPatchRecord(i); fid := GetLoadOrderFormID(rec); fid := (fid and $00FFFFFF) + cur; SetLoadOrderFormID(rec, fid); s := geev(rec, 'EDID - Editor ID'); seev(rec, 'EDID - Editor ID', '_z_' + s); s := geev(rec, 'FULL - Name'); seev(rec, 'FULL - Name', s + ' Teen'); if (geev(rec, 'ACBS/Flags/Unique') = '1') then seev(rec, 'ACBS/Flags/Unique', '0'); if (geev(rec, 'ACBS/Flags/Essential') = '1') then seev(rec, 'ACBS/Flags/Essential', '0'); if (geev(rec, 'ACBS/Flags/Protected') = '1') then seev(rec, 'ACBS/Flags/Protected', '0'); if (geev(rec, 'ACBS/Flags/PC Level Mult') = '1') then seev(rec, 'ACBS/Flags/PC Level Mult', '0'); seev(rec, 'ACBS/Level', '1'); if ElementExists(rec, 'VMAD - Virtual Machine Adapter') then RemoveElement(rec, 'VMAD - Virtual Machine Adapter'); if ElementExists(rec, 'SHRT - Short Name') then RemoveElement(rec, 'SHRT - Short Name'); if ElementExists(rec, 'Factions') then RemoveElement(rec, 'Factions'); if ElementExists(rec, 'Items') then RemoveElement(rec, 'Items'); if ElementExists(rec, 'Packages') then RemoveElement(rec, 'Packages'); end; PrintMXPFReport; FinalizeMXPF; end; end.
Now we have a mod, named "Teens.esp", full of base actors, beginning with formID "_z_".
2. Creation Kit on Teens.esp
Now the boring part. Load "Teens.esp" in CK. Go to Actors.
The first ones are teens (they all begin with "_z_"). Select all the teens, and only them.
Press Ctrl+F4 to export all facegen data. This should eliminate the gray face bug - but it will take a while.
Close CK once done, or make an archive from all facegen (note that such archive is valid, if limited to LE DLCs).
> however, Cimalia Valerius had the gray face, despite the file present.
> it may have been because I was there along the whole process, I don't know
=> EDIT: this "bug" was quite interesting. It was coming from... Ordenator, which repacked the vanilla BSAs.
So to make this export of facegeometry and facegendata working as expected, do not optimize your vanilla bsas with Ordenator.
3. Scripting things.
You can do the big-big part or just focus on the grow-to-teen process.
The big-big part is:
- make the aliases arrays (or use an auto register), for example 64 children and 64 moms
> a big mod should have 128 male children and 128 female ones, then 2*128 moms
- make all sets of hairs/eyes for children + base actors, anything you need for birth
> Serana's boy and girl will require a bit of work...
- catch a birth signal by mod event (from a pregnancy mod) and spawn the child, setting all the head parts (similar as OnTeenAge, below)
- assign the child into an alias
- make sure you catch OnLoad in the child alias to refresh head parts when the child is loaded
- make a player alias to re-register your events (birth and becoming teen)
- create a package template with two sequential procedures: follow mom and sandbox
> with conditions: inSameCell packageData->mom, don't forget to make the child run while following
- create as many packages as the number of children aliases
- create as many mom aliases as the number of children aliases
- for package000, assign it to child alias000 and make it targetting to mom alias000
With everything above, you have a complete children mod and it's not so hard to make.
Now to the grow-to-teen process.
The design is to place a child into an alias.
Once in the alias, make it updating the scale from 0.5 to 0.8 (the latter being the scale of Skyrim's children).
And once the scale is 0.8, transform the child into a teen.
Then, make the teen grow, from 0.8 to 1.0.
I set 50 days for each grow.
Now, to the code.
The Kid Alias (interessing part)
-------------
;--- ONUPDATEGAMETIME Event OnUpdateGameTime() ; fBirthday is the decimal game day when born ; fSpeedUp is a way to speed the grow ; IsTeen is a boolean ; scale for child: ; 0.50 > 0.80, in 50 days ; 0.625 > 1.0, 0.375 of range ; scale for teen: ; 0.80 > 1.00, in 50 days (same for range) Float myGrow = Utility.GetCurrentGameTime() - fBirthDay + fSpeedUp ; expressed in days myGrow /= 50.0 If(bIsTeen) If(myGrow >= 1.0) me.SetScale(1.0) Return ; adult size, no update needed anymore Else myGrow *= 0.2 myGrow += 0.8 me.SetScale(myGrow) EndIf Else If(myGrow >= 1.0) ; become teen, with a grow for 50 another days (or remain a child) me.SetScale(1.0) Int handle = ModEvent.Create("KidooTeenAge") ; <= that's it ModEvent.PushInt(handle, slot) ModEvent.Send(handle) Return Else myGrow *= 0.375 myGrow += 0.625 me.SetScale(myGrow) EndIf EndIf RegisterForSingleUpdateGameTime(10.0) EndEvent
The code above works and has been tested.
If a child cannot grow to teen (in the next script) then he/she will be stuck into the alias, with max children size. It's okay.
The Mod Event
-------------
;--- ONTEENAGE Event OnTeenAge(Int i) Log("receiving teen age signal for slot "+i) KidAlias refAlias = vChildren[i] As KidAlias ; vChildren is a referencealias[] where all child/teen aliases auto registered on init Actor refChild = refAlias.me ; property "Actor me" on alias ; - do we have a base actor? Int refID Actor myMother = refAlias.myMother ; property "Actor myMother" on alias Actor myFather = refAlias.myFather ; property "Actor myFather" on alias Int childSex = refChild.GetLeveledActorBase().GetSex() If(childSex == 1) refID = myMother.GetLeveledActorBase().GetFormID() Else refID = myFather.GetLeveledActorBase().GetFormID() EndIf ; - find a match in the database refID = Math.LogicalAnd(refID, 0x00FFFFFF) ActorBase refBase = Game.GetFormFromFile(refID, "Teens.esp") As ActorBase ; <= here If(!refBase) ; <= nothing was found, so the child will remain a child eternally Log("no teen base found") Return EndIf Log("teen base found: " + refBase.GetName()) ; - store all data Int iHair = refAlias.iHair ; this is the index in the formlist of children hairs Int iEyes = refAlias.iEyes ; this is the index in the formlist of children eyes (for non human) Float fBirthDay = Utility.GetCurrentGameTime() ; reinit grow for 50 new days ColorForm refHairColor = refAlias.refHairColor ; the ColorForm used as a child ; - spawn the teen refBase.SetHairColor(refHairColor) Actor refTeen = refChild.PlaceActorAtMe(refBase) refTeen.SetDisplayName(refChild.GetDisplayName(), False) ; transfer the name ; - assign new hair, eyes with respect to those previously chosen for childhood ; the trick is: the formlist for children and those for teens have the same internal order ; for example orc ice yes is at index 4 for child, 4 for teen Race myRace = refTeen.GetRace() FormList hairList FormList eyesList If(childSex) If(myRace == ArgonianRace) hairList = _HairTeenArgonianFemale eyesList = _EyesTeenArgonian ElseIf(myRace == BretonRace || myRace == ImperialRace || myRace == NordRace || myRace == RedguardRace) hairList = _HairTeenHumanFemale eyesList = _EyesTeenHuman ; there s only one eyes for human children so we have to choose randonly here iEyes = Utility.RandomInt(0, eyesList.GetSize() - 2) ; - 2 because I left inside, at the last index, vampire eyes (not used) ElseIf(myRace == DarkElfRace || myRace == HighElfRace || myRace == WoodElfRace) If(myRace == DarkElfRace) hairList = _HairTeenDarkElfFemale eyesList = _EyesTeenDarkElf ElseIf(myRace == HighElfRace) eyesList = _EyesTeenHighElf hairList = _HairTeenElfFemale Else eyesList = _EyesTeenWoodElf hairList = _HairTeenElfFemale EndIf ElseIf(myRace == KhajiitRace) hairList = _HairTeenKhajiitFemale eyesList = _EyesTeenKhajiit Else hairList = _HairTeenOrcFemale eyesList = _EyesTeenOrc EndIf Else If(myRace == ArgonianRace) hairList = _HairTeenArgonianMale eyesList = _EyesTeenArgonian ElseIf(myRace == BretonRace || myRace == ImperialRace || myRace == NordRace || myRace == RedguardRace) hairList = _HairTeenHumanMale eyesList = _EyesTeenHuman iEyes = Utility.RandomInt(0, eyesList.GetSize() - 2) ElseIf(myRace == DarkElfRace || myRace == HighElfRace || myRace == WoodElfRace) If(myRace == DarkElfRace) hairList = _HairTeenDarkElfMale eyesList = _EyesTeenDarkElf ElseIf(myRace == HighElfRace) eyesList = _EyesTeenHighElf hairList = _HairTeenElfMale Else eyesList = _EyesTeenWoodElf hairList = _HairTeenElfMale EndIf ElseIf(myRace == KhajiitRace) hairList = _HairTeenKhajiitMale eyesList = _EyesTeenKhajiit Else hairList = _HairTeenOrcMale eyesList = _EyesTeenOrc EndIf EndI ; as you see above, there s a lot of formlists to make... ; - assign the new hair style and eyes HeadPart refHair = hairList.GetAt(iHair) As HeadPart HeadPart refEyes = eyesList.GetAt(iEyes) As HeadPart refTeen.ChangeHeadPart(refHair) refTeen.ChangeHeadPart(refEyes) refTeen.QueueNiNodeUpdate() ; - prepare refTeen.SetScale(0.8) ; the initial scale of a teen refTeen.AddToFaction(KidFaction) ; something to identify them quickly ; - delete now the child Log("deleting child form") refAlias.Cleanup() ; mostly clear() refChild.Delete() ; - assign the teen refAlias.ForceRefTo(refTeen) refAlias.Initialize(myMother, myFather, fBirthDay, refHairColor, hairList, eyesList, iHair, iEyes, i, bIAmTeen = True) ; i was the slot parameter of this event Debug.Notification(refTeen.GetDisplayName() + " has become a teen") EndEvent
For existing children, you have to place them into an alias, with their mom and dad, so it may be a bit tricky to retrieve information.
But hey, after a while, Whiterun children will be all teens!
Now the little story: I made this log entry because one of my friends doubted it was possible to grow children to teens in Skyrim.
I'll be happy to show the result.
Feel free to plunder the code if you are interested.
0 Comments
Recommended Comments
There are no comments to display.