Jump to content

Tutorial: NVSE4+ part 1: syntax and expressions


Recommended Posts

Does anyone know if NVSE has any commands to see if an NPC has an specific item? I need to make a script that detects if an NPC has any sort of power armor, if the NPC has an item then the script would proceed to add some other items to that NPC's inventory. 

 

EDIT: Anything works actually, I'm updating a mod called Powered Power Armor, and I need a way to add the PA mods to NPCs who use power armor, the old way PPA used to do it is overly complex and creates an unnecessary dependency on another plugin. The problem is that if I add this normally into factions then the dudes without power armor will get these PA mods. If I create a leveled item with PA and the PA mods themselves, maybe that could work but I also want it to be more random on the selection of which PA mods each random NPC may get. 

Link to comment

Run GetItemCount and check for a FormList containing every piece of power armor instead of an individual item. As an added bonus, this method can allow you to let users specify custom armors as power armor in case they've added some new content.

Link to comment

I don't think you can really prevent that since you have to check them to see if they've been checked before. The alternative would be to slap a script with an OnLoad block on every relevant NPC, but that would probably cause a lot of incompatibilities. I suggest not worrying about it since it's very unlikely you'll be able to make a script that takes longer than a frame to complete. I've only heard of that happening once, and that situation involved moving around ten+ NPCs and doing all sorts of calls and whatnot; if you're just doing inventory stuff you should be fine.

 

Also, I use PPA so let me know when you've got an update out. :)

Link to comment

I don't think you can really prevent that since you have to check them to see if they've been checked before. The alternative would be to slap a script with an OnLoad block on every relevant NPC, but that would probably cause a lot of incompatibilities. I suggest not worrying about it since it's very unlikely you'll be able to make a script that takes longer than a frame to complete. I've only heard of that happening once, and that situation involved moving around ten+ NPCs and doing all sorts of calls and whatnot; if you're just doing inventory stuff you should be fine.

 

Also, I use PPA so let me know when you've got an update out. :)

 

It might take a while, its my second time scripting in Fallout, I've scripted with Java and other languages on more complex stuff but the scripts in the mod are really messed up, they're very messy and hard to understand and I've found they have all kinds of redundancies. For now, what I've done is axing Inventory Access.esm, which was the way it distributed PA and PA mods to NPCs. As far as I'm aware, the only faction to which the mod added PA was the Legion.

 

I'm planning on making an array that contains most of the PA mods, a few leveled items for Legion PA and a script that delivers those PA mods randomly to factions that use PA like the Brotherhood and the Enclave(provided you've got a mod that spawns them in the game anyway). My only unknown is that I don't know if things can be added to a leveled item(GECK website says' they're leveled lists, so I assume they can be treated as such) through script which is what I want to do with the Legion, so that if a Legion NPC has armor added, then he'll have 1 or 2 PA mods added  to his inventory. Hopefully this works like I'm thinking it does, I'm also not quite sure if there's a random function to pick out the elements in the array. I don't want to rely on Inventory Access to get this done, it added stuff in a clever way but in a more complex way that it should have been, plus PPA only referenced it in one instance. 

Link to comment

Actually, the "random function to pick out the elements in an array" is known as a leveled item/list. You can add to a leveled list (AddItemToLeveledList) but I'm not sure why you'd want to.

 

Yeah but doesn't NVSE 4 beta3 put arrays in? What I want to do is to put PA's mods in there, have the script pick one out randomly and then add it to the factions that use PA. Unless there's a better way to do it, which I can't think off and I don't want to have to resort  to checking the containers in a cell like PPA did before. 

 

Any reason not to use  AddItemToLeveledList? I was planning to add it randomly with that. 

Link to comment

I think what you're talking about is probably holding PPA items in arrays, and pick from them to add to levelled lists rather than adding them to the lists in the geck, in order to avoid bashing/merging of those lists?

 

Edit: unless you plan to use maps or stringmaps, there's strictly speaking no need for arrays in that case, you could populate from formlists with a while loop or something. This has the added advantage that other mods could add their items to them with BuildRef + AddFormToFormList.

Link to comment

Leveled lists are arrays where the item is randomly chosen. There's no need to code a method to do that from scratch. How to you plan to add it to "the factions"? The Legion alone have almost a hundred leveled lists NPCs can use. And this is the reason you shouldn't use AddItemToLeveledList: "Once altered using this function, it will persist in the save game data. It can not be undone."

Link to comment

I think what you're talking about is probably holding PPA items in arrays, and pick from them to add to levelled lists rather than adding them to the lists in the geck, in order to avoid bashing/merging of those lists?

 

Edit: unless you plan to use maps or stringmaps, there's strictly speaking no need for arrays in that case, you could populate from formlists with a while loop or something. This has the added advantage that other mods could add their items to them with BuildRef + AddFormToFormList.

 

 

Yes that's what I want to do. I'm pretty new at this language and there is some stuff about it that is kind of unusual.

 

 

 

Leveled lists are arrays where the item is randomly chosen. There's no need to code a method to do that from scratch. How to you plan to add it to "the factions"? The Legion alone have almost a hundred leveled lists NPCs can use. And this is the reason you shouldn't use AddItemToLeveledList: "Once altered using this function, it will persist in the save game data. It can not be undone."

 

 

Yeah, I know about that limitation, but then again who'd remove a mod like this mid game anyway? But yeah, I'd like to avoid it. I was planning on using the legions armor lists, mainly CondLegionArmorVeteran and CondLegionArmorPrime. I'm sure the Brotherhood has its own similar lists. My issue with the mods PPA adds is that if I just throw everything at one leveled list, then it'll spawn PPA mods in places it doesn't have to. SInce you're telling me it adds it randomly anyway, I think its probably best if I make some leveled items with the armors I want to add to the legion, I guess if an NPC gets said leveled item, then I'll be able to avoid those mods spawning to NPCs that don't use PA. However, there is one issue with this, conflicts, I'm trying to make the mod as conflict free as I can, since it was one of the issues that it has, I want it to work with mods like FOOK and PN, hopefully without having to make patches for those mods. 

Link to comment

You're most likely to run into one of two issue regarding the permanence: Either someone plays with the mod for a while and decides they don't want it or you need to change something to fix an issue. As for adding the items to the lists, that's simply not going to work. Leveled lists randomly select an item. If you add your mods to that list, then when the NPCs spawn with the mod in their inventories they'll be naked because it chose the mod instead of their armor.

 

So this leaves you with two options: You either add to NPC inventories in the GECK or in-game. While you can add in the GECK, I strongly suggest against it for a number of reasons. First, many users don't merge/bash their plugins. Second, even if they do it's a "dumb" process; I've had NPCs spawn with eight sets of armor because it doesn't know any better and just smashes them all together. Some users go through and make a plugin just to fix the dumb stuff the bash does, but again most won't. Third, in general you should override base-game records only when absolutely necessary, as other mods are likely expecting them to be unchanged.

 

Checking every NPC in-game is probably your best bet in terms of compatibility and the performance hit from it should be negligible, as not only would it not fall into the three problems above but would also work for NPCs added by mods.

Link to comment

Alright, if checking NPCs is the best way to go, then I will do that. However I'll need a little more explaining on how to do that. I sort of get the idea of the GetItemCount with the FormList, I think there's one called AllPowerArmor or NVAllPowerArmor and it has everything in there, you also mentioned that I'd be able to add in PA from other mods. That would work out great since, there's some custom PAs in PPA, plus a new one I'm going to add in. 

Link to comment

Well first you should make sure you're adding your new armors to the vanilla power armor lists using ListAddForm to allow compatibility with other mods that do things with power armor. Next make a form list with every one of your mods. Create a new Misc. Item and flag it Non-Playable, then place it in the list as well. Now make your leveled list(s). You can balance based on PC level here and if you want to you can make different lists for different factions. If you do, don't forget to make a generic list in case an NPC doesn't fit into any of them. If you want a chance for no mod to spawn, add that Non-Playable item you made.

 

You'll need to scan every NPC in the player's current cell. Run GetFirstRef and add the result to a form list. Then make a loop and use GetNextRef to continue adding to the list. Precede the function calls with an if statement that makes sure there are refs left (usually just "if actor" after "set actor to GetNextRef") to check and put a nice delay on it as you shouldn't need to be running this every frame anyway.

 

When you have your NPC(s) start with a GetItemCount check against your mod form list. If they have something on that list you don't need to do anything. Next check against the Power Armor form lists (if you want to get really fancy you could try to run IsPowerArmor against all the armor items in their inventories, but it's probably not worth the effort). If they don't have power armor, add the Non-Playable item so they fail the first check next time. If they're still here, AddItem one of your leveled lists depending on the faction.

Link to comment
  • 2 weeks later...

This code:

 

ref rActor
Begin GameMode

        if ListGetFormIndex SexoutListBannedActor rActor != -1 || ListGetFormIndex SexoutSLActorDataIsReserved rActor != -1
            ; Pass
        endif
End

 

Compiles fine, but if I use compiling override (_GameMode), I get "Warning: Invalid expression for parameter 2, expected form" ... "Could not parse this line". Why is this?

 

* the real script I want to use override for has a reason for it (arrays), the above is just a simple example that gives the same error

Link to comment
  • 1 year later...

I have a kind of general scripting question...

 

I'm trying to figure out the number of occupied slots of an arbitrary apparel item.

 

The GetBipedSlotMask function returns those slots as a long integer flag number. This can be any combination of possible slots. 1002 if it's single slot or 110012 if it's multiple slots for example.

So basically I'm trying to figure out how many digits in the given number are 1, not 0. If it's 1, that slot is occupied by the equipment, if 0 then not occupied.

Specifically the slot numbers I'm interested in are as follows:

 

2	0000000000000010
4	0000000000000100
8	0000000000001000
16	0000000000010000
32	0000000000100000
64	0000000001000000
128	0000000010000000
256	0000000100000000

8192	0100000000000000
32768	1000000000000000
I wrote two versions of the same thing to do this, but I'm not sure which one is more efficient.

 

The first one iterates over integers between 0 and 16, make a nth power of 2 and compare it to the original slot number with bitwise AND.

 

The second one is, do a bitwise right-left shift, thusly dividing the previous slot number by 2 - truncate - multiply by 2. Then compare this to the untouched previous slot number. If they are not the same (odd number) that slot is occupied, if same (even number) not occupied.

 

        Let slot_cur := (GetBipedSlotMask cur_base) & 41470
        if 0 == slot_cur
            continue
        endif

        Let num_slots := 0

; * 1 *
        Let iter2 := 0
        while 16 > (iter2 += 1)
            if eval ((2^iter2) & slot_cur)
                Let num_slots += 1
            endif
        loop

; OR
; * 2 *
        while slot_cur
            if (slot_cur >> 1 << 1) == slot_cur
                Let num_slots += 1
            endif
            Let slot_cur := slot_cur >> 1
        loop
I wrote the second one because I thought bitwise shift would be more efficient (becuase it's computer), is that correct? Would there be any way to do this more nicely?
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...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. For more information, see our Privacy Policy & Terms of Use