Scripting Tutorial: Working with FormLists

From GECK
Jump to: navigation, search

IMPORTANT

You will need Fallout Script Extender (FOSE) or NVSE for this tutorial.

Introduction

With the development of the GECK came several new object types and functions that were not seen in previous versions of Bethesda's game construction kits. One of the new object types that was added is called a FormList. A FormList is, naturally, a list of FormIDs of objects from the editor.

FormLists were originally created to serve a variety of purposes (See FormList). The creators of FOSE developed a handful of new functions that allow easy script manipulation of FormLists. These functions made FormLists much more powerful, giving them the all the functionalities of the List programming construct found in almost all high level programming languages.

In this tutorial, you may see forms described as base forms and reference forms. The difference between the two is that base forms are the object templates from the editor (like a class in C++) and reference forms are instances of these object templates (like an instance of a class). Both base forms and reference forms contain a FormID, so most list functions will not discriminate between the two. However, some list functions may only accept one type as a parameter. See the individual function definitions for more information.

FormList Functions

This table lists the FormList functions currently available to modders:

From the GECK From FOSE
AddFormToFormList ListAddForm
IsInList ListAddReference
IsWeaponInList ListRemoveForm
ListRemoveNthForm
ListReplaceForm
ListReplaceNthForm
ListGetNthForm
ListGetFormIndex
ListGetCount

Modifying FormLists

The following sections detail some common list operations you might want to perform on your FormLists.

Creating a List

  • FormLists must be created in the GECK, as there are no functions at this time to generate them from scripts.
  1. Navigate in the Object Window to Miscellaneous -> Form List.
  2. Right click, select New.
  3. Enter an ID for your FormList.

Adding Forms to a List

  • You can add base forms to a FormList in the GECK by dragging and dropping them into the FormList window.
  • You can use the ListAddForm function.
  • You can use the ListAddReference function.
  • You can use the AddFormToFormList function, although it is recommended to use ListAddForm instead since the FOSE function names are more standardized.

Removing Forms from a List

  • You can remove forms from a FormList in the GECK by selecting a form and pressing the 'Delete' key.
  • You can use the ListRemoveForm function.
  • You can use the ListRemoveNthForm function.

Replacing Forms in a List

Getting Forms from a List

Getting List Information

  • You can view the entries of a FormList in the GECK in the FormList window. In the first column is the index of the entry. In the second column is the type of form (references are REFR). In the third column is the EditorID. In the fourth column is the NumericID.
  • You can use the IsInList function.
  • You can use the ListGetFormIndex function.
  • You can use the ListGetCount function.

Using Lists to Create Floating Text

This example goes beyond what a casual modder might attempt to accomplish, but it displays the abilities of lists quite well.

Suppose we have added 27 textured meshes into the GECK, corresponding to the letters of the English alphabet plus a space character. These meshes are added as forms (e.g. we have objects in the editor letterA, letterB, etc.). Now suppose we want to use these to display a list of names on a sign board in Fallout 3.

We have determined:

  • The first letter of the first name should be positioned at (1000,1000,1000) in our cell with angle positions of (0,0,0).
  • The offset to the next letter is (0,5,0), meaning if the first letter of a name is at x=1000,y=1000,z=1000 in a cell, the next letter should be at x=1000,y=1005,z=1000.
  • The offset to the next name is (0,0,10), meaning if the first letter of the first name is at x=1000,y=1000,z=1000 in a cell, the first letter of the second name should be at x=1000,y=1000,z=1010.
  • Our names are (courtesy of FF7):
  1. Cloud
  2. Barret
  3. Tifa
  4. Aeris
  5. Red XIII
  6. Yuffie
  7. Vincent
  8. Cait Sith
  9. Cid

Let us create a FormList for each name that contains the letters of that name. The name of each list will be nameX where X is the name (e.g. nameCloud). If we try dragging and dropping B, a, r, r, e, t into a FormList for the name Barret, we will not be able to add the second r because dragging and dropping allows us only to add base forms to a FormList once, since they have the same FormID. So, we must add the letters as references via a script using ListAddReference OR by placing the letters into the world in a cell somewhere and dragging their references from the Cell View window. For this example, lets assume we've added them to a test cell somewhere so we can use MoveTo to relocate them to their destinations. We will add the letters so that the first letter of each name is at index 0.

Inefficient Script

If we wish to print these names into the world somewhere, we can use the Label/GoTo loop, one loop for each FormList of letters:

 scn floatingNamesScript
 ;vars (not listed for brevity)
 Begin GameMode
   ;set first letter's position
   set firstLetterPosX to 1000
   set firstLetterPosY to 1000
   set firstLetterPosZ to 1000
   ;initialize current letter's position
   set letterPosX to firstLetterPosX
   set letterPosY to firstLetterPosY
   set letterPosZ to firstLetterPosZ
   ;distance to the next letter
   set letterOffsetX to 0
   set letterOffsetY to 5
   set letterOffsetZ to 0
   ;distance to the next name
   set nameOffsetX to 0
   set nameOffsetY to 0
   set nameOffsetZ to 10    
   ;PRINT FIRST NAME
   set letterList to nameCloud
   Label 1
     set currentLetter to ListGetNthForm letterList count1
     currentLetter.MoveTo Player
     currentLetter.SetPos X letterPosX
     currentLetter.SetPos Y letterPosY
     currentLetter.SetPos Z letterPosZ
     ;add offsets for next letter
     set letterPosX to letterPosX + letterOffsetX
     set letterPosY to letterPosY + letterOffsetY
     set letterPosZ to letterPosZ + letterOffsetZ
     set count1 to count1 + 1
   if count1 < ListGetCount letterList
     GoTo 1
   endif
   ;set position of next name's first letter
   set letterPosX to firstLetterPosX
   set letterPosY to firstLetterPosY
   set letterPosZ to firstLetterPosZ
   set letterPosX to letterPosX + nameOffsetX
   set letterPosY to letterPosY + nameOffsetY
   set letterPosZ to letterPosZ + nameOffsetZ
   ;PRINT SECOND NAME
   set letterList to nameBarret
   Label 2
     set currentLetter to ListGetNthForm letterList count2
     currentLetter.MoveTo Player
     currentLetter.SetPos X letterPosX
     currentLetter.SetPos Y letterPosY
     currentLetter.SetPos Z letterPosZ
     ;add offsets for next letter
     set letterPosX to letterPosX + letterOffsetX
     set letterPosY to letterPosY + letterOffsetY
     set letterPosZ to letterPosZ + letterOffsetZ
     set count2 to count2 + 1
   if count2 < ListGetCount letterList
     GoTo 2
   endif
   (etc)
 End

The script was cut short because it is a bit tedious, since we have 9 names to print. The script could also quickly get long if we put more instructions into each loop, like angle and scale information.

Lists of Lists

As it would seem, a FormList is also an object in the editor, which has its own FormID. That makes a FormList a Form. What this means is that FormLists can contain other FormLists. Lets use this to our advantage by making another FormList named namesList. Now we will drag and drop all 9 of our name FormLists into this new FormList.

Efficient Script

Our script just got a whole lot shorter:

 scn floatingNamesScript
 ;vars (not listed for brevity)
 Begin GameMode
   ;set first letter's position
   set firstLetterPosX to 1000
   set firstLetterPosY to 1000
   set firstLetterPosZ to 1000
   ;initialize current letter's position
   set letterPosX to firstLetterPosX
   set letterPosY to firstLetterPosY
   set letterPosZ to firstLetterPosZ
   ;distance to the next letter
   set letterOffsetX to 0
   set letterOffsetY to 5
   set letterOffsetZ to 0
   ;distance to the next name
   set nameOffsetX to 0
   set nameOffsetY to 0
   set nameOffsetZ to 10    
   ;PRINT NAMES
   Label 1
     set letterList to ListGetNthForm namesList countNames
     set countLetters to 0
     Label 2
       set currentLetter to ListGetNthForm letterList countLetters
       currentLetter.MoveTo Player
       currentLetter.SetPos X letterPosX
       currentLetter.SetPos Y letterPosY
       currentLetter.SetPos Z letterPosZ
       ;add offsets for next letter
       set letterPosX to letterPosX + letterOffsetX
       set letterPosY to letterPosY + letterOffsetY
       set letterPosZ to letterPosZ + letterOffsetZ
       set countLetters to countLetters + 1
     if countLetters < ListGetCount letterList
       GoTo 2
     endif
     set countNames to countNames + 1
     ;set position of next name's first letter
     set letterPosX to firstLetterPosX + nameOffsetX * countNames
     set letterPosY to firstLetterPosY + nameOffsetY * countNames
     set letterPosZ to firstLetterPosZ + nameOffsetZ * countNames
   if countNames < ListGetCount namesList
     GoTo 1
   endif
 End

That was the entire script to print all 9 names in a cell. If these initial positions are changed, this script can print these names anywhere in the game world. If we add more names or want to change the names, this can easily be done in the editor without modifying the script.

The moral of the story is: there is a lot you can accomplish by working with FormLists.