Jump to content

Unusual but related scripting questions


HerrBaron

Recommended Posts

Hey folks,

 

I'm the guy who built HB's Wasteland Tent, and I'm looking to upgrade it to accomodate companions beyond the vanilla ones. These were easy, since they're present in the FalloutNV.esm.

 

The issues and questions revolve around a characteristic of the Havoc door objects; when a door is moved, its' location is not updated by the engine; believe me, I've tried several different ways. Hence, the Wasteland Tent doesn't use a door; just a couple of travel markers and the MoveTo function (for both the player and the vanilla followers subject to some checks to make sure that they're actually following, not waiting and not disabled). In other words, the Tent model itself is an activator.

 

As stated above, I'd like to be able to support other companions, such as Fake Plastic Trees EDE and GurkMeja's Sunny Smiles Companion (and a number of others recently uploaded to Nexus).

 

One of the things I've tried to do in preparation for this is to generalize the code which tests follower/waiting/disabled status before moving a follower into or out of the tent after the player. I did this with a formlist and NVSE to loop over and process it. It doesn't work, even for the vanilla followers, apparently because the engine has no way of knowing that an aliased reference to a reference (set rFollower to ListGetNthForm VanillaFollowerList, index). When you attempt to test variables through rFollower, the engine has no way of knowing that rFollower actually points at a follower with a script which contains variables like Waiting.

I've only been able to make it work for vanilla followers by duplicating the same conditional code block for each one. So, if generalizing this code doesnt' work for vanilla followers, it seems to bode ill for others not even in FalloutNV.esm.

 

Anyone have any ideas as to how I might:

 

A) get a door object to actually update its world location if moved,

or

B) Generalize the entry/exit code in such a way as to make it possible to test both vanilla followers and any others I might add to the list in the course of play?

 

I've been fighting this for weeks, now; if anyone has any workable ideas, I'd be most grateful.

 

Thanks!

-HB

Link to comment
Share on other sites

* MoveTo is an excellent way to move an object from one cell to another.

* Consider using SetPos if MoveTo doesn't do the trick.

* If this function is used to move the player, the function will queue up a movement request which will NOT process immediately and will NOT halt script execution.

* This function works as expected for Actors. For most other object types, like containers and activators, the object's coordinates are updated but its world art is not. Additional scripting may be necessary to ensure the object moves properly:

 

myObject.Disable

myObject.MoveTo myMarker

myObject.Enable

set xPos to myObject.GetPos X

myObject.SetPos X xPos

 

* The trick above won't work with pre-loaded furnitures : It seems neither MoveTo nor SetPos are allowed with furniture objects unless they are spawned with the PlaceAtMe function.

 

 

 

Try using this, but create a new door. I am not sure what they mean by Pre-Loaded furnitures. Try it wil This door I know it doesn't fit, but will work for testing.

Link to comment
Share on other sites

well, You may do some "probablistic" checks with NVSE.... You need to find out

 

a) current companions

b) their waiting status

 

I would assume any companions which are NOT waiting are close to tent when it's door is activated. so that's b)

by iterating over actors in current cell (NVSE GetFirstRef/GetNextRef) and using GetIsTeamMate (not even sure if this is true when companion is waiting) You can build formlist in OnActivate.

then You can in OnLoad of Your tent use PlaceAtMe.

 

as I said it's "probabilistic" however something better than nothing. (and also simple to code :P)

at worst it will end up teleporting companions waiting outside of tent into it... (You may also add activate choice whenever player want to enter tent alone so those pervs can do inside what they want)

Edited by thc1234
Link to comment
Share on other sites

* MoveTo is an excellent way to move an object from one cell to another.

* Consider using SetPos if MoveTo doesn't do the trick.

* If this function is used to move the player, the function will queue up a movement request which will NOT process immediately and will NOT halt script execution.

* This function works as expected for Actors. For most other object types, like containers and activators, the object's coordinates are updated but its world art is not. Additional scripting may be necessary to ensure the object moves properly:

 

myObject.Disable

myObject.MoveTo myMarker

myObject.Enable

set xPos to myObject.GetPos X

myObject.SetPos X xPos

 

* The trick above won't work with pre-loaded furnitures : It seems neither MoveTo nor SetPos are allowed with furniture objects unless they are spawned with the PlaceAtMe function.

 

 

 

Try using this, but create a new door. I am not sure what they mean by Pre-Loaded furnitures. Try it wil This door I know it doesn't fit, but will work for testing.

 

Thanks, Interfero; found something similar on the GECK wiki, but oddly, the door I'm using won't move consistently, only sometimes... pretty odd. Even tried using SetPos instead of MoveTo; no joy.

 

-HB

Link to comment
Share on other sites

well, You may do some "probablistic" checks with NVSE.... You need to find out

 

a) current companions

b) their waiting status

 

I would assume any companions which are NOT waiting are close to tent when it's door is activated. so that's b)

by iterating over actors in current cell (NVSE GetFirstRef/GetNextRef) and using GetIsTeamMate (not even sure if this is true when companion is waiting) You can build formlist in OnActivate.

then You can in OnLoad of Your tent use PlaceAtMe.

 

as I said it's "probabilistic" however something better than nothing. (and also simple to code :P)

at worst it will end up teleporting companions waiting outside of tent into it... (You may also add activate choice whenever player want to enter tent alone so those pervs can do inside what they want)

 

thc1234,

 

Working on something like this as we speak; I was trying to stay away from NVSE (Not because I don't like it, NVSE is terrific; I was just hesitant to introduce it into my codebase if I could think of another way to do it...). I'll post back as soon as I get it working, and let you know how it worked out! BTW, isn't PlaceAtMe only useable with form id's, not persistent references, like, say, BooneREF or VeronicaREF? Since there's only one of these each in-game, wouldn't you be creating copies of them, and bloating the game saves?

 

You're right; they are ALL pervs; you should SEE what they get upto in that tent when my back is turned! :P

 

-HB

Link to comment
Share on other sites

Well, got it working very nicely; gotta test it for a while though. I've attached the Entry code as a .TES file, since the code markers make a mess of the embedded code. All I have to do now is set up the quest code to check for NVSE and warn the user, and test for a good while. I used several of the suggestions from you guys; many thanks for helping me make a better mod than I had before! :)

 

Best!

-HB

 

P.S. it sure would be nice if the GECK could process line breaks in long lines of code; not doing so makes it tough to write complex conditional code, because it all has to go on the same line...

Link to comment
Share on other sites

For an NVSE check, you can do something like:

 

Global: MyModNVSEVersionGLOB (constant "0")

 

Quest: MyModNVSECheckQUST

scn MyModNVSECheckQUSTSCPT

Begin GameMode
if (MyModNVSEVersionGLOB == 0)
	set MyModNVSEVersionGLOB to -1 ; No NVSE
	set MyModNVSEVersionGLOB to GetNVSEVersion ; NVSE Version
endif
End

 

If they don't have NVSE, MyModNVSEVersionGLOB will be set to "-1" and then the next line will cause the script to stop running because of "GetNVSEVersion". If they have NVSE, MyModNVSEVersionGLOB will be the version they are using. So you can test for:

 

Quest: MyModInitializeQUST

scn MyModInitializeQUSTSCPT

int iNVSEVer

Begin GameMode
if (iNVSEVer != MyModNVSEVersionGLOB)
	if (MyModNVSEVersionGLOB == 0)
		Return ; give the check more time
	else
		if (MyModNVSEVersionGLOB > 0) ; They have NVSE
			ShowMessage MyModNVSEYesMESG
		else ; They do not have NVSE
			ShowMessage MyModNVSENoMESG
		endif
		set iNVSEVer to MyModNVSEVersionGLOB
	endif
endif
End

 

The check will be run every time the game starts (because MyModNVSEVersionGLOB is constant, it will get reset to "0").

 

it sure would be nice if the GECK could process line breaks in long lines of code

 

It sure would. If you're interested, there's a post floating around by Cipscis which talks about script performance. Where he times different logic to see which is the fastest. He noted that the more operators used, the slower the performance. It was actually faster to use separate conditional statements rather than "this && that && those && these" etc.

 

; Slower - checks all 5 every time
if ( ( rFollower.GetCurrentAIProcedure == 11 ) && ( rFollower.GetPackageTarget == Player ) && ( rFollower.GetDisabled == 0 ) && ( rFollower.GetDead == 0 ) && ( rFollower.GetUnconscious == 0 ) )
; Do stuff
endif

; Faster  - could even arrange by which are most likely to be true
if ( rFollower.GetCurrentAIProcedure == 11 )
if ( rFollower.GetPackageTarget == Player )
	if ( rFollower.GetDisabled == 0 )
		if (rFollower.GetDead == 0 )
			if ( rFollower.GetUnconscious == 0 )
				; Do stuff
			endif
		endif
	endif
endif
endif

 

Or if you're dealing with a boolean (true/false), you can leave the operators out completely. It was faster to run an if/else with no operators than an if with.

 

; Slower - check if equal to 0 every time
if ( rFollower.GetDisabled == 0 )
; Do stuff
endif

; Faster - not doing a logical comparison
if ( rFollower.GetDisabled ) ; functions like "rFollower.GetDisabled != 0"
else
; Do stuff
endif

 

So, something like this would theoretically be the fastest:

if ( rFollower.GetDead )
else
if ( rFollower.GetDisabled )
else
	if ( rFollower.GetUnconscious )
	else
		if ( rFollower.GetCurrentAIProcedure == 11 )
			if ( rFollower.GetPackageTarget == Player )
				; Do stuff
			endif
		endif
	endif
endif
endif

 

Having it all on one line is nice and neat, but not necessarily the best choice - if you're talking performance.

 

Edit:

I think there's a max of around 10 nested if's though.

Edited by Ez0n3
Link to comment
Share on other sites

For an NVSE check, you can do something like:

 

Global: MyModNVSEVersionGLOB (constant "0")

 

Quest: MyModNVSECheckQUST

scn MyModNVSECheckQUSTSCPT

Begin GameMode
   if (MyModNVSEVersionGLOB == 0)
       set MyModNVSEVersionGLOB to -1 ; No NVSE
       set MyModNVSEVersionGLOB to GetNVSEVersion ; NVSE Version
   endif
End

 

If they don't have NVSE, MyModNVSEVersionGLOB will be set to "-1" and then the next line will cause the script to stop running because of "GetNVSEVersion". If they have NVSE, MyModNVSEVersionGLOB will be the version they are using. So you can test for:

 

Quest: MyModInitializeQUST

scn MyModInitializeQUSTSCPT

int iNVSEVer

Begin GameMode
   if (iNVSEVer != MyModNVSEVersionGLOB)
       if (MyModNVSEVersionGLOB == 0)
           Return ; give the check more time
       else
           if (MyModNVSEVersionGLOB > 0) ; They have NVSE
               ShowMessage MyModNVSEYesMESG
           else ; They do not have NVSE
               ShowMessage MyModNVSENoMESG
           endif
           set iNVSEVer to MyModNVSEVersionGLOB
       endif
   endif
End

 

The check will be run every time the game starts (because MyModNVSEVersionGLOB is constant, it will get reset to "0").

 

it sure would be nice if the GECK could process line breaks in long lines of code

 

It sure would. If you're interested, there's a post floating around by Cipscis which talks about script performance. Where he times different logic to see which is the fastest. He noted that the more operators used, the slower the performance. It was actually faster to use separate conditional statements rather than "this && that && those && these" etc.

 

; Slower - checks all 5 every time
if ( ( rFollower.GetCurrentAIProcedure == 11 ) && ( rFollower.GetPackageTarget == Player ) && ( rFollower.GetDisabled == 0 ) && ( rFollower.GetDead == 0 ) && ( rFollower.GetUnconscious == 0 ) )
   ; Do stuff
endif

; Faster  - could even arrange by which are most likely to be true
if ( rFollower.GetCurrentAIProcedure == 11 )
   if ( rFollower.GetPackageTarget == Player )
       if ( rFollower.GetDisabled == 0 )
           if (rFollower.GetDead == 0 )
               if ( rFollower.GetUnconscious == 0 )
                   ; Do stuff
               endif
           endif
       endif
   endif
endif

 

Or if you're dealing with a boolean (true/false), you can leave the operators out completely. It was faster to run an if/else with no operators than an if with.

 

; Slower - check if equal to 0 every time
if ( rFollower.GetDisabled == 0 )
   ; Do stuff
endif

; Faster - not doing a logical comparison
if ( rFollower.GetDisabled ) ; functions like "rFollower.GetDisabled != 0"
else
   ; Do stuff
endif

 

So, something like this would theoretically be the fastest:

if ( rFollower.GetDead )
else
   if ( rFollower.GetDisabled )
   else
       if ( rFollower.GetUnconscious )
       else
           if ( rFollower.GetCurrentAIProcedure == 11 )
               if ( rFollower.GetPackageTarget == Player )
                   ; Do stuff
               endif
           endif
       endif
   endif
endif

 

Having it all on one line is nice and neat, but not necessarily the best choice - if you're talking performance.

 

Edit:

I think there's a max of around 10 nested if's though.

 

 

Ez0n3, Good stuff; thanks! I did run across that thread of Cipcis', over at the Bethsoft forums, but admittedly didn't spend more than a few minutes perusing it. I did intend to go back... Anyway, when I'm not focusing on the 'day job' (30 yrs+ as a software engineer), I've been focusing on getting this mod upgrade working, then getting it to work better... :thumbsup: Great suggestions you've made; I'll be using them. I'm very partial to the "If yada" notation; I use it in real life development all the time, but it seemed to me that I had some issues with it in TES script way back, so have kinda stayed away from it.

 

The only thing currently in the quest script for the tent is a one-time Player.AddItem to stick the manual in their inventory, then I call StopQuest on it, since it never needs to execute again, so maybe I'll work that into a global flag, as well, and remove the StopQuest call.

 

Anyway, thanks for all this; I'll be back!

 

Best,

-HB

Link to comment
Share on other sites

I hear ya :)

 

I did run across that thread of Cipcis', over at the Bethsoft forums

Ahh, that's why I couldn't find it, here it is: Script Optimisation

 

If you StopQuest the NVSE checker and then save, the next load, it will be stopped still (one time only check). If you want it to check if they are running NVSE every time it loads (in case they started it without NVSE after they already saved), you'll have to leave it running.

Edited by Ez0n3
Link to comment
Share on other sites

  • Recently Browsing   0 members

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