Jump to content

Scripting - Infinite condition loop


Cylentnyte

Recommended Posts

Hi all. Working on getting this script done partly for practice and partly for actual usage in the beginning of the mod. Basically you activate a generator on the ground and can repair it using a fission battery or 40+ Science. Once it's repaired it unlocks the terminal next to it and sets a global variable (for whether the generator was repaired or not) to 1. There's also a global for whether the station's main power source is turned on or not. If it is, it will make a coil of wire with an unused battery "enable" on the floor, and make the wire and cable seen when the generator's in use "disable". Also, if the generator was repaired, the unused battery on the floor will "disable" too. I know this is all overly complicated for a simple situation but like I said I want practice.

 

Anywho, this isn't the main problem. As soon as I load the game, I get the message that should normally only pop up after I try to fix the generator without having any batteries on me. I click "ok", it pops up again. And again. Infinite loop. Checked over the logic as much I could and encased all the conditionals but couldn't figure it out. Probably something stupid I missed. Any help would be appreciated. Thanks.

 

SCN ISSObservationGeneratorScript

Short sButtonPressed
Short sBatteryCount
Short sScienceLevel

Begin OnActivate

if (IsActionRef Player)
	Set sBatteryCount to Player.GetItemCount 00033bc6
	Set sScienceLevel to Player.GetActorValue Science
	if (ISSObservationGeneratorRepaired == 1)
		ShowMessage ISSObservationGeneratorRepairedMsg     ;Generator already repaired
	elseif (ISSObservationGeneratorRepaired == 0)
		ShowMessage ISSObservationGeneratorMsg      ;Regular message box with options to fix
	endif
endif

End

Begin GameMode

if (ISSPowerEnabled == 1)
	ISSGeneratorBatteryInUseREF.Disable
	ISSGeneratorWireInUseREF.Disable
	ISSGeneratorBatteryUnusedREF.Enable
	ISSGeneratorWireUnusedREF.Enable
elseif (ISSPowerEnabled == 0)
	ISSGeneratorWireInUseREF.Enable
	ISSGeneratorWireUnusedREF.Disable
	ISSGeneratorBatteryUnusedREF.Disable

	if (ISSObservationGeneratorRepaired == 1)
		ISSGeneratorBatteryInUseREF.Enable
	elseif (ISSObservationGeneratorRepaired == 0)
		ISSGeneratorBatteryInUseREF.Disable
	endif
endif

if (sButtonPressed == -1)
	Return
elseif (sButtonPressed == 0)							;Player chose to replace battery
	if (sBatteryCount == 0)								;Player doesn't have a fission battery
		ShowMessage ISSObservationGeneratorNoPartsMsg
	elseif (sBatteryCount >= 1)							;Player has a fission battery
		ShowMessage ISSObservationGeneratorSuccessMsg
		Player.RemoveItem 00033bc6 1
		set ISSObservationGeneratorRepaired to 1
		ISSGeneratorBatteryInUseREF.Enable
		ISSLaunchPadControlTerminalREF.Unlock
	endif
elseif (sButtonPressed == 1)							;Player chose to refurbish
	if (sScienceLevel < 40)								;Player isn't skilled enough to refurbish
		ShowMessage ISSObservationGeneratorScienceFailMsg
	elseif (sScienceLevel >= 40)						;Player has required skill
		ShowMessage ISSObservationGeneratorScienceSuccessMsg
		set ISSObservationGeneratorRepaired to 1
		ISSGeneratorBatteryInUseREF.Enable
		ISSLaunchPadControlTerminalREF.Unlock
	endif
elseif (sButtonPressed == 2)							;Player leaves generator alone

endif

End

Link to comment
Share on other sites

"00033bc6" -> You should be using the ObjectID to refer to an object in a script, not what you think the FormID will be. (FormIDs being modified by the load order... for objects in the main esm it will rarely be a problem, but is still very bad practice)

 

AFAIK: "If" statements need a logic test to test true, they don't count >0 values as being true like some languages, hence "IsActionRef Player" would be "IsActionRef Player == 1". But that exact same functionality can be achieved by using "Begin OnActivate Player" instead as the block start. In that case, the IsActionRef test isn't even needed. a second "Begin OnActivate" block can be added later if you want general functionality for other NPCs (It would include the PC too, but then you could sort them out).

 

In the "if (ISSPowerEnabled == 1 )" block (including the elseif) the contents of one of the two blocks is going to run every frame, trying to enable and disable the references every frame, this is going to cause the objects to appear to flicker instead of just sitting there. Adding in a do-once style block which ensures that the appropriate code is only run once on change of ISSPowerEnabled is the easiest way to fix that up.

 

You need to insert "set sButtonPressed to GetButtonPressed" just before the "if (sButtonPressed == -1)" line since you never actually set sButtonPressed anywhere in the script, so it's always going to be 0. You will also want to add in another check, such that the GetButtonPressed and subsequest code is only run after the message has been shown (with the check being cleared after a result box has run), since currently this will begin to operate any time a message pops up anywhere during the game. This is one of the 2 main bits causing the problems you're talking about.

 

 

Running through the logic -> All variables start at 0, so

[/font]elseif (ISSPowerEnabled == 0)
               ISSGeneratorWireInUseREF.Enable
               ISSGeneratorWireUnusedREF.Disable
               ISSGeneratorBatteryUnusedREF.Disable

               if (ISSObservationGeneratorRepaired == 1)
                       ISSGeneratorBatteryInUseREF.Enable
               elseif (ISSObservationGeneratorRepaired == 0)
                       ISSGeneratorBatteryInUseREF.Disable
               endif
       endif[font="Arial"]

In this bit the elseif test will test true, as will the elseif test within the block. With this running, enabling and disabling the references every frame.

 

[/font]elseif (sButtonPressed == 0)                                                    ;Player chose to replace battery
               if (sBatteryCount == 0)                                                         ;Player doesnt have a fission battery
                       ShowMessage ISSObservationGeneratorNoPartsMsg
               elseif (sBatteryCount >= 1)                                                     ;Player has a fission battery
                       ShowMessage ISSObservationGeneratorSuccessMsg
                       Player.RemoveItem 00033bc6 1
                       set ISSObservationGeneratorRepaired to 1
                       ISSGeneratorBatteryInUseREF.Enable
                       ISSLaunchPadControlTerminalREF.Unlock
               endif[font="Arial"]

This section of the second block will always run since sButtonPressed is always 0. sBatteryCount may or may not be 0 depending on the PC having which ever object is assigned to "33bc6", but since that section of the code is going to be running constantly, even if the object hasn't been activated, it's always going to be 0, hence the no parts message every frame.

 

There are a few more errors in various places, but those are most of the big ones.

Edited by Skevitj
Link to comment
Share on other sites

Wow I wasn't expecting nearly as detailed a reply as that, thank you so much :D

 

I did have the "set sButtonPressed to GetButtonPressed" in there somewhere but eventually became an accidental casualty of editing. Got confused with the way Cipscis explained how the button would return -1 on the next iteration through GameMode before MenuMode takes over and wasn't quite sure where to put GetButtonPressed.

 

There's going to be a main breaker for the power in a "Systems Room" cell, but it's not the same one as the generator. I'm guessing I can't put the enabling/disabling block inside the OnActivate script for the breaker because of the cell difference? That would be one way to do it I guess.

 

Also had "Begin OnActivate Player" in there before too but misread the GECK page on it and assumed it wouldn't be any different from just "Begin OnActivate", so will be redoing that.

 

Cheers

 

EDIT: Went over everything you said and finally got it working! Only other thing I did was move the message box option indexes around so 0 would be to "Leave it alone", would mean an uninitialized variable would default to that and avoid another loop. Finals results:

 

SCN ISSObservationGeneratorScript

Short sButtonPressed
Short sScienceLevel

Short sPowerStatusSentinel
Short sMsgSentinel

Begin OnActivate Player

Set sScienceLevel to Player.GetActorValue Science

if (ISSObservationGeneratorRepaired == 1)
	ShowMessage ISSObservationGeneratorRepairedMsg
elseif (ISSObservationGeneratorRepaired == 0)
	ShowMessage ISSObservationGeneratorMsg
	set sMsgSentinel to 1
endif

End

Begin GameMode

if (sPowerStatusSentinel != ISSPowerEnabled)
	if (ISSPowerEnabled == 1)
		ISSGeneratorBatteryInUseREF.Disable
		ISSGeneratorWireInUseREF.Disable
		ISSGeneratorBatteryUnusedREF.Enable
		ISSGeneratorWireUnusedREF.Enable
	elseif (ISSPowerEnabled == 0)
		ISSGeneratorWireInUseREF.Enable
		ISSGeneratorWireUnusedREF.Disable
		ISSGeneratorBatteryUnusedREF.Disable
		if (ISSObservationGeneratorRepaired == 1)
			ISSGeneratorBatteryInUseREF.Enable
		elseif (ISSObservationGeneratorRepaired == 0)
			ISSGeneratorBatteryInUseREF.Disable
		endif
	endif

	set sPowerStatusSentinel to ISSPowerEnabled
endif

if (sMsgSentinel == 1)
	set sButtonPressed to GetButtonPressed

	if (sButtonPressed == -1)
		Return
	elseif (sButtonPressed == 1)							;Player chose to replace battery
		ShowMessage ISSObservationGeneratorSuccessMsg
		Player.RemoveItem FissionBattery 1
		ISSGeneratorBatteryInUseREF.Enable
		ISSLaunchPadControlTerminalREF.Unlock
		set ISSObservationGeneratorRepaired to 1
		set sMsgSentinel to 0
	elseif (sButtonPressed == 2)							;Player chose to refurbish
		if (sScienceLevel < 40)								;Player isn't skilled enough to refurbish
			ShowMessage ISSObservationGeneratorScienceFailMsg
			set sMsgSentinel to 0
		elseif (sScienceLevel >= 40)						;Player has required skill
			ShowMessage ISSObservationGeneratorScienceSuccessMsg
			ISSGeneratorBatteryInUseREF.Enable
			ISSLaunchPadControlTerminalREF.Unlock
			set ISSObservationGeneratorRepaired to 1
			set sMsgSentinel to 0
		endif
	else
		set sMsgSentinel to 0
	endif
endif

End

Edited by sphinx275
Link to comment
Share on other sites

If the "breaker" object (placed in the game world) is marked as persistent, then you can call enable/disable on it from this script. I'm not 100% sure, but I believe the way to do it properly is to place an x-marker in the cell (same as the breaker object), and set it as the "parent" of the object. You then enable/disable the x-marker from this script and the "child object" (being the breaker object) is set to copy it's enabled/disabled state from the parent.

 

Only problem I can see in your latest is that you're removing a fission battery, without ever checking to see if they have one. The scripting around sBatteryCount from your first script was correct (assuming you change the FormID -> ObjectID), so just copy/pasting that should fix that up.

Edited by Skevitj
Link to comment
Share on other sites

Sounds good. I figure though it would be cleaner to just keep the ISSPowerEnabled global there and have each object/activator that uses it to essentially listen for it's state to change. That way all the breaker does is change the variable to a 1 and anything listening for it automatically changes. Also a versatility bonus as any new object added in could just listen to that variable instead of editing the breaker script directly.

 

As for the fission battery, I forgot to say I added a condition check to the message index for using a battery. If the player doesn't have one, the message option to replace it won't even appear. Thanks again!

Link to comment
Share on other sites

  • Recently Browsing   0 members

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