Jump to content

Elementary Scripting Question


kingtitan

Recommended Posts

While I don't think this question is extremely technical, I have just a couple questions about the messagebox and message functions. I am still learning the oblivion scripting language, so any clarification is much appreciated! Here are my issues:

 

1) When I use the message function, the message displays indefinitely until I go into menu mode.

 

2) Secondly, I know this is elementary, but when I use the messagebox function the message goes into an infinite loop.

 

What do I add to the script so that it terminates properly? The messages are being used in an "object" script if that helps. I have also already looked at the cs wiki, but so far it has been no help. Any assistance would be greatly appreciated!

 

Edit: I also forgot to mention that the message is executed under an "if" expression. So basically I want to limit the message to only a few seconds or make the messagebox terminate after "done" is pressed once.

Edited by kingtitan
Link to comment
Share on other sites

A "DoOnce" variable should fix the problem, like so:

 

 

 

scn ScriptName

 

short DoOnce

 

Begin GameMode

 

If (DoOnce==0)

Message "This is the message"

Set DoOnce to 1

else

return

endif

end

 

 

 

 

I used GameMode there as an example but any block type (OnAdd, OnActivate, etc.) will benefit from using a DoOnce. There might be better/more elegant ways (I leave that to those with more experience than myself) but this should pretty much solve your problem. :smile:

Link to comment
Share on other sites

That works better, but the only problem within my context is that the message literally only prints out once. Once the item is re-equiped the message does not print out at all. Any suggestions? Here is my code that I am trying to add the message to if it helps:

 

 


Scn ATPrisonerCollarScript

ref self
ref wearer

Begin GameMode

Set self To GetContainer

if ( wearer != 0 && wearer != self ) ; container changed and still has reference to previous wearer
       wearer.SetFactionRank ATPrisonerFaction, -1
       set wearer to 0 ; delete reference to previous wearer
endif

if ( self.IsActor == 0 ) ; item got dropped, previous wearer got removed from faction by previous check in this case, too
       Return
endif

set wearer to self ; store reference to current wearer 

if ( self.GetEquipped ATPrisonerCollar )
self.SetFactionRank ATPrisonerFaction, 0
else
self.SetFactionRank ATPrisonerFaction, -1

endif

End

 

 

I didn't include a doOnce variable / statement because it was giving me the aforementioned behavior. I want to add a message that displays a confirmation message everytime the NPC is added to the faction, to be precise. Here is an idea (nonworking with doOnce) of what I have in mind:

 

 


Scn ATPrisonerCollarScript

ref self
ref wearer

short doOnce
short doOnceNPC

Begin GameMode

Set self To GetContainer

if ( wearer != 0 && wearer != self ) ; container changed and still has reference to previous wearer
       wearer.SetFactionRank ATPrisonerFaction, -1
       set wearer to 0 ; delete reference to previous wearer
endif

if ( self.IsActor == 0 ) ; item got dropped, previous wearer got removed from faction by previous check in this case, too
       Return
endif

set wearer to self ; store reference to current wearer 

if ( self.GetEquipped ATPrisonerCollar )
self.SetFactionRank ATPrisonerFaction, 0
if wearer != player && doOnceNPC == 0
	Message "NPC has been added to the faction", 5
	set doOnceNPC to 1
elseif wearer == player && doOnce == 0
	Message "You have been added to the faction", 5
	set doOnce to 1
endif
else
self.SetFactionRank ATPrisonerFaction, -1

endif

End

 

Edited by kingtitan
Link to comment
Share on other sites

Hmm, that's a tougher one. These are the times I wish I could still start the CS to give it a try, but the game is installed on an external drive which is just on the verge of dying and I'm not going to power it up again before I have another one ready for backups.

 

I think this could be handled by catching the response you give by clicking the ok button (GetButtonPressed should return "0").

Try adding these lines to the start of your modified script and see if it helps already:

Scn ATPrisonerCollarScript

ref self
ref wearer

short doOnce
short doOnceNPC
short button ; for storing the result of the messagebox

Begin GameMode

set button to GetButtonPressed ; store the result of the messagebox
if ( button != -1 ) ; reset do-once flags when a button was pressed
       set doOnce to 0
       set doOnceNPC to 0
endif

Set self To GetContainer

if ( wearer != 0 && wearer != self ) ; container changed and still has reference to previous wearer
       wearer.SetFactionRank ATPrisonerFaction, -1
       set wearer to 0 ; delete reference to previous wearer
endif

if ( self.IsActor == 0 ) ; item got dropped, previous wearer got removed from faction by previous check in this case, too
       Return
endif

set wearer to self ; store reference to current wearer 

if ( self.GetEquipped ATPrisonerCollar )
       self.SetFactionRank ATPrisonerFaction, 0
       if wearer != player && doOnceNPC == 0
               Message "NPC has been added to the faction", 5
               set doOnceNPC to 1
       elseif wearer == player && doOnce == 0
               Message "You have been added to the faction", 5
               set doOnce to 1
       endif
else
       self.SetFactionRank ATPrisonerFaction, -1

endif

End

In theory, or going by documentation, GetButtonPressed should return a value != -1 the first time it's called after the messagebox closed. If this doesn't work the way I did it up there, try adding a MenuMode block instead and catch it in there.

I always call my messageboxes in GameMode and catch their result in MenuMode, which you're in while the messagebox is open. But if the documentation is right, it can also be done in GameMode, which you return to anyways right after the button was clicked and the messagebox closed.

 

I have no way to test this though.

 

edit: No, wait a second. The bottom part of the code will execute over and over again, putting self into the faction again and again, and popping up the messagebox again and again. A check if self already "is" in the faction (e.g. faction rank >= 0) becomes really mandatory here.

Edited by DrakeTheDragon
Link to comment
Share on other sites

Well I realized that a game mode type script won't work very well for having only a temporary message. However I had another idea, what if I were to write a whole other script and make it the collar's enchantment? That way the message can operate under a different block instead of having to use the GameMode block. If this would work could any of you guys get me started with it? I'm really not sure which block should be / could be used to get this working...
Link to comment
Share on other sites

Hmm, while you could solve this task via an enchantment as well, saving you the need to check the container for if the item is worn (if it is worn, the enchantment runs, else it won't), it won't help you much with your current issues.

I don't know if there is a way to detect when the enchanted item is unequipped at all in enchantments. Right now I see no way for the enchantment approach to remove the no-longer-affected actor from the faction again.

 

ScriptEffectStart blocks always work, execute once as soon as the spell effect gets applied, and never repeat, ever.

ScriptEffectFinish blocks act the same, just when the spell effect runs out, but I don't know if they're even triggered at all when an enchanted item gets unequipped/dropped/whatever!

ScriptEffectUpdate blocks, in my observations, turned out absolutely unreliable, period. You cannot tell if they will execute or not in all cases, and do not expect them to "regularly" repeat either. Plus neither do I know if they're even executed in an enchantment at all.

 

So, from my point of view we're back at a GameMode block, repeatedly checking for the affected actor having the item equipped, for the scripted effect spell the enchantment uses anyways. And this time, when you unequip the item, the script of the enchantment "dies", stopping execution right on the spot and taking all stored variables with it. There is no way an unequipped item could remove you from the faction via an enchantment approach, as while not worn no script will exist.

 

You're pretty close with what you have already. Why not stick with what's already working?

Scn ATPrisonerCollarScript

ref self
ref wearer

Begin GameMode

Set self To GetContainer

if ( wearer != 0 && wearer != self ) ; container changed and still has reference to previous wearer
       wearer.SetFactionRank ATPrisonerFaction, -1
       set wearer to 0 ; delete reference to previous wearer
endif

if ( self.IsActor == 0 ) ; item got dropped, previous wearer got removed from faction by previous check in this case, too
       Return
endif

set wearer to self ; store reference to current wearer 

if ( ( self.GetEquipped ATPrisonerCollar ) && (self.GetFactionRank ATPrisonerFaction < 0 ) )
       self.SetFactionRank ATPrisonerFaction, 0
       if wearer != player && doOnceNPC == 0
               Message "NPC has been added to the faction", 5
       elseif wearer == player && doOnce == 0
               Message "You have been added to the faction", 5
       endif
elseif ( self.GetFactionRank ATPrisonerFaction >= 0 )
       self.SetFactionRank ATPrisonerFaction, -1
endif

End

That script should do, and also pop up the message only the first time the collar gets detected as worn while it is worn, as the actor now is only put into the faction once when he's not already in it.

If you put on the collar, whether on yourself or an NPC, the messagebox should pop up, only once. If you take it off and put it on again, again the messagebox should pop up, and again only once.

(That is only if GetButtonPressed actually works the way I expected it to work in this script.)

 

Is this coming closer to what you had in mind?

 

edit: Hey, just realized the whole "do-once" stuff and button checking is no longer needed at all now that the whole "put into faction"/"remove from faction" business is also only happening once each time! (cleaned up the code accordingly)

Edited by DrakeTheDragon
Link to comment
Share on other sites

The only flaw with that script though is the fact that the message does not go away after a certain period of time (assuming the collar is still equipped). A couple of the doOnce variables were left in the other script, so I finished cleaning it up and updated it to this:

 

 


Scn ATPrisonerCollarScript

ref self
ref wearer

Begin GameMode

Set self To GetContainer

if ( wearer != 0 && wearer != self ) ; container changed and still has reference to previous wearer
       wearer.SetFactionRank ATPrisonerFaction, -1
       set wearer to 0 ; delete reference to previous wearer
endif

if ( self.IsActor == 0 ) ; item got dropped, previous wearer got removed from faction by previous check in this case, too
       Return
endif

set wearer to self ; store reference to current wearer 

if ( ( self.GetEquipped ATPrisonerCollar ) && (self.GetFactionRank ATPrisonerFaction < 0 ) )
       self.SetFactionRank ATPrisonerFaction, 0
       if wearer != player
               Message "NPC has been added to the faction", 5
       elseif wearer == player
               Message "You have been added to the faction", 5
       endif
elseif ( self.GetFactionRank ATPrisonerFaction >= 0 )
       self.SetFactionRank ATPrisonerFaction, -1
endif

End

 

 

As far as I can tell, the script is evaluating the code to say that as long as the "self.GetEquipped" condition is true, the message should not terminate. I have a couple of questions though: Could we use another block entirely within the same script (not using GameMode) that uses the other established variables (self and wearer) to send the message? Maybe something like

 


Begin OnEquip

if self != player
	Message "NPC has been added to the faction", 5
elseif self == player
	Message "You have been added to the faction", 5
endif

End

 

which I could simply put below the GameMode block. What would be the estimated behaviour for this?

 

Edit: I tested this added block of script and have at least some good news to report. The message appeared and disappeared in the five seconds allotted it, but there was never any message for the NPC character. Ha it's never that easy! But, that being said, can that block be improved upon to output the message for an NPC? Also, as another side note, I am still using Active Inventory to test this. My assumption is that 'self' is not holding the actor's reference between blocks.

Edited by kingtitan
Link to comment
Share on other sites

Hmm, I mixed up the Message you're using with a MessageBox call. My bad. That's different behavior for the two.

 

Yes, you can use this OnEquip approach just as fine.

 

Variables exist for the whole script. All blocks share the same and as such have access to the same values. Setting "wearer" to something in the GameMode block makes "wearer" also accessible from inside the OnEquip block.

 

The trouble with the Message call not appearing for an NPC perhaps lies within the fact that you cannot always compare "player" and "self". A condition like "self != player" is not always guaranteed to work. The WiKi advises to use "self.GetIsReference player == 0" instead. So maybe this will help. The same comparison worked for you before though, so that's not very likely the problem.

 

Oh, but I just realized "self" is not necessarily initialized in this case. It could help adding the "set self to GetContainer" line at the start of this block, too. Just so it is always initialized by all means.

 

And coming to think of it, it depends on the inner workings of the inventory exchange mod you're using if the OnEquip block will be called at all.

If the NPC itself decides to equip the item, so the mod only exchanges inventory contents, it will be fine.

But if the mod uses the old "EquipItem" calls to force-equip those items onto the NPCs, this will fail. EquipItem does "not" trigger the OnEquip event. (I learned that the hard way.)

 

 

I'm still clueless as to why the script I posted doesn't cease to display the Message though. Putting the wearer into the faction and only displaying the Message when this is not the case should work the same as a do-once condition. Technically it "is" a do-once condition. Yet, it still doesn't work as one... something must go wrong behind the scenes. Or the condition line is messed up but still compiles. For some cause the game doesn't like multiple function calls in a condition. It always mixes up what's a function parameter and what's another condition then.

Edited by DrakeTheDragon
Link to comment
Share on other sites

Well, I have a couple of interesting things to report. But first off the up to date code:

 

 


Scn ATPrisonerCollarScript

ref self
ref wearer

Begin GameMode

Set self To GetContainer

if ( wearer != 0 && wearer != self ) ; container changed and still has reference to previous wearer
       wearer.SetFactionRank ATPrisonerFaction, -1
       set wearer to 0 ; delete reference to previous wearer
endif

if ( self.IsActor == 0 ) ; item got dropped, previous wearer got removed from faction by previous check in this case, too
       Return
endif

set wearer to self ; store reference to current wearer 

if ( self.GetEquipped ATPrisonerCollar )
self.SetFactionRank ATPrisonerFaction, 0
self.EquipItem2 ATPrisonerCollar
else
self.SetFactionRank ATPrisonerFaction, -1

endif

End

Begin OnEquip self

set self to GetContainer

if self.GetIsReference Player == 0
Message "They are now in the faction", 5
elseif self.GetIsReference Player
Message "You are now in the faction", 5
endif

End

 

 

I did a bit of editing, for some reason one of the newer scripts was giving me strange behavior (the player would sometimes be in the faction and sometimes not). But I did have some interesting finds.

 

First off, just to test the actual validity of the script's logic, I added the collar to an NPC via console commands - the traditional additem ref 1 and equipitem2 ref 1. I highlighted that console command because it is an OBSE command (as I'm sure you are well aware) so that I could bypass the traditional equipitem behavior. As it is no surprise, the message fired correctly and the NPC was placed into the faction. Would it be good to post the code from the Active Inventory spell so that we can write this code in conjunction with it?

Edited by kingtitan
Link to comment
Share on other sites

Here is the Active Inventory script, if that helps anything:

 

 


scn xxU005SpellSCRIPT

ref xNpc
ref xInv

short xAccess

Begin ScriptEffectStart

if (IsInCombat == 0) && (GetDead == 0) && (IsActor == 1)
 set xNpc to getself
 set xAccess to 1
 xInBoxRef.Enable             ;set xInv to PlaceAtMe xInBox 1 0 -50
 xInBoxRef.SetOwnership  ;xInv.SetOwnership
else
 dispel xxUSP005
endif

End


Begin ScriptEffectUpdate

if (xAccess == 3)
 AddItem xURing 1
 EquipItem2 xURing
 RemoveItem xURing 1
 set xAccess to 4
endif

if (xAccess == 2 && MenuMode == 0)
 set xAccess to 3
 xInBoxRef.RemoveAllItems xNpc  ;xInv.  RemoveAllItems xNpc
 xInBoxRef.Disable                          ;xInv.Disable
endif

if (xAccess == 1)
 xNpc.RemoveAllItems xInBoxRef  ;xNpc.RemoveAllItems xInv
 xInBoxRef.activate player              ;xInv.activate player
 set xAccess to 2
endif

End

 

 

For now though, I think I am going to just write up a simple spell that adds and equips a collar on the NPC that it is cast upon.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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