Jump to content

Possible to enumerate a mods added items via script?


RoyBatterian

Recommended Posts

Well, five hours later I have a working NVSE plugin function.

 

The function code:

 

 

bool Cmd_GetLoadedType_Execute(COMMAND_ARGS)
{
    *result = 0;
    int type = 0;
    BGSListForm* list = NULL;
    if(!ExtractArgsEx(EXTRACT_ARGS_EX, &list, &type)) return true;
    if(list) list->list.RemoveAll();
    if(list && type)
    {
        // Some types are handled in specific DataHandler lists.
        if(type == 7) for(tList<TESClass>::Iterator iter = g_data->Get()->classList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), TESClass, TESForm), eListEnd);
        else if(type == 8) for(tList<TESFaction>::Iterator iter = g_data->Get()->factionList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), TESFaction, TESForm), eListEnd);
        else if(type == 9) for(tList<BGSHeadPart>::Iterator iter = g_data->Get()->headPartList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), BGSHeadPart, TESForm), eListEnd);
        else if(type == 10) for(tList<TESHair>::Iterator iter = g_data->Get()->hairList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), TESHair, TESForm), eListEnd);
        else if(type == 11) for(tList<TESEyes>::Iterator iter = g_data->Get()->eyeList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), TESEyes, TESForm), eListEnd);
        else if(type == 12) for(tList<TESRace>::Iterator iter = g_data->Get()->raceList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), TESRace, TESForm), eListEnd);
        else if(type == 13) for(tList<TESSound>::Iterator iter = g_data->Get()->soundList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), TESSound, TESForm), eListEnd);
        else if(type == 19) for(tList<EnchantmentItem>::Iterator iter = g_data->Get()->enchantmentItemList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), EnchantmentItem, TESForm), eListEnd);
        else if(type == 20) for(tList<SpellItem>::Iterator iter = g_data->Get()->spellItemList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), SpellItem, TESForm), eListEnd);
        else if(type == 49) for(tList<BGSNote>::Iterator iter = g_data->Get()->noteList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), BGSNote, TESForm), eListEnd);
        else if(type == 51) for(tList<BGSProjectile>::Iterator iter = g_data->Get()->projectileList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), BGSProjectile, TESForm), eListEnd);
        else if(type == 53) for(tList<TESWeather>::Iterator iter = g_data->Get()->weatherList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), TESWeather, TESForm), eListEnd);
        else if(type == 65) for(tList<TESWorldSpace>::Iterator iter = g_data->Get()->worldSpaceList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), TESWorldSpace, TESForm), eListEnd);
        else if(type == 71) for(tList<TESQuest>::Iterator iter = g_data->Get()->questList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), TESQuest, TESForm), eListEnd);
        else if(type == 81) for(tList<BGSExplosion>::Iterator iter = g_data->Get()->explosionList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), BGSExplosion, TESForm), eListEnd);
        else if(type == 83) for(tList<TESImageSpace>::Iterator iter = g_data->Get()->imageSpaceList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), TESImageSpace, TESForm), eListEnd);
        else if(type == 84) for(tList<TESImageSpaceModifier>::Iterator iter = g_data->Get()->imageSpaceModList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), TESImageSpaceModifier, TESForm), eListEnd);
        else if(type == 85) for(tList<BGSListForm>::Iterator iter = g_data->Get()->listFormList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), BGSListForm, TESForm), eListEnd);
        else if(type == 86) for(tList<BGSPerk>::Iterator iter = g_data->Get()->perkList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), BGSPerk, TESForm), eListEnd);
        else if(type == 91) for(tList<BGSCameraShot>::Iterator iter = g_data->Get()->cameraShotList.Begin(); !iter.End(); ++iter) list->AddAt(DYNAMIC_CAST(iter.Get(), BGSCameraShot, TESForm), eListEnd);
        else
        {
            // Other types are in the objects list.
            BoundObjectListHead* objects = g_data->Get()->boundObjectList;
            TESBoundObject* object = objects->first;
            while(object != objects->last)
            {
                object = object->next;
                if(object->GetTypeID() == type) list->AddAt(DYNAMIC_CAST(object, TESBoundObject, TESForm), eListEnd);
            }
        }
    }
    return true;
}

 

 

 

You pass a form list and a type code, and it fills the list with all items of that type from all loaded plugins.

 

 

Here's the script I tested it with:

short count
short index
ref varref
string_var typefile

begin gamemode

    if getgamerestarted
        set count to 0
        while count < 200
            getloadedtype testList count
            if listgetcount testList
                set index to 0
                let typefile := "typedumps\" + $count + ".txt"
                con_scof $typefile
                while index < listgetcount testList
                    set varref to listgetnthform testList index
                    printc "[%i] %n" varref varref
                    let index += 1
                loop
            endif
            set count to count + 1
        loop
    endif
 
end ; gamemode

Note that iterating through the form list and printing its contents is the longest part of the loop, and the GetLoadedType function is quite fast; type 32 (statics) took a minute to dump 9,686 forms, but there shouldn't be a reason to add all statics to a form list.

 

Here are the resulting text files: http://www.mediafire.com/download/ki4taxaxxk1854i

 

 

Expect to see this added to my NVSE plugin (link in sig).

Link to comment
Share on other sites

Yes but the reason I wondered if it could be done in game via script, was to automate the process of adding the items to containers to be accessed by the player regardless of what load order they were using. While it is certainly less time consuming to use TES/FNV Edit to do such a task, I would still have to create and fill all the containers by hand. It just seems impractical either way to do so in a timely manner. Hand editing would take just as long or longer to do than letting a script run in game to do it, which luthienanarion has already demonstrated takes a completely unreasonable amount of time to execute even with just the base game.

 

Perhaps later on when I have the ability to make plugins for NVSE I could make something which could do so in a reasonable amount of time in game. Of course if someone would like to take the idea who already has the skill and know how to do, then I wouldn't complain about it and actually would be quite happy if they did. Seems like a lot of work though for a "cheat", and I doubt anyone else besides me would actually bother with it.

 

I think this was more an exercise in "what if" :smile:

 

A TES5Edit script can easily fill containers automatically. What are you smoking? One script - a minute - your entire load order - random (or selected) containers filled as per your specifications.

 

Roughly:

unit userscript;
 
uses mteFunctions;
 
var
  slObjects: TStringList;
  slContainers: TStringList;
 
function Initialize: integer;
begin
  slObjects := TStringList.Create;
  slContainers := TStringList.Create;
end;
 
function Process(e: IInterface): integer;
var
  s: string;
begin
  s := Signature(e);
 
  // only process armor, weap, ingr or misc records, because those are the only
  // records that can be added to a container safely
  if (s <> 'ARMO') and (s <> 'WEAP') and (s <> 'INGR') and (s <> 'MISC') then
    exit;
 
  slObjects.AddObject(Name(e), TObject(e));
end;
 
function Finalize: integer;
var
  containerfile, cont, items, item: IInterface;
  i, j: integer;
begin
  containerfile := SelectFile('Select the file you want to use for the new/modified container...');
 
  // terminate script if containerfile not assigned
  if not Assigned(containerfile) then
    exit;

  // build containers list
  cont := GroupBySignature(containerfile, 'CONT');
  if not Assigned(cont) then begin
    Add(containerfile, cont, True);
    cont := GroupBySignature(containerfile, 'CONT');
    Add(cont, 'CONT', True);
  end;
  for i := 0 to ElementCount(cont) - 1 do begin
    slContainers.AddObject(Name(ElementByIndex(cont, i)), TObject(ElementByIndex(cont, i)));
  end;
 
  // some kind of UI for selecting one or more containers to add the items to
  // OptionsForm;
 
  // loop through containers and add all selected items to them.
  for i := 0 to slContainers.Count - 1 do begin
    cont := ObjectToElement(slContainers.Objects[i]);
    for j := 0 to slObjects.Count - 1 do begin
      items := ElementByPath(cont, 'Items');
      if not Assigned(items) then Add(cont, 'Items', True);
      item := ElementAssign(items, HighInteger, nil, false);
      seev(item, 'CNTO\Item', slObjects[j]);
      seev(item, 'CNTO\Count', '1');
    end;
  end;
end;
 
end.

This doesn't have an options form coded yet but I could easily code it when I have a bit more time on my hands. Also see http://www.nexusmods.com/skyrim/mods/49373/?

Edited by matortheeternal
Link to comment
Share on other sites

@luthienanarion: You, are awesome. BTW where to find nvse source?

 

@matortheeternal: Thanks for posting the example, I am still very much a noob with scripting for both the games and fnv/tes edit. That's very useful for me to create chests for a mod of my own which supports my particular load order. The solution which I was seeking however was a generic approach that anyone, modder or not could use with no fuss by making the functionality into a mod that you can just dump in game. luthienanarion seems to have made a solution to that dilemma too.

 

This community really is as cool as many say it is.

 

Cheers

Link to comment
Share on other sites

@luthienanarion: You, are awesome. BTW where to find nvse source?

 

@matortheeternal: Thanks for posting the example, I am still very much a noob with scripting for both the games and fnv/tes edit. That's very useful for me to create chests for a mod of my own which supports my particular load order. The solution which I was seeking however was a generic approach that anyone, modder or not could use with no fuss by making the functionality into a mod that you can just dump in game. luthienanarion seems to have made a solution to that dilemma too.

 

This community really is as cool as many say it is.

 

Cheers

 

It is a generic approach that anyone can use.

 

Proof of concept that TES5Edit scripts can be used by anyone:

http://www.nexusmods.com/skyrim/mods/37981

Link to comment
Share on other sites

NVSE's source code is included in the download, in the "src" folder. The included project files are for Visual C++ 2005 or 2008. Keep in mind that you'll have to install the DirectX SDK and add its libraries to the build path in order to compile it. I'll release my plugin's source when I finish up version 3; it could stand to be cleaned up a little.

 

 

On the subject of *Edit scripts, I definitely could use some good source examples. I never learned PASCAL having jumped straight from BASIC to C++ when I was a kid. I'll check out your automation scripts, Mator.

Link to comment
Share on other sites

@matortheeternal: I think you misunderstand my original idea, the last thing I would do when creating a mod is to FORCE people to use an FNVEdit script in order to set the mod up for themselves. The fact that many users can't even install a mod with NMM without having their hand held, it just makes my head spin with the amount of posts that I would get and complaints for doing such a thing.

 

@luthienanarion: uff... old man moment yes I see the source in the folder now. I never bothered with BASIC, abstracts always annoyed me (and still do) so I went straight to assembly. I regret that now as everything is in higher level languages...

Link to comment
Share on other sites

@matortheeternal: I think you misunderstand my original idea, the last thing I would do when creating a mod is to FORCE people to use an FNVEdit script in order to set the mod up for themselves. The fact that many users can't even install a mod with NMM without having their hand held, it just makes my head spin with the amount of posts that I would get and complaints for doing such a thing.

 

Go big or go home.

 

Edit Scripts are extremely powerful and to say that the community isn't intelligent enough to use them so you should never make a mod with them is terrible. The number of people who are actually that incapable are few, they just stick out like sore thumbs so we think there's more of them than there really are. Besides, they can learn.

Link to comment
Share on other sites

Ugh, that reminds me of the time I learned Java in order to make a SkyProc patcher. I'm glad I passed that project on to someone else so that they can play tech-support.

 

I've updated my NVSE plugin with the GetLoadedType function, so feel free to pull forms from other plugins like there's no tomorrow.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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