spicydoritos Posted April 19, 2021 Posted April 19, 2021 (edited) Sex Education View File Earn configurable experience points for having sex (through AAF). Why would I want this? Because your character has a lot of sex and you want to level up faster? At the very least, you want a little compensation for time spent on your knees. Personally I think this mod is best for play styles that are lighter on combat and heavier on sex. At the extreme end, that style might even stem from a prostitution scenario mod like Hardship, where XP is otherwise pretty hard to come by. Furthermore, not only is the bonus XP useful as an indirect source of self esteem for Sex Attributes, but SexEd also now includes the option to directly grant self esteem from consensual sex! Isn't there another mod with a similar effect? Lucrative Augments includes one augment that grants experience points for sex. Sex Education simply provides a more configurable version of this effect in a stand-alone package. Features and Other Gritty Details This mod works for any player gender and AAF animation, except for animations tagged hugging, dancing, or pose (kissing tags may be excluded in the MCM). Player can be pitching, catching, licking, spanking, or anything in between; XP will be granted as long as you are an actor in the animation. However, masturbation gives no XP. You only learn with at least one partner! Sex Education includes the following MCM settings: Spoiler Mod On/Off: Master switch that will disable sex-based XP entirely. Base XP Per Partner: Amount of baseline experience granted by each partner in an animation. For example, an animation with yourself and three partners will grant three helpings of baseline experience (one per partner) at the end of the animation. Any modifiers from Advanced Mode settings will apply to this value. Sex Ed Mode: Two modes are available. Simple Mode is very straightforward, and grants baseline XP for each partner, every time, with no other modifiers. Advanced Mode will adjust earned XP based on the settings found under Advanced Mode Settings. Allow XP For Kissing Tag: Some sex animations include kissing tag too. Player may choose whether to exclude animations with this tag from granting experience. Advanced Mode Settings XP Decay For Similar Partner: As long as Advanced Mode is active, Sex Education will dynamically track the number of sexual encounters with each combination of race type and gender (i.e. human male, ghoul female, supermutant male, etc). Each encounter with the same race/gender combination will provide slightly less XP based on this setting. This XP reduction is additive, not multiplicative. For example: using the default setting of 2%, after three encounters with a human male, the XP for the next encounter with a human male would be reduced by 6% (2+2+2). Please note that the decay will eventually reach 100% reduction, granting zero XP unless you use a Minimum XP Per Partner setting greater than zero. Lastly, none of the races are hard coded; Sex Education will properly track every race type separately, including any DLC creatures or custom races used in animations. Group Sex XP Modifier: Is your character the type who would learn more from a foursome than from banging three partners one by one? Or would they be too distracted and maybe learn a little less? With this setting, every simultaneous partner beyond the first will increase (or decrease) the total XP for the sexual encounter by your chosen amount. Aggressive Sex XP%: Animations tagged “aggressive” can have their total XP adjusted here. Default setting is 50% as much XP as you'd earn for a non-aggressive animation. This default assumes you're the unfortunate recipient of aggressive sex, meaning you're less like a participant and more like a half-willing communal repository. However, if your character is the type to initiate aggressive sex on some poor captive, or if you feel your character could stand to “learn their place” in a more literal sense, this setting can be turned all the way up to 200%. Alternatively, this can be set to negative values if you prefer to actually lose experience. Allow XP Loss Below Current Level: This setting can prevent XP falling below the current level minimum. Please note that you will not actually lose levels or perks, nor drop below 1 XP, regardless of settings. Prevent/Allow FO4 XP Restoration: If player experience falls below their current level, FO4's engine will try to restore the difference in XP when you load a save. This MCM option can either allow or prevent that default behavior. The “Prevent” option means SexEd will track and store your experience whenever you open the menu. If it detects a higher experience value upon loading a save, SexEd will restore the last saved value. Fellow Pitchers XP%: Players in the pitcher role (e.g. giver, fucker, penis/strapon donor) will always earn full XP from the catcher (e.g. receiver, fuckee, penis/strapon storage unit). If multiple pitchers are utilizing the same catcher in an animation, what percent of XP do you want to earn from the additional pitchers? I recommend leaving this value at 100% if you are usually the catcher in animations; that way you won't lose XP for edge cases where a group animation has a more ambiguous pitcher/catcher dynamic. Minimum XP Per Partner: With the various Advanced Mode settings above, it's possible to reach a point where an encounter would grant zero XP. This setting ensures that you'll always earn at least some XP per partner, regardless of any other factors. Mod Integration Settings AAF Spectators XP On/Off: If AAF Spectators is installed, you can gain experience for being watched during sex. Any observers called by AAF Spectators will grant XP. This even works in simple mode or solo animations. Now you can level up by masturbating for a crowd! Base XP Per Observer: Amount of baseline experience granted by each AAF Spectators observer. XP% Reduction Per Extra Observer: Percent to reduce baseline experience earned from each AAF Spectators observer beyond the first. Values are additive. Apply Aggressive Tag XP Modifier: If Advanced Mode is active, experience earned from AAF Spectators observers can be modified by the “Aggressive Sex XP%” setting. Treat Raped/Rapist Flags as Aggressive: Various popular mods (e.g. Violate, SEU, MCG, Sexual Harassment, and others) include community standardized indicators when initiating AAF scenes. Player may choose to treat PlayerRaped and PlayerRapist flagged scenes as aggressive, regardless of animation tags. Treat Consensual Flag as Non-Aggressive: Similar to the previous MCM option, player may choose to treat Consensual flagged scenes as non-aggressive, regardless of animation tags. Self Esteem for Consensual Sex: If you have Sex Attributes installed, SexEd can grant a small amount of self esteem every time you have consensual sex. Hardcore XP Mode Hardcore XP Mode On/Off: When activated, any XP gained (outside of SexEd itself) can be reduced to nearly zero. Hardcore XP% Earned: Adjusts the percentage of XP gained from sources other than SexEd. Requirements -Vanilla Fallout 4 (no DLCs needed) -AAF with working animations. Sex Education contains no animations of its own. -MCM, for mod settings and overall function. -Tags for your animations (soft requirement). The main effect of missing tags will be a non-working Aggressive Sex XP% setting. I recommend installing AAF Themes. Alternatively, you can use animations that are tagged by the author, like those made by SavageCabbage (1.2.4+) and BP70 (2.7+). Certain patches add, revise, or substitute their own tags as well. -Optional Requirement: AAF Spectators. If installed, integration settings can be used to earn XP when NPCs watch you have sex. -Optional Requirement: Sex Attributes. If installed, integration settings can be used to earn self esteem after consensual sex. Known Issues -Gen 3 synths all use human race, so they'll be tracked as one race type instead of separately. Arguably, that's how they should be treated anyway... -If a player animation starts while a separate NPC animation is in progress, player may receive XP credit for the NPC's spectators (when using AAF Spectators integration). My Thanks To -kinggath, who I cannot praise enough for his incredibly detailed and informative “Bethesda Mod School” series on YouTube. Don't stab around in the dark, go learn from his hundreds (thousands?) of hours of modding experience. -EgoBallistic, twistedtrebla, and Nebuchadnezzer2, all of whose excellent scripting I studied, scoured, pondered, and otherwise picked apart to figure out how to make my mods hum. -Double secret bonus thanks to Nebuchadnezzer2, whose top notch Cum Overlays mod had such a good AAF listening structure that I tried my best not to just plagiarize it directly (and partially succeeded) ?. -Special thanks to the AAF Help discord for sorting me out back when I was a wide-eyed newbie, to whom the secrets of mod-making were akin to sorcery. -Honorable mention to Saya_Scarlett for the often thankless work of maintaining the definitive AAF Fucking Manual. Permissions Feel free to re-post, re-host, patch, translate, and/or incorporate into your own mods as long as the result is freely available and not paywalled. Just give credit back to me and the original LL post. No support offered for sites outside of LL. Submitter spicydoritos Submitted 04/19/2021 Category Sex Effects Requirements AAF, Animations, MCM, Themes (soft req.), AAF Spectators (optional) Edited October 18, 2021 by spicydoritos Updated description for 1.21 8
justforcensor Posted April 19, 2021 Posted April 19, 2021 Nice job! I like where adult fo4 modding going. Sooner or later we'll have complete different gameplay style for replay.
Farsh-nuke Posted April 19, 2021 Posted April 19, 2021 Considering there are mods that require perks a player might not normally go for something like this makes sense. The sex game player can earn the exp for the perks it needs to max out.
Undertower Posted April 19, 2021 Posted April 19, 2021 Seems interesting, especially the mentioned combo with Sex Attributes.
Nebuchadnezzer2 Posted April 19, 2021 Posted April 19, 2021 9 hours ago, spicydoritos said: Known Issues -Because Sex Education tracks races for XP Decay dynamically, we're at the mercy of Bethesda's race types. For example, there are three races of vanilla dogs (ViciousDogRace, RaiderDogRace, and DogmeatRace) that will be tracked separately when arguably they should use a single entry. Same issue with Gen 2 synths and Valentine, who inexplicably has his own race. Plus, of course, Gen 3 synths all use human race, so they'll be tracked as the same race type instead of separately. I think it's a worthwhile tradeoff for the flexibility of handling any vanilla, DLC, or custom race type. -Group sex XP makes more sense as the catcher than as a pitcher. Regardless of your role in an animation, you'll earn the same XP. So take, for example, an all-human MMMF animation with your character as the female center of attention. You would earn 3x baseline XP before multipliers and get statistics incremented for 3 human male partners. On the other hand, if your character was one of those human males, you would still earn 3x baseline XP while also having your statistics incremented by 2 human males and 1 human female. Sex Education simply cannot differentiate “having sex with someone” from “sharing a partner”. The former, I think, could be handled somewhat similar to Violate's handling of races, with an array of similar races clustered together, and treated as one. I think another way around requiring DLC in the plugin, would be to wait for `OnAAFReady`, then use `GetActorTypeList`, and go through that array to fill out one or more lists of similar races (Dogs being the most obvious example). Would have to go digging more, but the method you went with is, as you say, a worthwhile tradeoff, I think. The latter, you can set up separate XP rewards for 'participating' vs 'receiving', and check the actors for the player. Should only need to have stored the player reference as an `Actor Property`, in much the same way I did `OverlayRef`, and a short `While` loop to check through the actor array, with an if/else to use one, or the other. Actually, looking at the script, with some more added to the `If actors.find(PlayerRef)`, you could do that. For example; Spoiler Instead of If actors.find(PlayerRef) < 0 DTrace("Player was not involved in this animation.") Return EndIf int actorPlayer = actors.find(PlayerRef) If actorPlayer < 0 DTrace("Player was not involved in this animation.") Return EndIf If actorPlayer == 0 DTrace("Player was 'receiver' in this animation.") ElseIf actorPlayer > 0 DTrace("Player was 'giver' in this animation.") EndIf Either way, nicely done. I remember seeing someone mention something about getting XP from AAF scenes recently, so good timing, too. Will have to go on the list for if when I finally update and adjust my install to use it again. Congrats on releasing it 1
spicydoritos Posted April 19, 2021 Author Posted April 19, 2021 45 minutes ago, Nebuchadnezzer2 said: The former, I think, could be handled somewhat similar to Violate's handling of races, with an array of similar races clustered together, and treated as one. I think another way around requiring DLC in the plugin, would be to wait for `OnAAFReady`, then use `GetActorTypeList`, and go through that array to fill out one or more lists of similar races (Dogs being the most obvious example). Would have to go digging more, but the method you went with is, as you say, a worthwhile tradeoff, I think. The latter, you can set up separate XP rewards for 'participating' vs 'receiving', and check the actors for the player. Should only need to have stored the player reference as an `Actor Property`, in much the same way I did `OverlayRef`, and a short `While` loop to check through the actor array, with an if/else to use one, or the other. Actually, looking at the script, with some more added to the `If actors.find(PlayerRef)`, you could do that. For example; Hide contents Instead of If actors.find(PlayerRef) < 0 DTrace("Player was not involved in this animation.") Return EndIf int actorPlayer = actors.find(PlayerRef) If actorPlayer < 0 DTrace("Player was not involved in this animation.") Return EndIf If actorPlayer == 0 DTrace("Player was 'receiver' in this animation.") ElseIf actorPlayer > 0 DTrace("Player was 'giver' in this animation.") EndIf Either way, nicely done. I remember seeing someone mention something about getting XP from AAF scenes recently, so good timing, too. Will have to go on the list for if when I finally update and adjust my install to use it again. Congrats on releasing it Yeah I see what you're talking about with Violate's race arrays. I waffled back and forth on whether to include the race types as properties for some kind of grouping (and I like the arrays better than the scripted groupings I might have done otherwise). In the end I decided I'd rather have total flexibility of race types even if the player gets a bit more XP than expected, occasionally. Race tracking really only matters for the XP decay setting anyway. On the DLC note, if you know of a way to reference those creatures without requiring the plugin, that'd be quite useful! Granted, I think it's silly for anyone to not have the DLC at this point in the game's life... I like that technique to differentiate pitchers and catchers. That might be worth adding, along with an MCM option to let the player choose whether their position influences XP gained. It'd work very well for animations with one catcher and multiple pitchers. There are a couple outlier cases where I think it might break down, notably the Love Triangle and Lesbian Threesome animations by BP70. Player *might* end up as actor[0] in those animations or not, while still being either giver, receiver, or both. And thank you! The script structure of Cum Overlays was a big help ("listen for an AAF animation and do X").
Nebuchadnezzer2 Posted April 19, 2021 Posted April 19, 2021 13 minutes ago, spicydoritos said: In the end I decided I'd rather have total flexibility of race types even if the player gets a bit more XP than expected, occasionally. Race tracking really only matters for the XP decay setting anyway. That was also part of why I thought "well, it's probably not worth the effort to combine 'em", especially with the options you've got already for customising things. 14 minutes ago, spicydoritos said: On the DLC note, if you know of a way to reference those creatures without requiring the plugin, that'd be quite useful! Granted, I think it's silly for anyone to not have the DLC at this point in the game's life... Agreed, with how cheap it gets, now. Can set up a similar method to getting the AAF API and checking MCM's installed. There's `IsPluginInstalled`; Spoiler ; Is the specified plugin (esm or esp) installed? bool Function IsPluginInstalled(string asName) native global Vanilla, I believe, but extended with F4SE. Just need the plugin name to send it. Can check if the relevant DLC's are installed in an `If`, and if so, go ahead and use `Game.GetFormFromFile` to fill some blank `Keyword Property`'s. 32 minutes ago, spicydoritos said: There are a couple outlier cases where I think it might break down, notably the Love Triangle and Lesbian Threesome animations by BP70. Player *might* end up as actor[0] in those animations or not, while still being either giver, receiver, or both. It's assumed that actor[0] is (always) the 'receiving' actor, with the rest as 'giving', so most of the edge cases would be shit like the Love Triangle one, where it's a bit more ambiguous/there's technically more than one 'receiver', but for the most part, it'll be fine, and a little more XP here or there from something like that won't make much of a difference, in the end. 39 minutes ago, spicydoritos said: And thank you! The script structure of Cum Overlays was a big help ("listen for an AAF animation and do X"). I did see some familiar segments, but that's what the sources were for, and I'm glad it helped! Borrowed some ideas from Ego's scripts for SEU/Violate, and beat my head on a few walls here and there in trying to think through how to get something working, and buried my head in other scripts (F4SE, MCM, Vanilla game ones, etc.) but it certainly paid off.
spicydoritos Posted April 19, 2021 Author Posted April 19, 2021 38 minutes ago, Nebuchadnezzer2 said: It's assumed that actor[0] is (always) the 'receiving' actor, with the rest as 'giving', so most of the edge cases would be shit like the Love Triangle one, where it's a bit more ambiguous/there's technically more than one 'receiver', but for the most part, it'll be fine, and a little more XP here or there from something like that won't make much of a difference, in the end. Yeah I suppose it's not the worst thing. You'd also potentially encounter *less* XP if you're unexpectedly cast as a giver when you think you're a receiver. However, those cases would be rare I think. Rare enough to be worth the effort of parsing the roles out, dropping an MCM setting in place, and letting the player choose their poison. Quote I did see some familiar segments, but that's what the sources were for, and I'm glad it helped! Borrowed some ideas from Ego's scripts for SEU/Violate, and beat my head on a few walls here and there in trying to think through how to get something working, and buried my head in other scripts (F4SE, MCM, Vanilla game ones, etc.) but it certainly paid off. I tried my best not to plagiarize too badly (except from Ego, whose mods were open season at the time of writing ?). Nonetheless I'm happy to credit your work more directly if it's a little too familiar! Edit: Additional credit has been applied and you can't stop me.
Nebuchadnezzer2 Posted April 19, 2021 Posted April 19, 2021 1 minute ago, spicydoritos said: I tried my best not to plagiarize too badly (except from Ego, whose mods were open season at the time of writing ?). Nonetheless I'm happy to credit your work more directly if it's a little too familiar! Totally didn't do the same before he had a hiatus Reminds me, will have to remember to go look at Kinggath's youtube stuff, too... And no, you're fine. A mention like you've got there's plenty, to me. Almost a year, and 27k downloads later (jesus, I didn't realise it hit that many), I still get a kick outta spottin my mod in load orders (usually when helpin out in the AAF Discord). Just glad it's been useful and isn't bug riddled Congrats again
kukoricamorzsa Posted April 20, 2021 Posted April 20, 2021 Education.... Really? For some reason I don't understand why or why is education a reward? Rewarding an activity like killing or ... plugging is one of the proponents of the game ... so it’s a player development tool. So "development" would be a good term ... "Sex Developer" or "Sex promote development" or anything like that... Here, think about this ... Valamiért nem értem mitől vagy miért "nevelés" a jutalmazás? A szexuális tevékenység díjazása... ? A magyar az oktatás és a nevelés kifejezést használja ehhez a szóhoz. Az idomítás csak időszakos használatú de az se illik ide. Szerintem... nem. A tevékenység jutalmazása mint a gyilkolás vagy a ... dugás a játék egyik előrevivő ... tehát a játékost fejlesztő eszköz. Így a "fejlesztés" lenne a jó kifejezés... "Szexfejlesztő", "Szexfejlődés segítése" vagy bármi hasonló az... realitás. Tessék ezen elgondolkodni ...
spicydoritos Posted April 20, 2021 Author Posted April 20, 2021 3 hours ago, kukoricamorzsa said: Education.... Really? For some reason I don't understand why or why is education a reward? The mod title is a reference to classes in the US where they teach students about the basics of human sexuality (anatomy, reproduction, safe sex, birth control, etc). Such classes are often called "Sex Education". 1
Farsh-nuke Posted April 20, 2021 Posted April 20, 2021 35 minutes ago, spicydoritos said: The mod title is a reference to classes in the US where they teach students about the basics of human sexuality (anatomy, reproduction, safe sex, birth control, etc). Such classes are often called "Sex Education". I have a friend who writes erotica, her stories are a slew of American pop culture puns so I get it.
kukoricamorzsa Posted April 20, 2021 Posted April 20, 2021 On 4/20/2021 at 4:54 PM, spicydoritos said: The mod title Hahaha... but this is game! And if I'm not wrong at school they don't give a reward because ... students fuck ... hahaha... One of the fucking games rewarding supplement. It doesn't really have much to do with the "to idea or (to conception)"! The whole supply is different from the "imagination" just described. The more you fuck, the more the reward. Would that be the essence of sex education and upbringing? Or is this the basic idea of a street girl education !? hahaha.... Maybe the shotgun ... should not be caught the other way around when shooting! Still, it's very good ... as it is. If so, what about? What should have been accepted should be accepted. Then in another round, in a more thoughtful way, you will PROHIBIT the fucking show instead of rewarding it and then the current idea will also take place. But then they won't be used as much as they are now ... Nerm deduction ... reward. You do not get it, right? hahaha... _______________________________ De ez egy játék! A baszós játék egyik jutalmazó kiegészítője. Valójában nem sok köze van az "elképzeléshez" ! és ha nem tévedek az iskolában nem adnak jutalmat azért mert a... diákok dugnak... Az egész kínálat másban mutatkozik mint a most leírt "képzelgés". ** A mód azért "fizet" (jutalmaz)... hogy basszál. Minél többet baszol, annál több a jutalom. A szexuális nevelés alapja... nem az, hogy fűvel-fával dugj. Az a bizonyos oktatási forma a társadalmi elvek szerinti elvárásokat próbálja érvényesíteni. Tehát a visszafogást és a felelősségtejes viselkedést tanítja. Elsődlegesen azt, hogy a kutya, ló, gorilla... nem a szexuális igény kielégítésére való. Minden bizonnyal a teljes elgondolást újra kellene definiálni.... de akkor ki a fenét érdekelne... ITT!? Mindazonáltal nagyon jó úgy... ahogyan van. Ha így sikerül, na és? El kellene fogadni annak ami lett. Majd egy másik körben átgondoltabb módon TILTANI FOGJA a baszóműsort jutalmazás helyett és akkor a mostani elképzelés is helyet nyer. De akkor közel sem fogják annyian használni, mint most... A jutalom... az jutalom. Az iskolai oktatás ( - a program mód szerint - egy kurvaképző) azonban olyan, amilyen.
spicydoritos Posted April 20, 2021 Author Posted April 20, 2021 New version up: 1.05: -New MCM Option: Fellow Pitchers XP%. Allows player in the "giver" position to set a lower XP value earned from the other "givers" in a group animation. (thanks Nebuchadnezzer2!) 1
kakkaohjus Posted April 21, 2021 Posted April 21, 2021 How hard would it be to hook in something that gives you bonus xp if you had people watching?
spicydoritos Posted April 21, 2021 Author Posted April 21, 2021 3 minutes ago, kakkaohjus said: How hard would it be to hook in something that gives you bonus xp if you had people watching? Hmm... looks like Spectators groups all its observers into a single RefCollectionAlias. I could probably pass that over to Sex Ed during the animation somehow. I really like this idea. 1
spicydoritos Posted April 22, 2021 Author Posted April 22, 2021 Version 1.10 is up! -Includes optional integration with AAF Spectators. If that mod is installed, you can earn XP when NPCs watch you get busy. (thanks for the great suggestion, kakkaohjus!) Edit: Posted a 1.10a version to fix an issue I found with counting of spectators. 2
Operand Posted April 22, 2021 Posted April 22, 2021 On 4/19/2021 at 9:20 AM, spicydoritos said: Known Issues -Because Sex Education tracks races for XP Decay dynamically, we're at the mercy of Bethesda's race types. For example, there are three races of vanilla dogs (ViciousDogRace, RaiderDogRace, and DogmeatRace) that will be tracked separately when arguably they should use a single entry. Same issue with Gen 2 synths and Valentine, who inexplicably has his own race. Plus, of course, Gen 3 synths all use human race, so they'll be tracked as the same race type instead of separately. I think it's a worthwhile tradeoff for the flexibility of handling any vanilla, DLC, or custom race type. Here, from the work on the mod I'm currently doing: Spoiler Race Function GetNativeHumanRace(string callerId = "Default") global return Game.GetFormFromFile(0x00013746, "Fallout4.esm") as Race EndFunction Race Function GetNativeHumanChildRace(string callerId = "Default") global return Game.GetFormFromFile(0x0011D83F, "Fallout4.esm") as Race EndFunction Race Function GetNativeFeralGhoulRace(string callerId = "Default") global return Game.GetFormFromFile(0x0006B4EC, "Fallout4.esm") as Race EndFunction Race Function GetNativeGhoulRace(string callerId = "Default") global return Game.GetFormFromFile(0x000EAFB6, "Fallout4.esm") as Race EndFunction Race Function GetNativeGlowingOneRace(string callerId = "Default") global return Game.GetFormFromFile(0x000A96BF, "Fallout4.esm") as Race EndFunction Race Function GetNativeGhoulChildRace(string callerId = "Default") global return Game.GetFormFromFile(0x0011EB96, "Fallout4.esm") as Race EndFunction Race Function GetNativeSuperMutantRace(string callerId = "Default") global return Game.GetFormFromFile(0x0001A009, "Fallout4.esm") as Race EndFunction Race Function GetNativeMutantHoundRace(string callerId = "Default") global return Game.GetFormFromFile(0x00090C33, "Fallout4.esm") as Race EndFunction Race Function GetNativeBehemothRace(string callerId = "Default") global return Game.GetFormFromFile(0x000BB7D9, "Fallout4.esm") as Race EndFunction Race Function GetNativeGen1SynthRace(string callerId = "Default") global return Game.GetFormFromFile(0x000E8D09, "Fallout4.esm") as Race EndFunction Race Function GetNativeGen2SynthRace(string callerId = "Default") global return Game.GetFormFromFile(0x0010BD65, "Fallout4.esm") as Race EndFunction Race Function GetNativeGen2P5SynthRace(string callerId = "Default") global return Game.GetFormFromFile(0x002261A4, "Fallout4.esm") as Race EndFunction Race Function GetNativeMrHandyRace(string callerId = "Default") global return Game.GetFormFromFile(0x000359F4, "Fallout4.esm") as Race EndFunction Race Function GetNativeEyeBotRace(string callerId = "Default") global return Game.GetFormFromFile(0x000A563A, "Fallout4.esm") as Race EndFunction Race Function GetNativeSentryBotRace(string callerId = "Default") global return Game.GetFormFromFile(0x000AE0B7, "Fallout4.esm") as Race EndFunction Race Function GetNativeTripodTurretRace(string callerId = "Default") global return Game.GetFormFromFile(0x000B1F08, "Fallout4.esm") as Race EndFunction Race Function GetNativeBubbleTurretRace(string callerId = "Default") global return Game.GetFormFromFile(0x000B28D3, "Fallout4.esm") as Race EndFunction Race Function GetNativeVertibirdRace(string callerId = "Default") global return Game.GetFormFromFile(0x000D77E3, "Fallout4.esm") as Race EndFunction Race Function GetNativeAssaultronRace(string callerId = "Default") global return Game.GetFormFromFile(0x000D8417, "Fallout4.esm") as Race EndFunction Race Function GetNativeProtectronRace(string callerId = "Default") global return Game.GetFormFromFile(0x000DFB33, "Fallout4.esm") as Race EndFunction Race Function GetNativeLibertyPrimeRace(string callerId = "Default") global return Game.GetFormFromFile(0x00108019, "Fallout4.esm") as Race EndFunction Race Function GetNativeSettlementTurretRace(string callerId = "Default") global return Game.GetFormFromFile(0x001A6D64, "Fallout4.esm") as Race EndFunction Race Function GetNativeRobobrainAutomatronRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCRobot.esm") return Game.GetFormFromFile(0x00001129, "DLCRobot.esm") as Race endif return None EndFunction Race Function GetNativeSettlementSpotlightAutomatronRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCRobot.esm") return Game.GetFormFromFile(0x00002804, "DLCRobot.esm") as Race endif return None EndFunction Race Function GetNativeBubbleTurretFarHarborRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCCoast.esm") return Game.GetFormFromFile(0x00009591, "DLCCoast.esm") as Race endif return None EndFunction Race Function GetNativeIndexerFarHarborRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCCoast.esm") return Game.GetFormFromFile(0x0002E010, "DLCCoast.esm") as Race endif return None EndFunction Race Function GetNativeRobobrainFarHarborRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCCoast.esm") return Game.GetFormFromFile(0x00033E79, "DLCCoast.esm") as Race endif return None EndFunction Race Function GetNativeAnimatronicAlienNukaWorldRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCNukaWorld.esm") return Game.GetFormFromFile(0x000019126, "DLCNukaWorld.esm") as Race endif return None EndFunction Race Function GetNativeZetanAlienRace(string callerId = "Default") global return Game.GetFormFromFile(0x00184C4D, "Fallout4.esm") as Race EndFunction Race Function GetNativeBloodbugRace(string callerId = "Default") global return Game.GetFormFromFile(0x0002456D, "Fallout4.esm") as Race EndFunction Race Function GetNativeBloatflyRace(string callerId = "Default") global return Game.GetFormFromFile(0x00029463, "Fallout4.esm") as Race EndFunction Race Function GetNativeRadroachRace(string callerId = "Default") global return Game.GetFormFromFile(0x0004716C, "Fallout4.esm") as Race EndFunction Race Function GetNativeStingwingRace(string callerId = "Default") global return Game.GetFormFromFile(0x0005FBB1, "Fallout4.esm") as Race EndFunction Race Function GetNativeCaveCricketNukaWorldRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCNukaWorld.esm") return Game.GetFormFromFile(0x0000AAFE, "DLCNukaWorld.esm") as Race endif return None EndFunction Race Function GetNativeSwarmNukaWorldRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCNukaWorld.esm") return Game.GetFormFromFile(0x0000B028, "DLCNukaWorld.esm") as Race endif return None EndFunction Race Function GetNativeAntNukaWorldRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCNukaWorld.esm") return Game.GetFormFromFile(0x0000CE5E, "DLCNukaWorld.esm") as Race endif return None EndFunction Race Function GetNativeMirelurkRace(string callerId = "Default") global return Game.GetFormFromFile(0x00023FFC, "Fallout4.esm") as Race EndFunction Race Function GetNativeMirelurkHunterRace(string callerId = "Default") global return Game.GetFormFromFile(0x00064C60, "Fallout4.esm") as Race EndFunction Race Function GetNativeMirelurkKingRace(string callerId = "Default") global return Game.GetFormFromFile(0x000B7F91, "Fallout4.esm") as Race EndFunction Race Function GetNativeMirelurkQueenRace(string callerId = "Default") global return Game.GetFormFromFile(0x000E12A6, "Fallout4.esm") as Race EndFunction Race Function GetNativeAnglerFarHarborRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCCoast.esm") return Game.GetFormFromFile(0x0000FEEA, "DLCCoast.esm") as Race endif return None EndFunction Race Function GetNativeFogCrawlerFarHarborRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCCoast.esm") return Game.GetFormFromFile(0x00014174, "DLCCoast.esm") as Race endif return None EndFunction Race Function GetNativeHermitCrabFarHarborRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCCoast.esm") return Game.GetFormFromFile(0x000180A8, "DLCCoast.esm") as Race endif return None EndFunction Race Function GetNativeGulperFarHarborRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCCoast.esm") return Game.GetFormFromFile(0x000247C1, "DLCCoast.esm") as Race endif return None EndFunction Race Function GetNativeSmallGulperFarHarborRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCCoast.esm") return Game.GetFormFromFile(0x0004E28E, "DLCCoast.esm") as Race endif return None EndFunction Race Function GetNativeMoleRatRace(string callerId = "Default") global return Game.GetFormFromFile(0x0001D810, "Fallout4.esm") as Race EndFunction Race Function GetNativeDeathclawRace(string callerId = "Default") global return Game.GetFormFromFile(0x0001DB4A, "Fallout4.esm") as Race EndFunction Race Function GetNativeBrahminRace(string callerId = "Default") global return Game.GetFormFromFile(0x0002047E, "Fallout4.esm") as Race EndFunction Race Function GetNativeMongrelRace(string callerId = "Default") global return Game.GetFormFromFile(0x0003578A, "Fallout4.esm") as Race EndFunction Race Function GetNativeRadscorpionRace(string callerId = "Default") global return Game.GetFormFromFile(0x000636AB, "Fallout4.esm") as Race EndFunction Race Function GetNativeRadstagRace(string callerId = "Default") global return Game.GetFormFromFile(0x0007ED1D, "Fallout4.esm") as Race EndFunction Race Function GetNativeYaoGuaiRace(string callerId = "Default") global return Game.GetFormFromFile(0x000A0F2F, "Fallout4.esm") as Race EndFunction Race Function GetNativeCatRace(string callerId = "Default") global return Game.GetFormFromFile(0x000C9ACF, "Fallout4.esm") as Race EndFunction Race Function GetNativeGorillaRace(string callerId = "Default") global return Game.GetFormFromFile(0x000D9804, "Fallout4.esm") as Race EndFunction Race Function GetNativeRaiderDogRace(string callerId = "Default") global return Game.GetFormFromFile(0x00187AF9, "Fallout4.esm") as Race EndFunction Race Function GetNativeMutatedWolfFarHarborRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCCoast.esm") return Game.GetFormFromFile(0x0003D077, "DLCCoast.esm") as Race endif return None EndFunction Race Function GetNativeRadRabbitFarHarborRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCCoast.esm") return Game.GetFormFromFile(0x0003DDDE, "DLCCoast.esm") as Race endif return None EndFunction Race Function GetNativeRadChickenFarHarborRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCCoast.esm") return Game.GetFormFromFile(0x0003FD66, "DLCCoast.esm") as Race endif return None EndFunction Race Function GetNativeBloodwormNukaWorldRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCNukaWorld.esm") return Game.GetFormFromFile(0x0000A1A6, "DLCNukaWorld.esm") as Race endif return None EndFunction Race Function GetNativeRadRatNukaWorldRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCNukaWorld.esm") return Game.GetFormFromFile(0x0000C606, "DLCNukaWorld.esm") as Race endif return None EndFunction Race Function GetNativeQuantumDeathclawNukaWorldRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCNukaWorld.esm") return Game.GetFormFromFile(0x0000D329, "DLCNukaWorld.esm") as Race endif return None EndFunction Race Function GetNativeBrahmiluffNukaWorldRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCNukaWorld.esm") return Game.GetFormFromFile(0x00023435, "DLCNukaWorld.esm") as Race endif return None EndFunction Race Function GetNativeGatorclawNukaWorldRace(string callerId = "Default") global if Game.IsPluginInstalled("DLCNukaWorld.esm") return Game.GetFormFromFile(0x0003637A, "DLCNukaWorld.esm") as Race endif return None EndFunction ; Alternative (cleaner?) approach would be to define stuff in FormLists ; That, however, will not allow for dynamic DLCs resolution in runtime and will add hard dependency bool Function IsHumanRace(Race source, string callerId = "Default") global if source == GetNativeHumanRace(callerId) return true endif if source == GetNativeHumanChildRace(callerId) return true endif return false EndFunction bool Function IsGhoulRace(Race source, string callerId = "Default") global if source == GetNativeFeralGhoulRace(callerId) return true endif if source == GetNativeGhoulRace(callerId) return true endif if source == GetNativeGlowingOneRace(callerId) return true endif if source == GetNativeGhoulChildRace(callerId) return true endif return false EndFunction bool Function IsMutantRace(Race source, string callerId = "Default") global if source == GetNativeSuperMutantRace(callerId) return true endif if source == GetNativeMutantHoundRace(callerId) return true endif if source == GetNativeBehemothRace(callerId) return true endif return false EndFunction bool Function IsSynthRace(Race source, string callerId = "Default") global if source == GetNativeGen1SynthRace(callerId) return true endif if source == GetNativeGen2SynthRace(callerId) return true endif if source == GetNativeGen2P5SynthRace(callerId) return true endif return false EndFunction bool Function IsRobotRace(Race source, string callerId = "Default") global if source == GetNativeMrHandyRace(callerId) return true endif if source == GetNativeEyeBotRace(callerId) return true endif if source == GetNativeSentryBotRace(callerId) return true endif if source == GetNativeTripodTurretRace(callerId) return true endif if source == GetNativeBubbleTurretRace(callerId) return true endif if source == GetNativeVertibirdRace(callerId) return true endif if source == GetNativeAssaultronRace(callerId) return true endif if source == GetNativeProtectronRace(callerId) return true endif if source == GetNativeLibertyPrimeRace(callerId) return true endif if source == GetNativeSettlementTurretRace(callerId) return true endif if Game.IsPluginInstalled("DLCRobot.esm") if source == GetNativeRobobrainAutomatronRace(callerId) return true endif if source == GetNativeSettlementSpotlightAutomatronRace(callerId) return true endif endif if Game.IsPluginInstalled("DLCCoast.esm") if source == GetNativeBubbleTurretFarHarborRace(callerId) return true endif if source == GetNativeIndexerFarHarborRace(callerId) return true endif if source == GetNativeRobobrainFarHarborRace(callerId) return true endif endif if Game.IsPluginInstalled("DLCNukaWorld.esm") if source == GetNativeAnimatronicAlienNukaWorldRace(callerId) return true endif endif return false EndFunction bool Function IsBugRace(Race source, string callerId = "Default") global if source == GetNativeBloodbugRace(callerId) return true endif if source == GetNativeBloatflyRace(callerId) return true endif if source == GetNativeRadroachRace(callerId) return true endif if source == GetNativeStingwingRace(callerId) return true endif if Game.IsPluginInstalled("DLCNukaWorld.esm") if source == GetNativeCaveCricketNukaWorldRace(callerId) return true endif if source == GetNativeSwarmNukaWorldRace(callerId) return true endif if source == GetNativeAntNukaWorldRace(callerId) return true endif endif return false EndFunction bool Function IsSeaworldRace(Race source, string callerId = "Default") global if source == GetNativeMirelurkRace(callerId) return true endif if source == GetNativeMirelurkHunterRace(callerId) return true endif if source == GetNativeMirelurkKingRace(callerId) return true endif if source == GetNativeMirelurkQueenRace(callerId) return true endif if Game.IsPluginInstalled("DLCCoast.esm") if source == GetNativeAnglerFarHarborRace(callerId) return true endif if source == GetNativeFogCrawlerFarHarborRace(callerId) return true endif if source == GetNativeHermitCrabFarHarborRace(callerId) return true endif if source == GetNativeGulperFarHarborRace(callerId) return true endif if source == GetNativeSmallGulperFarHarborRace(callerId) return true endif endif return false EndFunction bool Function IsAnimalRace(Race source, string callerId = "Default") global if source == GetNativeMoleRatRace(callerId) return true endif if source == GetNativeDeathclawRace(callerId) return true endif if source == GetNativeBrahminRace(callerId) return true endif if source == GetNativeMongrelRace(callerId) return true endif if source == GetNativeRadscorpionRace(callerId) return true endif if source == GetNativeRadstagRace(callerId) return true endif if source == GetNativeYaoGuaiRace(callerId) return true endif if source == GetNativeCatRace(callerId) return true endif if source == GetNativeGorillaRace(callerId) return true endif if source == GetNativeRaiderDogRace(callerId) return true endif if Game.IsPluginInstalled("DLCCoast.esm") if source == GetNativeMutatedWolfFarHarborRace(callerId) return true endif if source == GetNativeRadRabbitFarHarborRace(callerId) return true endif if source == GetNativeRadChickenFarHarborRace(callerId) return true endif endif if Game.IsPluginInstalled("DLCNukaWorld.esm") if source == GetNativeBloodwormNukaWorldRace(callerId) return true endif if source == GetNativeRadRatNukaWorldRace(callerId) return true endif if source == GetNativeQuantumDeathclawNukaWorldRace(callerId) return true endif if source == GetNativeBrahmiluffNukaWorldRace(callerId) return true endif if source == GetNativeGatorclawNukaWorldRace(callerId) return true endif endif return false EndFunction Feel free to use / rehash / restructure. It will definitely solve the problems with mapping the native races into something you want to identify as the same thing.
spicydoritos Posted April 22, 2021 Author Posted April 22, 2021 5 minutes ago, Operand said: Feel free to use / rehash / restructure. It will definitely solve the problems with mapping the native races into something you want to identify as the same thing. I know I *can* map the races with script properties and form calls to combine them; I did it for my other mod (She's Got That Glow). The issue is that I can't map them without also losing the flexibility to track current (and future) custom races as separate entries. Ditto for DLC races. As far as I know I can't run form calls on other mods without incurring unwanted dependencies. For the purposes of this mod, I'm of the opinion that flexibility is more valuable than race groupings.
Operand Posted April 22, 2021 Posted April 22, 2021 17 minutes ago, spicydoritos said: As far as I know I can't run form calls on other mods without incurring unwanted dependencies You can. CallFunction / CallGlobalFunction / GetFormFromFile are meant to be used exactly for that (you can also see the usage of the latter in the code above to dynamically resolve DLCs and use records from them without introducing hard DLC dependencies in the mod). But - oh well, I wished for the best. Apologies for the bother.
spicydoritos Posted April 22, 2021 Author Posted April 22, 2021 11 minutes ago, Operand said: You can. CallFunction / CallGlobalFunction / GetFormFromFile are meant to be used exactly for that (you can also see the usage of the latter in the code above to dynamically resolve DLCs and use records from them without introducing hard DLC dependencies in the mod). But - oh well, I wished for the best. Apologies for the bother. No, I appreciate the input, and I apologize if I came off short. That's actually good to know about the non-dependency, and upon reflection I asked myself why I was stuck on doing it fully one way or another (form calls vs purely GetRace). There's no reason I couldn't do a hybrid and just make a couple extra property arrays for the races I want to group.
Operand Posted April 22, 2021 Posted April 22, 2021 16 minutes ago, spicydoritos said: That's actually good to know about the non-dependency, and upon reflection I asked myself why I was stuck on doing it fully one way or another (form calls vs purely GetRace). There's no reason I couldn't do a hybrid and just make a couple extra property arrays for the races I want to group. To be frank, your issue sounds like a perfect use-case for doing the dynamic resolution. Why? Because new mods featuring races in Fallout4 come out so rare that even now there are less new races (worth attention) than fingers on one hand. Or two hands, if you include everything. This means that occasional update for it would need to be done once per half a year (if at all). And that update would be either adding a new native race option (like in the code above) or just another entry to the internal array (like you mentioned). I chose methods over arrays because recompiling a script with another method call from within an existing method is completely safe and can be updated just by overwriting the old mod version whereas arrays require some updating mechanism since they are stored in the saves. So knowing that, you can reliably map the native racial entries to whichever structures you want. 99.99% of the work needs to be done only once - for races from base game and game DLCs.
spicydoritos Posted April 22, 2021 Author Posted April 22, 2021 30 minutes ago, Operand said: To be frank, your issue sounds like a perfect use-case for doing the dynamic resolution. Why? Because new mods featuring races in Fallout4 come out so rare that even now there are less new races (worth attention) than fingers on one hand. Or two hands, if you include everything. This means that occasional update for it would need to be done once per half a year (if at all). And that update would be either adding a new native race option (like in the code above) or just another entry to the internal array (like you mentioned). I chose methods over arrays because recompiling a script with another method call from within an existing method is completely safe and can be updated just by overwriting the old mod version whereas arrays require some updating mechanism since they are stored in the saves. So knowing that, you can reliably map the native racial entries to whichever structures you want. 99.99% of the work needs to be done only once - for races from base game and game DLCs. Honestly what I'll do is just leave the current tracking methods in place, but add a secondary check for the races I want to group... which are really only dogs and Gen 2 synths anyway. No need to make entries for every race. Meanwhile I'll ponder whether the DLC races are worth any extra effort. The only ones I know of with animations are from Far Harbor, and those creatures are sufficiently different that I wouldn't group them with vanilla creatures anyway.
spicydoritos Posted April 24, 2021 Author Posted April 24, 2021 Version 1.15 is up! -The three dog races (Dogmeat, RaiderDog, and ViciousDog) will now be tracked as a single race type. Gen 2 Synths and Valentine will also be tracked as a single race type. A clean save is not necessary. However, XP decay for the above races will restart if an existing save is updated. Hopefully that's the last major change and I can slow down the pace of updates a bit. 2
spicydoritos Posted April 25, 2021 Author Posted April 25, 2021 After playing with the previous version for a bit longer, I noticed many instances where observers from AAF Spectators were not being counted accurately. So here's a fix. Version 1.16 is up! -Greatly improve AAF Spectators listening function. Now it will periodically check for new observers as long as the player animation is in progress. This allows time for distant walkers to arrive.
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