Jump to content

Tutorial: NVSE4+ part 3: String variables

Recommended Posts

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. :)


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.







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
 ; you haven't initialized

if eval !(sv_mystringvar)   ; ! means LogicalNot
 ; you haven't initialized

Of course, if you know you've initialized it, there's no point.






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'.



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.




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'

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.





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'


let iInt := 32
let sv_name := $iInt
printc "%z" sv_name            --> displays '32'


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  
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


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


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

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

And I could have different sets if I replaced this:

let sv_keystring := "OutfitMod:OutfitSet:" + "1:"


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?







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

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.

Link to comment

I wish :)


Although for Notes, Terminal text, and even Messages/MessageBoxes in the object window, that's probably a case of coming up with functions to do whatever EX does with other functions like MessageBoxEX and the like. Dialog... I don't think that can really work.

Link to comment

I just remembered, it's not quite the same but the Enhanced Economy mod for Oblivion does set up quests dynamically, and the quest giver's text is therefor also dynamically created, but that's done with a messagebox that's superimposed over the dialog window, not dialog itself.

Link to comment
  • 1 month later...
  • 2 months later...

You don't need to sv_Destruct strings that are stored in arrays, right?


This is enough?:


let aArray := ar_List "the", "cat", "sat", "on", "the", "mat"


let aArray := ar_Null


Yeah, as far as I can tell sv_destruct is only for strings stored in string vars.


Link to comment
Guest tomm434

Is there any way to use a string variable in notes and dialogues like you can use &PCName?


You can always do it manually and have fun with dialogues.

String_var PlayerName

Begin Function {}

let PlayerName := player.GetName
if eval PlayerName == "Courier"
set aaalunetteref.PlayerName to 1
printC "Player name is Courier"
elseif eval PlayerName == "Dumbass"
set aaalunetteref.PlayerName to 2
printC "Player name is Dumbass"
PrintC "player name is not in list"

Link to comment
Guest tomm434

Doctasax, Why can't I complile string? I followed your tutorial.

let rForm := Pencil01
let sv_mystringvar := "Got a %n?" rForm

My code is

ref Cell
String_var CellString

Begin gamemode
let Cell :=Player.GetParentCell
PrintC "%n" Cell
PrintC "%i" Cell
let CellString := "%i" Cell
PrintC "%z" CellString

I want to  put ID of the cell into string (for exterior cells) and GECK says that "let CellString := "%i" Cell" line has more args that needed. I can't use $Cell because every cell has a name.

Link to comment

Ah, maybe I just supposed that'll work because it did during the early days when I used sv_construct (too much)


Try either:


let CellString := ("%i" Cell)




let CellString := sv_construct "%i" Cell


In fact, why don't you try both ;)

Link to comment
Guest tomm434

Ah, maybe I just supposed that'll work because it did during the early days when I used sv_construct (too much)


Try either:


let CellString := ("%i" Cell)




let CellString := sv_construct "%i" Cell


In fact, why don't you try both ;)


First one compilies but breaks the script

Seconds one works flawlessly, thanks!

Link to comment
  • 3 weeks later...

Just an fyi for those of you looking at sv_replace.


The first argument must be a string literal or a $-prefixed string var, it can't be a raw string var.


string_var svLeft
string_var svRight
string_var svMerge
string_var svTarget

; ...
let svLeft   := "foo"
let svRight  := "bar"
let svMerge  := svLeft + "|" + svRight

; Example 1, works
let svTarget := "my foo thing"
sv_replace "foo|bar" svTarget
; svTarget now == 'my bar thing'

; Example 2, works
let svTarget := "my foo thing"
sv_replace $svMerge svTarget
; svTarget now == 'my bar thing'

; Example 3, does NOT work
let svTarget := "my foo thing"
sv_replace svMerge svTarget
; svTarget unchanged
Just FYI.
Link to comment

And I was sure that I know a lot about strings, lol =D

Thanks again for one more great tutorial, found a lot of extremely useful information.

And a little question. Trying to add ascii codes of the each character into an array with "ForEach", and no cigar. Characters works fine, but when I try to make them an ascii - something is going bad.

string_var sv_SomeChar

        Let Array1 := Ar_Construct Array
        ForEach sv_iterator <- $Targetref
        Let sv_SomeChar := CharToAscii sv_iterator
        Ar_Append Array1 sv_SomeChar
        Ar_Dump Array1

Array have empty elements. Something like this

[ 0.000000 ] :
[ 1.000000 ] :
[ 2.000000 ] :
[ 3.000000 ] :

Tried to use integer

int iSomeChar

        Let Array1 := Ar_Construct Array
        ForEach sv_iterator <- $Targetref
        Let iSomeChar := CharToAscii sv_iterator
        Ar_Append Array1 iSomeChar
        Ar_Dump Array1


[ 0.000000 ] : -1
[ 1.000000 ] : -1
[ 2.000000 ] : -1
[ 3.000000 ] : -1

and even "ToNumber" didn't help:

string_var sv_SomeChar

        Let Array1 := Ar_Construct Array
        ForEach sv_iterator <- $Targetref
        Let sv_SomeChar := CharToAscii sv_iterator
        Ar_Append Array1 #sv_SomeChar
        Ar_Dump Array1
[ 0.000000 ] : 0
[ 1.000000 ] : 0
[ 2.000000 ] : 0
[ 3.000000 ] : 0

What exactly I'm doing wrong?





this code will do the job, but still don't understand why CharToAscii didn't =\

        Let Array1 := Ar_Construct Array
        ForEach sv_iterator <- $Targetref
        set iSomeInt to sv_GetChar sv_iterator 0
        Ar_Append Array1 iSomeInt
        Ar_Dump Array1
Link to comment

You can do foreach on strings? I don't think I've seen that documented anywhere, but makes sense. Awesome.


I think this is why it doesn't work (note: this is kind of a logical guess):


CharToAscii only works on a single char.


ForEach sv_iterator <- $Targetref
Let sv_SomeChar := CharToAscii sv_iterator



sv_iterator is not a single char, it is a string containing both key and value.


When you do this:

sv_GetChar sv_iterator 0


You get a single char (the first, aka 0) from the string, so it works.



(@DoctaSax: I got around to translating this tutorial to the GECK wiki, haven't done the others yet)

Link to comment


sv_iterator is not a single char, it is a string containing both key and value.


Yeah, it does make sense. I was tricked with single characters in array, and I thought that I can charToAscii before appending char into an array. Thank you for explanation =)

Link to comment

Actually, I don't think a foreach loop in a string or string var bothers with "key" and "value" - that's just specific to arrays.

What's going on there, I feel, is that sv_getchar is rigged to accept a string var as parameter - being an sv function after all - while chartoascii isn't (obse docs have it as accepting 'strings', not string vars) and so probably needs ToString in front of the string var:

Let iSomeChar := CharToAscii $sv_iterator


sv_GetChar is marked as deprecated in obse docs, because we're supposed to just as easily do

let char := str[pos]

and then, I guess, use chartoascii on "char" if my hunch about tostring is correct.

In this case, I like the depecrated function better, doing both in one line and sparing an extra string var.


The use of bracket notation on string vars is something that passed me by a bit. Someone adventurous may wanna test what it can do in terms of adding to & replacing characters in strings.

Link to comment

Wow, so I can skip all the array and foreach stuff just with str[pos]? o_O This is so awesome! I need to test it right now. Soon my script will be like "begin, 2 lines of code that do all the facny stuff, end" =D Thank you good people for helping me^^

Link to comment
  • 3 weeks later...
Guest tomm434

I got to ask - if I have string variable as function parameter  -do I have to destroy it manually?

String_var SexType

Begin function{SexType}

player.NX_SetEVFl "Sexout:Start::CallVer" 1

if eval SexType == "Oral"
player.NX_SetEVFo "Sexout:Start::ActorA" Raider
player.NX_SetEVFo "Sexout:Start::ActorB" playerREF
player.NX_SetEVFl "Sexout:Start::isOral" 1
player.NX_SetEVFo "Sexout:Start::CBDialogA" aaaraidersOralafter


This code executes often.

Do I have to destroy  "SexType" every time?

Link to comment
Guest tomm434

How exactly can I do that?


The code from calling script is

call aaraiderFunctionSex Oral

And this string is only present in function.

Link to comment

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...