Jump to content

Show Message Script Not Working As Intended


31277b

Recommended Posts

Heyo!

 

So, super simple, the script is working properly in the sense that the message(s) is appearing when I've interacted with the object, a dead npc. I intended for it to display the first message upon the players first activation and then show the second on any further attempt to interact with the body. However, with the current script, when activating the body, the first message appears and then immediately the second one. This occurs on any and all activations.

 

Heres the script!

---------------------------------------------------------------------------

short doonce

begin onActivate

set doonce to 0

If GetStage InitQuestID ==30 || Getstage InitQuestID ==40
if (getActionRef == player)
if (doonce ==0)
ShowMessage MetalMercExamine
player.additem caps001 25
set doonce to 1
endif
endif
endif
end

begin onActivate
if (doonce ==1)
ShowMessage LBodySearchCompletedMessage
endif

end
------------------------------------------------------------------------
Thanks for all of your help!

Link to comment
Share on other sites

The "OnActivate" is an "event". Creating two such blocks is why you are getting both messages on one event trigger. What you need to control them is just one "event" and a separate "flag" variable (e.g. "DoOnce") for each message. Just restructure your conditional test so you set "DoOnce" to a different value for each message when it is displayed. For example:

begin OnActivate
  ...
  If (InitQuestDoOnce == 0)
    display message1
    set InitQuestDoOnce to 1
  elseif (InitQuestDoOnce == 1)
    display message2
    set InitQuestDoOnce to 2
  else
    set InitQuestDoOnce to 0 ; or 3 or however you want to indicate both messages have been displayed
  endif
end

-Dubious-

Link to comment
Share on other sites

The "OnActivate" is an "event". Creating two such blocks is why you are getting both messages on one event trigger. What you need to control them is just one "event" and a separate "flag" variable (e.g. "DoOnce") for each message. Just restructure your conditional test so you set "DoOnce" to a different value for each message when it is displayed. For example:

begin OnActivate
  ...
  If (InitQuestDoOnce == 0)
    display message1
    set InitQuestDoOnce to 1
  elseif (InitQuestDoOnce == 1)
    display message2
    set InitQuestDoOnce to 2
  else
    set InitQuestDoOnce to 0 ; or 3 or however you want to indicate both messages have been displayed
  endif
end

-Dubious-

 

Haha, knew it was something super simple. Just started scripting / modding and, while I've had a few epiphanies on my own, I just couldn't see this one. Thanks a ton!

Link to comment
Share on other sites

The "OnActivate" is an "event". Creating two such blocks is why you are getting both messages on one event trigger. What you need to control them is just one "event" and a separate "flag" variable (e.g. "DoOnce") for each message. Just restructure your conditional test so you set "DoOnce" to a different value for each message when it is displayed. For example:

begin OnActivate
  ...
  If (InitQuestDoOnce == 0)
    display message1
    set InitQuestDoOnce to 1
  elseif (InitQuestDoOnce == 1)
    display message2
    set InitQuestDoOnce to 2
  else
    set InitQuestDoOnce to 0 ; or 3 or however you want to indicate both messages have been displayed
  endif
end

-Dubious-

 

Hey, I've implemented your suggestion however now It simply only displays the first message. I can't understand why.

 

I assumed that it was because of the fact that I had set doonce to 0 right after the onactivate event. However I've tested the script without it as well and it continues to only display the first message whenever activated. The reason as to why I haven't implemented the second else command within your suggestion is because the second message is supposed to say, "Another search of the body renders nothing of importance"

 

 

begin onActivate

 

set doonce to 0 <---(Have tried without this)

 

If GetStage InitQuestID == 30 || Getstage InitQuestID == 40

if (getActionRef == player)

if (doonce == 0)

ShowMessage Message1

set doonce to 1

elseif (doonce == 1)

ShowMessage Message2

endif

endif

endif

end

Edited by 31277b
Link to comment
Share on other sites

As you say you are a novice scripter, let's turn this into a lesson on how to work out (the term is "debug") such problems. Every scripter (programmer) has to do this. (All the "Tips" below are found under the "Scripting" section of the wiki "Getting started creating mods using GECK" article.)

 

First of all you need to pin down where in your script things are deviating from your expectations. This is typically done by adding "debug messages" at every point where the script offers the possibility of deviating from the expected course of action. These are typically where you test the values of variables (like "If/elseif" statements). Please see the TIP: Debugging data to file on how to produce a "console log file" of your test results. Usually you want to know the value of a variable both before your test, and after any lines in which it MIGHT be changed.

 

Please see 'TIP Assigning and Testing variables' and 'TIP Block Types Multiple vs Single Frame processing'. In the latter, pay attention to the definition of scope. Hint: "scope" is the basis of your problem.

 

Working through such problems is an essential part of developing your skills with scripting. If you are still stumped after doing your best, lay out what you have tried and I'll explain where you went astray or misunderstood.

 

-Dubious-

Link to comment
Share on other sites

As you say you are a novice scripter, let's turn this into a lesson on how to work out (the term is "debug") such problems. Every scripter (programmer) has to do this. (All the "Tips" below are found under the "Scripting" section of the wiki "Getting started creating mods using GECK" article.)

 

First of all you need to pin down where in your script things are deviating from your expectations. This is typically done by adding "debug messages" at every point where the script offers the possibility of deviating from the expected course of action. These are typically where you test the values of variables (like "If/elseif" statements). Please see the TIP: Debugging data to file on how to produce a "console log file" of your test results. Usually you want to know the value of a variable both before your test, and after any lines in which it MIGHT be changed.

 

Please see 'TIP Assigning and Testing variables' and 'TIP Block Types Multiple vs Single Frame processing'. In the latter, pay attention to the definition of scope. Hint: "scope" is the basis of your problem.

 

Working through such problems is an essential part of developing your skills with scripting. If you are still stumped after doing your best, lay out what you have tried and I'll explain where you went astray or misunderstood.

 

-Dubious-

 

Hello again, I was having a hard time understanding how to implement the debugging but I did try a few things from the other sections from the wiki you provided.

 

Please correct me if I'm wrong at any point, and again, thank you so much for your help :).

 

After having read the TIP regarding blocktypes, I had gathered, to my understanding, that OnActivate, a single frame blocktype, would reset QuestDoOnce to 0 upon activation of the object. Therefore, I would have to change the blocktype to GameMode as it would save the variable.

 

I'm slightly confused in regards to TIP Assigning and Testing variables, mainly because I wasn't sure if my previous attempts would fit under attempting to use the variable immediately after having assigned it.

 

My first attempt at rewriting the script had been

short questdoonce


begin GameMode

If GetStage InitQuestID ==30 || Getstage InitQuestID ==40
    if Activate Player
        if (questdoonce < 1)
        ShowMessage Message1
        set questdoonce to 1
        elseif (questdoonce > 0)
            ShowMessage Message2   
        endif
    endif
endif
end

Very quickly it was apparent this was not correct as when reaching the appropriate queststage, the body's inventory would open and not display any kind of message at all. It would also immediately reopen the inventory upon closing. I realized I forgot to input the refId so I put that in. I also added Return because, from my understanding, it would allow the script to frame needed to make the assigned variable available.

short questdoonce


begin GameMode

If GetStage LazloInitQuestID ==30 || Getstage LazloInitQuestID ==40
        if MyNpc.Activate Player
        if (questdoonce == 0)
        ShowMessage Message1
        set questdoonce to 1
        return
        elseif (questdoonce > 0)
            ShowMessage Message2
        endif
    endif
endif
end

This got rid of the previous issue, however now upon activation of the object it would open it's inventory and still not display the message.

 

At this point from what you've referred me to I'm assuming that in order to accomplish what I want, I need to use the GameMode Block due to it being capable of multiple frame processing. I'm also under the assumption that I need to incorporate a return function within the script so that, once I interact with the body and doonce ='s 1, it would ignore if (questdoonce == 1) and proceed down to the elseif.

 

 

Thanks again! I've been having a great time modding and would love to further develop my skills.

 

 

Link to comment
Share on other sites

You have correctly deduced the essence of your problem. Congratulations.

 

Speaking to your original "OnActivate" code example (which also applies generally to your modified example):

 

The solution is not necessarily to change the type of block you have your code in (possible, but not always feasible or desirable), but rather to move your "DoOnce" variable out of that "single frame block" (where it only has "local scope", constrained to that block of code and gets re-initialized to zero each time it runs) and into a "GameMode" block so it gets "script scope" and retains it's value from frame to frame and block to block. Which is why you should use a unique variable for different blocks if you want to ensure a common name like "DoOnce" does not get changed unintentionally by another block.

 

Variables declared OUTSIDE the BEGIN blocks retain their value between runs, and in save games. Variables declared INSIDE a BEGIN block will be reset to 0/null each time the script is run.

Declare all your variables at the top. If you declare them all over the place when you need them, you'll eventually regret it.

When you want to share variables across more than one script (effectively "global" in scope but not the same as GECK's designated "global variables"), create a simple "Quest" with a name indicating it's purpose (e.g. "<MyCurrentQuest>VAR"). It doesn't need to do anything (i.e. it can be completely empty), but you need to "tick/check" the "StartGame enabled" property checkbox.

 

Then create a new script to be run when that Quest starts, so name it something related like "<MyCurrentQuest>VARScript", and use the "Script Type" drop down list at the top right of the "Script Edit" window shortcut icons to designate it as "Quest" type.

Now define any variables you want to use across more than one script or permit others to read for other Mods. In a "variable declaration script" you do not need or want any "BEGIN/END" blocks. All you are doing is declaring variables.

 

Prefix your variable with their "function type" to avoid confusion when using them in other scripts. (See TIP Best Practice Type prefixes for Variables.)

 

Use "floats" (floating decimal values) or "ints" (integer values), but never "shorts". The less you have to type the better, and using a "non-int" alias in the GECK language will cause confusion if you do know another programming language. Don't expect a "short" to roll over at 32767 or 65535; it will roll over at 2-billion-something just like a regular signed integer. Use prefixes for for usage and functionality, not "datatype".

All numeric vars in the GECK language are stored as signed numbers (can be negative).

Now your later "GameMode" code has different problems.

 

Initializing a variable to it's normal default state (zero, or "" (null) for strings) does not seem to need to wait a frame. Assigning it a value can be done at any time (but there are always exceptions). It's just that "testing" for the assigned value may need to be delayed a frame to allow time for it's change to "take effect".

 

Blocks of all types are processed from the top down, each line completely at once. So if you assign a value in one "if / elseif" block and test it's value in a later "elseif" block, the code will be skipped to the "endif" statement as soon as the "true" statement block (the first "if / elseif" has finished processing. Subsequent "elseif / else" blocks will not get processed until the next frame in a multi-frame block.

 

One thing you need to pay attention to is when you "nest" conditions. Each condition test must be evaluated as "true" in order for any nested condition under it's "block" to then be evaluated also. As soon as an "endif" statement is encountered, the last series of "if / elseif /else" block statements is terminated, and since only one "true" block can exist for each nested block, that means the outer blocks will also terminate down to their "endif" statements. This is why it is so important to indent each level of nesting, in order to easily trace the test and termination flow.

 

"Activate" is a single frame event (just like "OnActivate"). In this instance, the "calling reference" is a body (technically a "container"). Note #5 on that function's description page applies. Until you close the container (which is a "menu" action), you cannot "activate" it again. Because your "message" is interfering with the container interface, use the "activation" of the container to set a flag, and process the messages outside of the activation block. Your criteria was two different messages for each time the body was interacted with. Detecting the body was "touched" does not require simultaneously displaying the message. A frame or two delay is acceptable.

 

Alternatively, you can replace the default "activation" process with your own. In which case you can display your message, force the player to acknowledge it, and only then proceed with the "open container" default action.

 

There is usually more than one way to handle things. It is generally best to keep it as simple as possible, but that may not be as simple as it would seem logical.

 

-Dubious-

Link to comment
Share on other sites

  • Recently Browsing   0 members

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