Search the Community
Showing results for tags 'meter'.
-
Required knowledge: Passable skill in reading xml syntax and Bethesda's xml elements, see here. Some parts require some experience writing an MCM script. Introduction: what to expect What this guide will focus on is how to create your own HUD readout to reflect a number that you set in an esp, much like the primary needs readout that you'd expect from mods like oHUD and IMCNNV. The method I'll follow is to slowly build up a mod that I've made for this purpose, called Hygiene, so you can follow along. You'll find it attached, as well as a copy of all scripts inside. Building up from the simplest method and gradually adding functionalities, you'll learn to add both a percentage readout (text) and a visual meter/bar (image). Along the way you'll learn about subscribing UI components to UIO, passing information from geck script to xml, and writing a script to allow your users to manually adjust the positioning of your HUD component. 1. Baby steps: a bit of text 1.1. Objective: a HUD for Hygiene.esp Let's assume we have a little mod called Hygiene.esp that calculates and adjusts the player's hygiene levels as time passes. Actually, we do have it, I wrote it just to have something to refer to. Here's its main script, calculating a number that I wish to display in HUD eventually: In the good old Fallout tradition, we'll call the stat "Hygiene" but assume that the higher the value is, the worse off the player is. In the same way that "more H2O" means "thirstier", high Hygiene has you reaching critical values, going into the red, something like that. You may see from the script that it basically just adds up a number as time passes, depending on what the player's doing and how long it should take to reach 100 normally, all variables that are set in MCM. What I want to display, in the HUD, is simply a percentage, and a bit of text saying what it signifies, something like: HYG: 10% 1.2. Subscribing to UIO Let's get started. First off, in case you didn't know, when it comes to adding something to the HUD we can't just plunk a new file in some folder. There is only one .xml file that handles hud and it's called hud_main_menu.xml, located under menus\main. In the past, adding anything to it in a mod obviously invited conflicts with other mods trying to do the same. A workaround using the <include> element had the disadvantage of not being user-friendly, and the uHUD mod which automated that process required its author to update it whenever a new HUD mod was published. All a thing of the past now that with UIO the conflicts are resolved and it'll automatically recognize our hud component if: - we create an .xml file for it in a folder under menus\prefabs - we create a text file in a folder called uio\public that notifies UIO that we have an .XML file that needs to be appended to the main hud, with this bit of code in it: DSHyg\DSHygHUD.xml::HUDMainMenu true 1.3. Adding text elements With that out of the way, let's open our .xml file. We're going to split "HYG: 10%" into 3 bits: the label, the value, and the percent sign. After all, while HYG: (the label) and the percent sign are bits of text that won't change, the value is something we will expect to receive from the .esp. Now's also a good time to group those 3 elements in one element because we'll go a little crazy with repositioning and jacking into other hud mods in later chapters. Because we also still need a root element to contain everything that'll be in our xml we're looking at an overall structure like this, for the moment: a root, with our 'own hud' components grouped together underneath. <rect name="DSHygHUDROOT"> <rect name="DSHygOwnHUD" > <text name="DSHygLabel"> </text> <text name="DSHygPercentSign"> </text> <text name="DSHygValueDisplay"> </text> </rect> </rect> If you've read my guide on reading XML, you should realize that this main structure only contains object elements, adding 3 text components, grouped under one invisible rectangle. What's lacking is what text they should display, and where on our screen they should be positioned. Let's add some properties, starting with the strings. <text name="DSHygLabel"> <string>HYG:</string> </text> <text name="DSHygPercentSign"> <string>%</string> </text> The strings that aren't dynamic are simply put between <string> tags. The one that is, the actual value to display, will be phoned in by my main script in the .esp, where this line SetUIFloat "HUDMainMenu\_DSHygValue" fHyg sets a custom property element, _DSHygValue, to the value. What's left for us to do in the xml is read it from that element, using the <copy> operator with the "io()" src attribute: <text name="DSHygValueDisplay"> <copy src="io()" trait="_DSHygValue"/> </text> 2. Positioning Right now, this is what our xml looks like: <spoiler> <rect name="DSHygHUDROOT"> <rect name="DSHygOwnHUD" > <text name="DSHygLabel"> <string>HYG:</string> </text> <text name="DSHygPercentSign"> <string>%</string> </text> <text name="DSHygValueDisplay"> <copy src="io()" trait="_DSHygValue"/> </text> </rect> </rect> </spoiler> At present, in-game, all three text components would start displaying, except they'd all be stuck in the top-left corner, because we haven't entered positioning data yet. Let's handle that, by first setting a position for their parent rectangle: <rect name="DSHygOwnHUD" > <locus> &true; </locus> <x> <copy src="screen" trait="width" /> <sub> 150 </sub> </x> <y> <copy src="screen" trait="height" /> <div> 2 </div> </y> <!--our three text components are still here--> </rect> Copying the width of the screen and subtracting some pixels, I place it to the right of the screen, and in the middle in terms of height. Because we're going to fine-tune the positioning of the individual text elements in a minute, now's a good time to also toggle on the <locus> element, which'll treat x & y positioning of the children as relative to the parent rectangle, rather than to the screen. That's just my preference. With locus toggled on, I choose not to specify any positioning for the label's text component, so it just copies its x & y position from the parent. However, I do outline it to the left with the <justify> element: <text name="DSHygLabel"> <justify> &left; </justify> <string> HYG: </string> </text> I'll outline the other two to the right because I want the value to stay neatly close to the percent sign, and this is easier that way. The other elements share the same y-position, but I shift the percent sign more to the right (+ 100 x, relative to the parent), and do the same for the value display by copying the x-position of the percent sign, minus the width of the percent sign itself: <text name="DSHygPercentSign"> <x>100</x> <justify> &right; </justify> <string>%</string> </text> <text name="DSHygValueDisplay"> <x> <copy src="sibling(DSHygPercentSign)" trait="x" /> <sub src="sibling(DSHygPercentSign)" trait="width" /> </x> <y> <copy src="sibling(DSHygLabel)" trait="y" /> </y> <justify> &right; </justify> <string> <copy src="io()" trait="_DSHygValue" /> </string> </text> Which results in this, in-game: A good start. I could've achieved the same result in many other ways, really. You can position components relative to the screen or each other using a variety of formulas - what I used was just one of the simpler methods. 3. Toggling visibility We may've gotten our little percentage HUD to display in gamemode, as planned, but as you can see from the following picture, it also shows in menumode. I don't want that, it's going to detract from whatever I try to do there, so it's gotta go. There are also situations where we may not even want it to show in gamemode - in combat, for instancee. Handling that winds down to the same technique: toggling visibility using a custom property. Remember our rectangle parent that groups the three text components? In itself, it's invisible, but if we toggle its visibility, it toggles that of all its children. So let's do that, by making its visibility dependent on a custom property: <rect name="DSHygOwnHUD"> <visible> <copy src="io()" trait="_DSHygOwnHud" </visible> <locus> &true; </locus> <x> <copy src="screen" trait="width" /> <sub> 150 </sub> </x> <y> <copy src="screen" trait="height" /> <div> 2 </div> </y> <!--our three text components are still here--> </rect> while in our esp, I made a separate little quest script that just does this, for the moment: scn DSHygHUDQstScpt Begin GameMode SetUIFloat "HUDMainMenu\_DSHygOwnHud" 1 End Begin MenuMode SetUIFloat "HUDMainMenu\_DSHygOwnHud" 0 End You can easily add more conditions in the esp's script. For instance, to make our little HUD disappear during combat, you'd go: Begin GameMode if playerref.IsInCombat SetUIFloat "HUDMainMenu\_DSHygOwnHud" 0 else SetUIFloat "HUDMainMenu\_DSHygOwnHud" 1 endif End Finally, maybe we should toggle off our HUD in situations where vanilla HUD is also off, perhaps in some type of cutscene or other... Usually, this is done by simply making the visibility of the HUD dependent on the visibility of the "ActionPoints" HUD component: <rect name="DSHygOwnHUD"> <visible> <copy src="ActionPoints" trait="visible"/> <and src="io()" trait="_DSHygOwnHud" /> </visible> <!--everything else--> </rect> (Note that a mod like iHUD which makes the ActionPoints component visually disappear under certain conditions, doesn't touch the visibility state of it: it'll still be on. Conditions that really switch it off include situations that use DisablePlayerControls.) On the other hand, we may want to provide an option to force our HUD to stay on despite the usual HUD being off. In that case we toggle the state of a new custom property in our esp: <rect name="DSHygOwnHUD"> <visible> <copy src="ActionPoints" trait="visible"/> <or src="io()" trait="_DSHygForceHud" /> <and src="io()" trait="_DSHygOwnHud" /> </visible> <!--everything else--> </rect> scn DSHygHUDQstScpt int iForceHUD Begin GameMode SetUIFloat "HUDMainMenu\_DSHygOwnHud" 1 SetUIFloat "HUDMainMenu\_DSHygForceHud" iForceHUD End Begin MenuMode SetUIFloat "HUDMainMenu\_DSHygOwnHud" 0 End Forcing it on can then happen through script on special occasions, or as a player preference in MCM, which is what I'm adding in our little mod: a checkbox that toggles the iForceHUD variable there. 4. Letting the player adjust position In addition to not overriding vanilla menus, we obviously want to be good neighbors with other hud mods. The best way of doing that is to provide players with the option to manually reposition our HUD component themselves in case of conflict. Some mods let you do this in MCM's menumode; we'll do it in gamemode instead. 4.1 Adding custom properties for x and y offsets First off, we're in luck because we decided to group our components under one rectangle, so we really need to only be able to adjust that one's position. In fact, that was a major reason for doing that in the first place, or what did you think. We add new custom properties to our rectangle's existing x & y elements, conveniently called _DSHygX and _DSHygY: <rect name="DSHygOwnHud"> <!--visibility intel here> <locus> &true; </locus> <x> <copy src="screen" trait="width" /> <sub> 150 </sub> <add src="io()" trait="_DSHygX" /> </x> <y> <copy src="screen" trait="height" /> <div> 2 </div> <add src="io()" trait="_DSHygY" /> </y> <!-- everything else--> </rect> The idea is that both custom properties will reflect variables, now held in our regular HUD script, that are added to or subtracted from depending on keys that the player presses. scn DSHygHudQstScpt int iForceHud float fOffsetX float fOffsetY Begin GameMode SetUIFloat "HUDMainMenu\_DSHygOwnHud" 1 SetUIFloat "HUDMainMenu\_DSHygForceHud" iForceHud SetUIFloat "HudMainMenu\_DSHygX" fOffsetX SetUIFloat "HudMainMenu\_DSHygY" fOffsetY End Begin MenuMode SetUIFloat "HUDMainMenu\_DSHygOwnHud" 0 End In order to properly catch those key presses we need a rather fast-firing script, so let's make that separate from our others, toggle it on through MCM, and off when we're done. 4.2. Detecting and storing key presses There are two ways of accomplishing this. One is writing the adjustment script so that people can do it right in the MCM menu, but that itself can interfere with visibility and doesn't always show the other HUD components that may get in the way so you have to go in and out to adjust some more. The other is doing it in gamemode. The trouble there is that most keypresses already mean something, especially the movement controls which I'd like to use. Still, all things considered, option 2 is what we'll go with here - perfectly feasible if we tie up the player a little bit. First off, the entire quest needs toggled via MCM. Here's one way of doing that. In my MCM script: ; 'reset' block SetUIFloat "StartMenu/MCM/*:1/*:9/_enable" 1 SetUIString "StartMenu/MCM/*:1/*:9/_title" "Configure HUD position" SetUIFloat "StartMenu/MCM/*:1/*:9/_type" 5 SetUIFloat "StartMenu/MCM/*:1/*:9/_value" DSHygHud.iEditing ; 'default' block set DSHygHud.iEditing to 0 ; 'new value' block ; other stuff elseif iOption == 9 set DSHygHud.iEditing to fValue endif ; 'mouseover' block elseif iMouseover == 9 SetUIString "StartMenu/MCM/*:9/string" "Go back to gamemode to start moving the HUD around" endif In my DSHygHud quest script: int iEditing if iEditing set DSHygHudConfig.iStage to 0 startquest DSHygHudConfig if GetQuestRunning DSHygHudConfig set iEditing to 0 endif endif Since we want to be able to let players manipulate position using their usual movement controls, we need to - restrain the player character so that our keypresses don't move them around - capture the relevant keypresses and store them as offsets in local variables to the regular hud quest - broadcast the offsets to the xml, where our custom properties, _DSHygX and _DSHygY, are waiting to be read. The following quest script does that. Try to follow along. In stage 0, it forces the HUD on in case it was off for some reason, and restrains the player. In stage 1, it detects the pressing of controls and takes action accordingly, increasing and decreasing offsets that we broadcast as the custom properties _DSHygX and DSHygY to our xml. Stage 100 cleans up, setting the player free and stopping the script. scn DSHygHudConfigQstScpt int iGap int iStage int iWasForced Begin GameMode if playerref.IsInCombat set iStage to 100 endif if iStage == 0 if Playerref.GetRestrained set iStage to 1 SetUIFloat "HUDMainMenu\_DSHygOwnHud" 1 if 0 == GetQuestRunning DSHygHUD startquest DSHygHUD endif if DSHygHUD.iForceHUD == 0 set DSHygHUD.iForceHud to 1 set iWasForced to 1 endif else playerref.SetRestrained 1 return endif endif if iStage == 1 ; how much to move if IsControlPressed 9 ; 'run' control set iGap to 50 else set iGap to 5 endif ; forward = up if IsControlPressed 0 let DSHygHUD.fOffSetY -= iGap ; backward = down elseif IsControlPressed 1 let DSHygHud.fOffSetY += iGap endif SetUIFloat "HUDMainMenu\_DSHygY" DSHygHud.fOffsetY ; left = left if IsControlPressed 2 let DSHygHUD.fOffSetX -= iGap ; right = right elseif IsControlPressed 3 let DSHygHUD.fOffSetX += iGap endif SetUIFloat "HUDMainMenu\_DSHygX" DSHygHud.fOffsetX ; activate = save and exit if IsControlPressed 5 set iStage to 100 ; jump = restore defaults elseif IsControlPressed 12 set DSHygHUD.fOffSetX to 0 set DSHygHUD.fOffSetY to 0 set iStage to 0 endif endif ; quit if iStage == 100 if iWasForced set DSHygHud.iForceHud to 0 endif SetUIFloat "HUDMainMenu\_DSHygOwnHud" 0 if playerref.GetRestrained playerref.SetRestrained 0 else stopquest DSHygHudConfig endif endif End 4.3. Showing text hints on-screen One thing missing though is a little user-friendliness: people don't know which keys to press unless we tell them. A readme is fine and dandy but everybody knows people don't read them, so let's tell them which buttons to press in-game by adding some more text to our xml. First off, let's create a new rectangle, sibling to "DSHygOwnHUD", that'll group our new text elements and toggle the visibility of them all with a custom property. <rect name="DSHygPositionTips"> <visible> <copy src="io()" trait="_DSHygShowHints" /> </visible> <text name="DSHygTipsMove"> </text> <text name="DSHygTipsActivate"> </text> <text name="DSHygTipsJump"> </text> <text name="DSHygTipsRun"> </text> <!-- we'll add a new control to check as well: 'grab' will toggle the hints --> <text name="DSHygTipsGrab"> </text> </rect> We add a bit of code to toggle the hints when the grab control is pressed to our HudConfig script: if iStage == 1 ; the other stuff elseif IsControlPressed 27 if 0 == IsPressed set isPressed to 1 if 0 == GetUIFloat "HUDMainMenu\_DSHygShowHints" SetUIFloat "HUDMainMenu\_DSHygShowHints" 1 else SetUIFloat "HUDMainMenu\_DSHygShowHints" 0 endif endif else set isPressed to 0 endif We'll need a few textual elements to describe which keys to press. People know the movement ones, so they can go in a static string in the xml: <text name="DSHygTipsMove"> <string> Use the movement keys to move the HUD </string> </text> We'll construct the other strings in our HUD config quest script using GetControl to figure out which keys belong to which controls, broadcast them with SetUIStringEx and the %k format specifier, and turn on the visibility of the whole block by default: if iStage == 0 if playerref.GetRestrained ; other stuff set iKeyCode to GetControl 5 SetUIStringEX "HUDMainMenu\_DSHygTipActivate" "Press ACTIVATE (%k) to save and exit" iKeyCode set iKeyCode to GetControl 12 SetUIStringEX "HUDMainMenu\_DSHygTipJump" "Press JUMP (%k) to restore default position" iKeyCode set iKeyCode to GetControl 27 SetUIStringEx "HUDMainMenu\_DSHygTipGrab" "Press GRAB (%k) to toggle these hints" iKeyCode set iKeyCode to GetControl 9 SetUIStringEX "HUDMainMenu\_DSHygTipRun" "Hold RUN (%k) to move greater distances" iKeyCode SetUIFloat "HUDMainMenu\_DSHygShowHints" 1 ; other stuff and copy them in our xml, putting them underneath each other somewhere in the middle of the screen: <rect name="DSHygPositionTips"> <visible> <copy src="io()" trait="_DSHygShowHints" /> </visible> <locus> &true; </locus> <x> <copy src="screen()" trait="width"/> <div> 2 </div> </x> <y> 400 </y> <text name="DSHygTipsMove"> <string> Use the movement controls to move the HUD. </string> </text> <text name="DSHygTipsActivate"> <y> 25 </y> <string> <copy src="io()" trait="_DSHygTipActivate" /> </string> </text> <text name="DSHygTipsJump"> <y> 50 </y> <string> <copy src="io()" trait="_DSHygTipJump" /> </string> </text> <text name="DSHygTipsRun"> <y> 75 </y> <string> <copy src="io()" trait="_DSHygTipRun" /> </string> </text> <text name="DSHygTipsGrab"> <y> 100 </y> <string> <copy src="io()" trait="_DSHygTipGrab" /> </string> </text> </rect> which has this result: 5. Intermezzo: More layout 5.1 adjusting fonts It's entirely possible you think the standard font is too small, and you wish the HUD component to jump out a little bit more. It's also entirely possible that unlike me you don't use Darn's UI mod, which makes the fonts smaller, and I haven't really checked if the positioning data I used work equally well with vanilla's fonts. Being able to switch between whichever fonts you have in your game could help players out in both cases, if there's a readability problem. We're in luck because whether you have font overrides or not in your game, they're numbered 1-8, so all it really takes is to stipulate in the .xml, for each text component, that we'll copy that number from the esp, using another custom property: <font> <copy src="io()" trait="_DSHYGFont" /> </font> A simple solution, then, would be to broadcast this in our HUD quest script, tied to a variable: SetUIFloat "HUDMainMenu\_DSHygFont" iFont and let players set that variable with an MCM scale. Another option is to tack on this bit to our positioning config script to have players switch it in real time when they're positioning it: ; crouch key = change font size elseif IsControlPressed 8 if 0 == IsPressed let isPressed := 1 if DSHygHUD.iFont < 8 let DSHygHud.iFont += 1 else let DSHygHud.iFont := 1 endif endif as well as constructing the string: set iKeyCode to GetControl 8 SETUIStringEX "HUDMainMenu\_DSHygTipCrouch" "Press the Pipboy Key (%k) to alter font size" iKeycode and putting that in our xml: <text name="DSHygTipsCrouch"> <y> 100 </y> <string> <copy src="io()" trait="_DSHygTipCrouch" /> </string> </text> 5.2. adjusting opacity At the moment we don't yet have a way for players to disable our HUD if they want to make a screenshot or something. And perhaps, at full opacity, some may found our hud component to be just a little too intrusive. Let's kill two birds with one stone and provide a way to adjust the component's opacity. In this case, we opt for an MCM slider - it's just the more sensible thing to do. Opacity goes from 0 to 255, so that'll be the range for our MCM slider too, tied to yet another variable that we broadcast from our main HUD quest script to our xml: In our xml: <alpha> <copy src="io()" trait="_DSHygAlpha" /> </alpha> In our main hud quest: SetUIFloat "HUDMainMenu\_DSHygAlpha" iAlpha With these entries in our MCM menu: ; reset block SetUIFloat "StartMenu/MCM/*:1/*:10/_enable" 1 SetUIString "StartMenu/MCM/*:1/*:10/_title" "Set HUD opacity" SetUIFloat "StartMenu/MCM/*:1/*:10/_type" 2 SetUIFloat "StartMenu/MCM/*:1/*:10/_value" DSHygHud.iAlpha ; default block set DSHygHud.iAlpha to 255 ; new value block elseif iOption == 10 set DSHygHud.iAlpha to fValue ; show scale block elseif iOption == 10 SetUIFloat "StartMenu/MCM/_Value" DSHygHud.iAlpha SETUIFloat "StartMenu/MCM/_ValueDecimal" 3 SetUIFloat "StartMenu/MCM/_ValueIncrement" 5 SetUIFLoat "StartMenu/MCM/_ValueMax" 255 SetUIFloat "StartMenu/MCM/_ValueMin" 0 SetUIString "StartMenu/MCM/*:2/_title" "Hud opacity" ; default scale block elseif iOption == 10 SetUIFloat "StartMenu/MCM/_Value" 255 5.3. adjusting color FNV's amber is nice and dandy, but perhaps TTW players would prefer a shiny, sickly green, and maybe someone else a bright pink, and who are we to deny that to them, really? After all, we have <red>, <green> and <blue> property elements, and the four standard colors for HUD have the following RGB values: amber - 255, 182, 66 blue - 46, 207, 255 green - 26, 255, 128 white - 197, 255, 255 Something tells me we can create a 'list' in MCM that has those names, as well as a custom option, tied to a variable in our HUD quest script: int iColorOption int iRed int iGreen int iBlue if iColorOption == 1 ; amber SetUIFloat "HUDMainMenu\_DSHygRed" 255 SetUIFloat "HUDMainMenu\_DSHygGreen" 182 SetUIFloat "HUDMainMenu\_DSHygBlue" 66 elseif iColorOption == 2 ; blue values go under here elseif iColorOption == 3 ; green values here elseif iColorOption == 4 ; white values here elseif iColorOption == 5 ; custom SetUIFloat "HUDMainMenu\_DSHygRed" iRed SetUIFloat "HUDMainMenu\_DSHygGreen" iGreen SetUIFloat "HUDMainMenu\_DSHygBlue" iBlue endif In our xml, we detach our text elements from their usual color scheme by saying we don't want to use the regular system color, and we add <red>, <green> and <blue> property elements to each text element: <systemcolor> &nosystemcolor; </systemcolor> <red> <copy src="io()" trait="_DSHygRed" /> </red> <green> <copy src="io()" trait="_DSHygGreen" /> </green> <blue> <copy src="io()" trait="_DSHygBlue" /> </blue> Our MCM, meanwhile gets expanded with the following list code: ; reset block SetUIFloat "StartMenu/MCM/*:1/*:11/_enable" 1 SetUIString "StartMenu/MCM/*:1/*:11/_title" "Color options" SetUIFloat "StartMenu/MCM/*:1/*:11/_type" 1 SetUIFloat "StartMenu/MCM/*:1/*:11/_value" DSHygHud.iColorOption if DSHygHud.iColorOption == 1 SetUIString "StartMenu/MCM/*:1/*:11/value/*:1/string" "Amber" elseif DSHygHud.iColorOption == 2 SetUIString "StartMenu/MCM/*:1/*:11/value/*:1/string" "Blue" elseif DSHygHud.iColorOption == 3 SetUIString "StartMenu/MCM/*:1/*:11/value/*:1/string" "Green" elseif DSHygHud.iColorOption == 4 SetUIString "StartMenu/MCM/*:1/*:11/value/*:1/string" "White" elseif DSHygHud.iColorOption == 5 SetUIString "StartMenu/MCM/*:1/*:11/value/*:1/string" "Custom" endif ; default block set DSHygHud.iColorOption to 1 ; new value block elseif iOption == 11 set DSHygHud.iColorOption to fValue ; list block elseif iOption == 11 SetUIString "StartMenu/MCM/*:3/_title" "Color option" SetUIFloat "StartMenu/MCM/*:3/*:1/_enable" 1 SetUIFloat "StartMenu/MCM/*:3/*:2/_enable" 1 SetUIFloat "StartMenu/MCM/*:3/*:3/_enable" 1 SetUIFloat "StartMenu/MCM/*:3/*:4/_enable" 1 SetUIFloat "StartMenu/MCM/*:3/*:5/_enable" 1 SetUIString "StartMenu/MCM/*:3/*:1/text/string" "Amber" SetUIString "StartMenu/MCM/*:3/*:2/text/string" "Blue" SetUIString "StartMenu/MCM/*:3/*:3/text/string" "Green" SetUIString "StartMenu/MCM/*:3/*:4/text/string" "White" SetUIString "StartMenu/MCM/*:3/*:5/text/string" "Custom" Which gets me a nice sickly green when I choose that in-game: Of course, why stop there. We can also use MCM's handy type-9 option to let people enter any RGB value they like. When were you ever going to use it otherwise, honestly? Take care though that the MCM documentation has a mistake in it, where it says to use SetUIStringEX where you really should use SetUIFloat. Let's also make this option dependent on our 'custom' option in the list: ; reset block if DSHygHud.iColorOption == 5 SetUIFloat "StartMenu/MCM/*:1/*:12/_enable" 1 else SetUIFloat "StartMenu/MCM/*:1/*:12/_enable" 2 endif SetUIString "StartMenu/MCM/*:1/*:12/_title" "Custom color" SetUIFloat "StartMenu/MCM/*:1/*:12/_type" 9 SetUIFloat "StartMenu/MCM/*:1/*:12/_value1" DSHygHud.iRed SetUIFloat "StartMenu/MCM/*:1/*:12/_value2" DSHygHud.iGreen SetUIFloat "StartMenu/MCM/*:1/*:12/_value3" DSHygHud.iBlue ; default block set DSHygHud.iRed to 255 set DSHygHud.iGreen to 182 set DSHygHud.iBlue to 66 ; new value block: elseif iOption == 12 set DSHygHud.iRed to GetUIFloat "StartMenu/MCM/_Value1" set DSHygHud.iGreen to GetUIFloat "StartMenu/MCM/_Value2" set DSHygHud.iBlue to GetUIFloat "StartMenu/MCM/_Value3" ; showscale block: elseif iOption == 12 SetUIFloat "StartMenu/MCM/_Value1" DSHygHud.iRed SetUIFloat "StartMenu/MCM/_Value2" DSHygHud.iGreen SetUIFloat "StartMenu/MCM/_Value3" DSHygHud.iBlue SetUIString "StartMenu/MCM/*:2/_title" "Custom color" ; defaultscale block elseif iOption == 12 SetUIFloat "StartMenu/MCM/_Value1" 255 SetUIFloat "StartMenu/MCM/_Value2" 182 SetUIFloat "StartMenu/MCM/_Value3" 66 Which has me pick this gooey pink for our color: 6. Adding a meter or bar Instead of a dry percentage readout, maybe some would prefer a meter or bar displaying just how filthy we are. If we are to provide the option via another MCM toggle... ; reset block SetUIFloat "StartMenu/MCM/*:1/*:13/_enable" 1 SetUIString "StartMenu/MCM/*:1/*:13/_title" "Use a bar" SetUIFloat "StartMenu/MCM/*:1/*:13/_type" 5 SetUIFloat "StartMenu/MCM/*:1/*:13/_value" DSHygHud.iUseBar ; new value elseif iOption == 13 set DSHygHud.iUseBar to fValue and broadcast the option to xml with another custom property... if iUseBar SetUIFloat "HUDMainMenu\_DSHygBar" 1 else SetUIFloat "HUDMainMenu\_DSHygBar" 0 endif then we'll need to stop displaying the percentage if bars are on: <text name="DSHygPercentSign"> <!--other stuff--> <visible> <copy src="io()" trait="_DSHygValue" /> <gt>0</gt> <and> <copy src="io()" trait="_DSHygBar" /> <eq>0</eq> </and> </visible> <!--other stuff--> </text> <!-- also do the same for DSHygValueDisplay --> Next up, we write outselves 2 image elements, under the same rectangle where we placed our text components, one for the background and the other to fill that in: <rect name="DSHygOwnHUD"> <!-- the other stuff--> <image name="DSHygBarBack"> </image> <image name="DSHygBar"> </image> </rect> Their visibility will need to depend on the 'use bars' option, and we'll use a simple solid texture from vanilla to fill up their surface area: <image name="DSHygBarBack"> <visible> <copy src="io()" trait="_DSHygBar" /> </visible> <width>100</width> <height>15</height> <depth>2</depth> <y>20</y> <filename>Interface\Shared\solid.dds</filename> </image> You can tell from above that I gave the background image a maximum width of 100 pixels. (You could obviously go for more, but then you'll have to convert the hygiene value too.) The image that will fill in the background, DSHygBar, will take its width from the value of fHyg in our main quest: if iUseBar SetUIFloat "HUDMainMenu\_DSHygBar" 1 SetUIFloat "HUDMainMenu\_DSHygBarWidth" DSHyg.fHyg else SetUIFloat "HUDMainMenu\_DSHygBar" 0 endif <image name="DSHygBar" <visible> <copy src="io()" trait="_DSHygBar" /> </visible> <width> <copy src="io()" trait="_DSHygBarWidth" /> </width> <height>15</height> <depth>3</depth> <y>20</y> <filename>Interface\Shared\solid.dds</filename> </image> In order to provide contrast, I switch off the usual HUD color on the background and paint it grey, setting its opacity at half of whatever the opacity value we hooked up to a variable earlier is. Also note that the actual bar has a higher <depth> value than that of the background, making sure that it's painted on top of it. <image name="DSHygBarBack"> <visible> <copy src="io()" trait="_DSHygBar" /> </visible> <width>100</width> <height>15</height> <depth>2</depth> <y>20</y> <filename>Interface\Shared\solid.dds</filename> <systemcolor>&nosystemcolor;</systemcolor> <red>190</red> <green>190</green> <blue>190</blue> <alpha> <copy src="io()" trait="_DSHygAlpha" /> <div>2</div> </alpha> </image> Which gives us this result: Why stop there. Now that we know you can adjust the color of images as well as text with the red, green & blue elements, we might as well give the bar the same color that is selected for the text element: <image name="DSHygBar"> <!-- other stuff--> <systemcolor>&nosystemcolor;</systemcolor> <red> <copy src="io()" trait="_DSHygRed" /> </red> <green> <copy src="io()" trait="_DSHygGreen" /> </green> <blue> <copy src="io()" trait="_DSHygBlue" /> </blue> <!-- other stuff--> </image> And, why not, override it all with bright red if hygiene levels exceed a threshold value of 90: <image name="DSHygBarOverride"> <visible> <copy src="io()" trait="_DSHygBar" /> <and> <copy src="io()" trait="_DSHygBarWidth" /> <gt>90</gt> </and> </visible> <width> <copy src="io()" trait="_DSHygBarWidth" /> </width> <height>15</height> <depth>4</depth> <y>20</y> <systemcolor>&nosystemcolor;</systemcolor> <red>255</255> <green>0</green> <blue>0</blue> <alpha> 255 </alpha> <filename>Interface\Shared\solid.dds</filename> </image> By this time, maybe you can figure out how to make it flash then too, using the visibility block and another custom property element turned on and off in our .esp somewhere? Epilogue, credits There are always different ways to achieve the same results we've booked by following this little guide, so do mess around with it once you're familiar with the basics. I've kept to the more straightforward approaches, relying heavily on setting custom properties in my esp, given that I'm no expert at all in this and am much more at home with geck script than Beth xml. In the end parting advice is, as always, that if you're going to write some code like this, read what others have written first. I've based this on code I've seen in vanilla, what's explained in the Oblivion wiki, mods by Gopher, Impoftheperverse and JIP, and a small mod that Fallout2AM once made for me to explain some things. hygiene mod.7z Hygiene source.7z