Search the Community
Showing results for tags 'string'.
-
-
View File Various string bikini bottoms in black (made from nude bottom) Submitter unipatronus Submitted 01/05/2018 Category The Sims 4 Requires
-
View File Various string bikini bottoms in black (made from basic bottom) Submitter unipatronus Submitted 01/05/2018 Category The Sims 4 Requires
- 12 replies
-
13
-
View File String bikini bottoms in bright and pastel colors to match my triangle tops Submitter unipatronus Submitted 01/05/2018 Category The Sims 4 Requires
- 12 replies
-
11
-
1. Intro: String Theory 2. String Variable basics: declaring and initializing 3. 'Let' v. 'Set' syntax 4. Destroying string vars 5. Checking string vars 6. Passing string vars as parameters: ToString ($) 7. Concatenting strings, or: adding them up 8. Measuring and searching your string vars 9. Breaking string vars down, replacing bits, and reassembling them 10. Switching between string and other representations, character functions This one's a bit longer than the previous ones. Don't panic, and take a break when it gets too much. I know I had to. 1. INTRO: STRING THEORY Strings are particular combinations of characters - letters, numbers and punctuation. They're nothing new to the vanilla game and you've seen them all over. Just about anything that you see in the game interface that is a word or sentence instead of a number is a string: the names of the buttons in your menu, the word 'Open' or 'Enter' that pops up when your crosshair hovers over a container or door, the notification "You no longer have hunger sickness", the 'name' of the key the game kindly suggests that you press to activate something, and so on, they're all stored as strings under "Game Settings". Different language versions of the game will have different values for those strings. You may remember from the syntax tutorial that in a line like 'playerref.SetAV Health', 'health' is in fact also a string that can be replaced by a numeric int referring to the same actor value - using the string 'Health' just makes it so you don't have to look up that int. What we'll be talking about here mostly are formatted strings, however, not those game setting or actor value ones. If you've ever used functions like DebugPrint, Printc, or MessageEX, (and if you're reading this, I have to assume you have because it's standard practice even on a basic level) you've used formatted strings to tell the game what to display: DebugPrint "I'm a string" Printc "I'm a string" MessageEX "I'm a string" What's in between those quotation marks is a formatted string, because you've used the double quotation marks to tell the script compiler it's a formatted string. Without that, it would complain that 'I'm' is not a valid argument to the function. (Because double quotation marks are used to mark strings, I'll use single ones to quote things in this tutorial, to avoid confusion. And I'll just use 'string' for 'formatted string' from now on.) The problem with strings, from a scripting POV, is that vanilla doesn't leave us many options to manipulate them through script. That is especially true for game setting strings and actor value strings. Formatted strings at least have the advantage that vanilla allows us to use a few format specifiers on them, to pass the value of a float variable to the string itself: if 'fSomeFloat' is 3.4, then DebugPrint "The number is %.0f" fSomeFloat --> will display 'The number is 3' DebugPrint "the number is %g" fSomeFloat --> will display 'The number is 3' DebugPrint "The number is %.2f" fSomeFloat --> will display 'The number is 3.40' DebugPrint "The number is %2.2f" fSomeFloat --> will display 'The number is 3.40' and NVSE added some more that we've been using a long time: if rRef is DocMitchellRef: DebugPrint "The object's name is %n" rRef --> will display 'The object's name is Doc Mitchell' DebugPrint "The ref's formid is %i" rRef --> will display 'The ref's formid is 00104C0C' DebugPrint "%ps is scratching %pp balls" rRef rRef --> will display 'He's scratching his balls.' MessageEX "Press %k to scratch your own" iInt --> will display 'Press M to scratch your own', if iInt is 50, because that's the directxinput scan code for the M key MessageEX "Press sUActnActivate to activate" --> will display "Press E to activate" if that's the key you use for activating stuff, because sUActnActivate displays the key name for what's used for the activate control, see the control codes So far so good, but we still couldn't store those strings anywhere or edit them through script other than by entirely retyping them. That's where string vars come in. 2. STRING VARIABLE BASICS: DECLARING AND INITIALIZING Just like a reference variable refers to a reference or form, and a float variable refers to a floating point number, string variables refer to a string. Duh, you might say, but it's important to remember that they themselves are not strings, which is why I make it a point not to use the word 'string' when I mean 'string var'. We already had strings, string vars to store and manipulate them are what's new. The actual strings that the string vars refer to are stored in the .nvse file that corresponds to your save file. You declare a string variable like this: string_var somestringvariablename (A note about naming conventions: most scripters prefix their variable names like float fSomeFloat, ref rSomeForm to remember what they really refer to. Some do the same with string vars and use the 's' prefix (sSomeStringVar) but I'd advise against that, because there are hundreds of string-based game settings that use that same convention and you're bound to have conflicts. Personally, I use the 'sv_' prefix a lot, or none at all. Of course, there is the possibility of a conflict with the string var functions, which also have that prefix, but there are only a handful of them. Anyway, your choice, just watch out.) Declaring a string var doesn't mean it actually refers to a string yet, it's the same with them as with other script variables: if you don't bother to set a ref var to something, it refers to nothing at all, and if you perform a function on nothing at all, you get squat or even crash. Making a string var refer to a string is called 'initializing' it, and you do this by either: - 'letting' it to a formatted string, - 'letting' it to a string var, or - 'letting' it to a function that is supposed to return a string (including UDFs that return a string or stringvar with SetFunctionValue). let sv_mystringvar1 := "I'm a string" ; compare with: let rRefVar := DocMitchellRef let sv_mystringvar2 := sv_mystringvar1 ; compare with: let rRefVar2 := rRefVar1 let sv_mystringvar3 := player.GetName ; compare with: let rRefVar := GetActionRef You can also still use the format specifiers when initializing, although this has you revert to an antiquated way of initializing strings by using sv_construct: let sv_mystringvar := sv_construct "I'm string number %.0f" someInt let rForm := Pencil01 let sv_mystringvar := sv_construct "Got a %n?" rForm (Note that for passing numbers and names of things into strings, it's often much easier to just use ToString, see the chapter on that.) If a string var is not initialized it returns the numeric value 0. That's the only time string vars and numbers can mix: never set a string var to a number, never set a float var to a string var or a string. In order to check if a string var is initialized or not before you use it for something, you can go: if sv_mystringvar ; do stuff with your initialized string var else ; you haven't initialized endif if eval !(sv_mystringvar) ; ! means LogicalNot ; you haven't initialized endif Of course, if you know you've initialized it, there's no point. 3. 'LET' V. 'SET' SYNTAX String vars were introduced to OBSE right before the 'let' and 'if eval' syntax was introduced, so keep in mind that most of the explanations in the obse docs about them are still based on the pre-'let' era. Before you could simply go 'let sv_mystringvar := "I'm a string"' you had to go 'set sv_mystringvar to sv_construct "I'm a string"'. With 'let', the sv_construct function is entirely unnecessary, and to keep things straightforward I will never mention "set" equivalents to ways of doing things if there is no need for them. Use "let", seriously. The main reason I'm frowning on the 'set' way is that if you 'set' a string var to another they actually refer to the same string, and changing one of them will automatically change the other one too: script console readout string_var sv_string1 string_var sv_string2 set sv_string1 to sv_construct "I'm string 1" printc "string1 says '%z'" sv_string1 --> string1 says 'I'm string 1' set sv_string2 to sv_string1 printc "string2 says '%z'" sv_string2 --> string2 says 'I'm string 1' set sv_string1 to sv_construct "I'm a different string now" printc "string1 says '%z'" sv_string1 --> string1 says 'I'm a different string now' printc "string2 says '%z'" sv_string2 --> string2 says 'I'm a different string now' set sv_string2 to sv_construct "In that case, I'm changing too" printc "string1 says '%z'" sv_string1 --> string1 says 'In that case, I'm changing too' printc "string2 says '%z'" sv_string2 --> string2 says 'In that case, I'm changing too' and in order to make sure that didn't happen, you had to this to make sv_string2 refer to a copy of sv_string1's string, rather than the same one: set sv_string2 to sv_construct "%z" sv_string1 and that was pretty much the only way of 'cleanly' copying a string var's contents to another. When you 'let' a string var to another one, however, it will refer to a copy of the first's string rightaway, and if you later change something about either of them, the other one will remain intact: script console readout string_var sv_string1 string_var sv_string2 let sv_string1 := "I'm string 1" printc "string1 says '%z'" sv_string1 --> string1 says 'I'm string 1' let sv_string2 := sv_string1 printc "string2 says '%z'" sv_string2 --> string2 says 'I'm string 1' let sv_string1 := "I'm a different string now" printc "string1 says '%z'" sv_string1 --> string1 says 'I'm a different string now' printc "string2 says '%z'" sv_string2 --> string2 says 'I'm string 1' let sv_string2 := "In that case, I'm changing too" printc "string1 says '%z'" sv_string1 --> string1 says 'I'm a different string now' printc "string2 says '%z'" sv_string2 --> string2 says 'In that case, I'm changing too' In my view, the 'set' way not only involves more typing, but is more dangerous, in that people may accidentally lose the contents of a string that they didn't mean to overwrite. And let's face it: why have 2 string vars around that'll always be the exact same thing? You're much more likely to want to copy one and then change one of the 2, than to want to have duplicates of the same thing all the time. The same difference applies, btw, if you 'set' or 'let' string vars to the values returned by functions like GetName, increasing the chance of accidental fuck-ups with 'set'. 4. DESTROYING STRING VARS With regular vanilla script variables (ref float int short long), you don't need to worry about them after you've used them. If you stuck them in a quest script, you meant to keep them around indefinitely until your mod's uninstalled; if they're part of an object or spell script that doesn't run anymore, they simply disappear from view - you don't need to erase them. It's different with string variables, whose strings keep lingering in the .nvse files even if the script they were in doesn't run anymore. They are forcibly removed when your mod is uninstalled, but in the meantime they cause a bit of bloat, so be sure to remove/uninitialize/destroy them yourself as soon as you don't need them anymore by going sv_destruct MyStringVar You can destroy up to 10 different ones in the same line like that: sv_destruct stringvar1 stringvar2 stringvar3 stringvar4 stringvar5 stringvar6 stringvar7 stringvar8 stringvar9 stringvar10 As you may remember from the UDF tutorial, string vars are also the only UDF local variables that you need to destruct yourself. 5. CHECKING STRING VARS With 'if eval' syntax, checking whether a string var is the same as another, or the same as a string, is as simple as if eval sv_stringvar == "some string" if eval sv_stringvar1 == sv_stringvar2 and you can use != too for inequality. Before that, you had to check the int value returned by the sv_compare function if you wanted to compare a string var to a string: if 0 == sv_compare "some string" FormatSpecVars sv_stringvar bCaseSensitiveComparebool ; they are the same elseif 1 == sv_compare "some string" FormatSpecVars sv_stringvar bCaseSensitiveComparebool ; the string var's string occurs before the string, alphabetically speaking elseif -1 == sv_compare "some string" FormatSpecVars sv_stringvar bCaseSensitiveComparebool ; the string var's string occurs after the string, alphabetically speaking elseif -2 == sv_compare "some string" FormatSpecVars sv_stringvar bCaseSensitiveComparebool ; none of the above, the compare 'fails' endif Not much point in using sv_compare, now that we have the "if eval" alternative, except if you're really interested in the alphabetical comparison. These comparisons are case-insensitive, unless you use that bool with sv_compare. 6. PASSING STRING VARS AS PARAMETERS: TOSTRING ($) If a vanilla or NVSE function takes one or more formatted strings as parameters DebugPrint "I'm a string" MessageEX "I'm a string" SetModelPathEX "Clutter\Junk\WhetStone.NIF" NX_SetEVFl "some nx key string" someFloat NX_SetEVSt "some nx key string" "some string value" you can force them to take a string variable as parameter instead with the ToString function, in short: $ DebugPrint $sv_stringvar MessageEX $sv_stringvar SetModelPathEX $sv_stringvar NX_SetEVFl $sv_keystringvar someFloat NX_SetEVSt $sv_keystringvar $sv_valuestringvar There are a few functions, especially fallout-specific ones and ones that are injected with NVSE plugins like MCM, that may not seem to work with ToString + string var that way - this is because they probably haven't been prepped for that. If you think this is the case in your script, try using the script compiler override, which should force it. The ToString function can also return a string representation of numbers and forms, ie pass them to a string: It will return the name of a form if the form has a name: let rRef := DocMitchellRef let sv_name := $rRef printc "%z" sv_name --> displays 'Doc Mitchell' Or its hex FormID if it doesn't: let rRef := BlackBoardMarker let sv_name := $rRef printc "%z" sv_name --> displays '00002FCC' Numbers: let iInt := 32 let sv_name := $iInt printc "%z" sv_name --> displays '32' 7. CONCATENATING STRING VARS, OR: ADDING THEM UP Easy : + let sv_stringvar := "String1" + "String2" printc "%z" sv_stringvar --> reads: 'String1String2' let sv_stringvar := Player.GetName + "'s chair" rChairRef.SetName $sv_stringvar --> reads 'Prudencia's chair' because that's my test char's name note: names are usually defined on the base form, and setting them will set them on the base form too When adding strings up, you may run into some limitations of passing variables into strings with format specifiers in the context of an addition: let sv_stringvar := "First Part:" + playerref.GetName + ":" + "%.0f" iSomeInt let sv_stringvar := "First Part:" + "%n" rSomeForm will make warnings pop up and not compile. Luckily ToString is there, once again saving the day a lot of times: let sv_stringvar := "First Part:" + playerref.GetName + ":" + $iSomeInt let sv_stringvar := "First Part:" + $rSomeForm will be fine. And if you can't do it in one line and can't use ToString, sometimes you just gotta do it in two or involve sv_construct again: let sv_stringvar := sv_construct " is scratching %pp balls" rSomeRef ; had to add the sv_construct there to make the %pp specifier work let sv_stringvar := $rSomeRef + $sv_stringvar And you can also get some traction out of the sv_Insert function: sv_insert "Some String" FormatSpecifierVars String_VarToInsertTheStringIn PositionToInsertAtInt let sv_stringvar := " balls must itch something fierce" sv_insert "%pp" rSomeForm sv_stringvar 0 or sv_insert "%pp" rSomeform sv_stringvar ; we want to insert it at the very first character of the stringvar's string, which is position 0 (like with formlists and arrays) ;if the position is 0 we can leave out that parameter, really 8. MEASURING AND SEARCHING YOUR STRING VARS sv_Length returns the length of a string var's string: let iSomeInt := sv_length sv_stringvar Since the start position is always indexed at 0, the end position will always be (sv_length sv_stringvar) - 1. sv_Find finds the first occurrence of a substring in a string, and returns the position as an int: let iSomeInt := sv_Find "substring" FormatSpecifierVars SourceStringVar StartPositionInt SearchLengthFromStartPosInt CaseSensitiveSearchBool ; leave out the parameters if you don't need them let sv_stringvar := "This is example 3" let iSomeInt1 := sv_Find "example %.0f" someInt2 sv_stringvar ; --> will return 8 if someInt2 is 3 sv_Count returns just how many instances of a substring a string contains: let sv_stringvar := "Ain't no sunshine when she's gone. And she's always gone too long." let iSomeInt := sv_Count "gone" sv_stringvar --> returns 2 9. BREAKING STRING VARS DOWN, REPLACING BITS, AND REASSEMBLING THEM The most straightforward way of taking chunks out of a string var's string is by using sv_Erase: sv_erase stringvar StartPositionInt NumberofCharacterstoEraseInt ; StartPositionInt: if you leave that out we'll start at position 0 ; NumberofCharacterstoEraseInt: if you leave that out we erase everything from the start position to the end scn CensorshipScpt let sv_stringvar := "I fucked up." sv_erase sv_stringvar 2 6 --> 'I up.' sv_insert "messed" sv_stringvar 2 Or you can replace a chunk of a string with another with sv_Replace: sv_replace "texttoreplace|texttoreplacewith" FormatSpecVars SourceStringVar StartPositionInt SearchLengthFromStartPosInt CaseSensitiveBool NumberofOccurrencestoReplaceInt ; don't use what you don't need sv_replace "fucked|messed" sv_stringvar bit more complicated version: scn Variety let sv_stringvar := "That motherfucking fuckhead fuckin' fucked me over. Fuck!" sv_replace "fuck|dick" sv_stringvar 19 38 1 1 The only 'fuck' that is replaced there is in 'fuckhead', and the same goes for sv_replace "fuck|dick" sv_stringvar 19 4 If elements of a string are separated by a common character, like a space or backslash, sv_Split can split them up for you and return them as string values to an array: let sv_stringvar := "This is a sentence." let somearray := sv_Split sv_stringvar " " ; the 'delimiter', what separates the string, is the 'space' that I stuck in that format string, you can have several characters be delimiters at once and then ar_dump somearray will return [ 0.000000 ] : This [ 1.000000 ] : is [ 2.000000 ] : a [ 3.000000 ] : sentence. Each of those values being strings. Example 1: Let's say you have clothing meshes for a particular body corresponding to some vanilla ones, and want to switch them out in-game through script: if vanilla's have filepaths like these: armor\LegateArmor\LegateArmor.NIF and yours have filepaths like these: armor\MyModFolderName\LegateArmor\LegateArmor.NIF then you'd go about it like this: let sv_filepath := GetBiPedModelPath 0 ArmorLegate --> 'armor\LegateArmor\LegateArmor.NIF' let ar_paths := sv_Split sv_filepath "\" --> ar_paths[0] is "armor", ar_paths[1] is "LegateArmor", ar_paths[2] is "LegateArmor.Nif" let sv_newfilepath := ar_paths[0] + "\MyModFolderName\" + ar_paths[1] + "\" + ar_paths[2] ; you'll probably need the compiler override on to add that up SetBiPedModelPathEX $sv_newfilepath 0 ArmorLegate Note that I used the 0 parameter to get the male mesh, and the 'object' calling convention rather than the reference calling convention (ArmorRef.GetBiPedModelPath 0), so that code switches out the filepaths for the base form. Maybe get in a TempCloneForm or something in between. You may notice that at no point do I really need to know the specific filepaths, just that I need to insert a foldername after the first section ('armor'), so ArmorLegate can just as easily be a ref var holding whichever armor retrieved by GetEquippedObject or some such, as long as the new filepath is otherwise the same. Example 2: spend some time in the fallout sections on LL and you're bound to run into mentioning of NX vars, which take a format string for the function key: let fSomeFloat := rSomeRef.NX_GetEVfl "Some key string" rSomeRef.NX_SetEVfl "Some key string" fSomeFloatValue Those keys being format strings, you can use the ToString symbol to force the nx functions to take a string variable as a key: let fSomeFloat := rSomeRef.NX_GetEVFl $somestringvar You'll probably build your keys like "MyModNameString:SomeKeyString:SomeOptionalSubKeyString" (and those colons would be a good delimiter for sv_split), but of course, typing is a drag, and sometimes you want to build nx keys dynamically, on the fly, without knowing how many you need. Let's say we want to keep tabs on the specific combination of clothing items we have equipped, in order to store the combo: let iNum := -1 while (iNum += 1) < 20 ; check equipment slots 0-19 let rForm := playerref.GetEquippedObject iNum if rForm let sv_keystring := "OutfitMod:OutfitSet:" + "1:" + "Slot:" + $iNum playerref.NX_SetEVFo $sv_keystring rForm endif loop and equipping that later on would be something similar: let iNum := -1 while (iNum += 1) < 20 let sv_keystring := "OutfitMod:OutfitSet:" + "1:" + "Slot:" + $iNum let rForm := playerref.NX_GetEVfo $sv_keystring if rForm if playerref.getitemcount rForm playerref.EquipItem rForm endif endif loop And I could have different sets if I replaced this: let sv_keystring := "OutfitMod:OutfitSet:" + "1:" with let sv_keystring := "OutfitMod:OutfitSet:" + $someInt + ":" Depending on what you're wearing, and how many slots each item has flagged, you can have any number of those EVFo variables set on your character. The fact that you just don't know and would otherwise have to type that out in x elseif conditions, makes this another prime example of why string vars are so useful, being editable. Here's hoping someone revises MCM to use some of that, right? 10. SWITCHING BETWEEN STRING AND OTHER REPRESENTATIONS, CHARACTER FUNCTIONS I'm not spending too much time on these, this has gotten pretty lengthy already I'm just pooling them together here because they're all over the place in obse docs. Just a few basics: you know the ToString function by now, and it's turned out to be pretty handy. It has a ToNumber counterpart (short: #), which will try to pass a number that's in a string to a float/int: let sv_somestringvar := "34.5" let fFloat := ToNumber sv_somestringvar let fFloat := #sv_somestringvar --> fFloat will be 34.5 It can also do this with hex numbers kept in a string if you add the hex bool parameter, or if the string started with "0x", which can catch a dynamically created ref. let sv_somestringvar := "00000ADE" let fFloat := ToNumber sv_somestringvar 1 NumToHex, in turn, converts an int into a hex string, with a default width of 8 characters, although you can specify that as a parameter: let iInt := 2782 let sv_somestringvar := NumToHex iInt --> "00000ADE" let sv_somestringvar := NumToHex iInt 4 --> "0ADE" To get some operations done at the level of single characters, you need to pass a character from a string to its ascii code, which you can do with sv_GetChar: let iSomeInt := "somestring" somePosInt let iSomeInt := sv_GetChar "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ.,:" 4 --> will return 53, which is the ascii code for 5, at pos 4 in our string If our string is a single character, we can just use CharToAscii let sv_somestringvar := "5" let iSomeInt := CharToAsci sv_somestringvar --> again, 53 Once passed to its ASCII code, you can check the type of the character with the boolean IsDigit, IsLetter, IsPunctuation and IsPrintable functions. You can also switch between upper and lower case with ToUpper and ToLower. And then return that ASCII code int back to a string, with ASCIIToChar: let sv_SomeStringVar := AsciiTochar 53 --> "5" let iSomeInt := ToLower 65 ; 65 is A --> iSomeINt = 141 let sv_SomeStringVar := AsciiToChar iSomeInt --> "a" If you want to perform such functions and checks on each character in a string, rather than one you retrieve from its position with sv_GetChar, you can use a foreach loop, which passes each character to a string var, the iterator, that holds it for the duration of the loop body: foreach sv_iterator <- "SomeString" ; or: foreach sv_iterator <- somestringvar ; do stuff to sv_iterator, which will contain one character for each loop run going from position 0 to end loop The "Break" and "continue" commands, which you might remember from while loops, apply here too. See the array var tutorial for more information on foreach loops. And now that we're talking int codes anyway, GetKeyName (aka GetKey) will return the string for a keyboard key if you pass the directx scan code as an int parameter: let sv_somestringvar := GetKeyName 1 --> "Escape" which I suppose can be handy for different localizations, or for situations where the %k format specifier doesn't suffice. And that's it. Trust me, you'll rarely need to use all of these functions, just be sure to be clear on the basics, and know what your options are.
- 61 replies
-
- string
- string var
-
(and 3 more)
Tagged with: