Jump to content

MessageBox, what am I doing wrong?


Recommended Posts

Object script for a menu? IMHO, a very bad choice...

 

Hello Master Forli :)

I think this will be no problem, since he probably attach this script to Mimic pet.

So this 'object script' can have same level as Quest script ---> persistance object, companion

I suppose the menu itself is some interaction with Mimic pet and must exist in same cell space with pc, so the menu script is always run in GameMode

but, if he call the Mimic pet menu from different worldspace

yes....

this script should run in Quest rather than object script

Link to comment
Share on other sites

... are you aware I solved the problem as I mentioned in message #15?

 

The problem was that the script stop working after one frame of doing nothing and in the code I put in the first message this happened once the game went in MenuMode. One frame and script stopped.

 

Ah. Okay. I thought you meant that "the solution" was the one presented in the tutorial, but that you had not managed to crack some minor detail of it yet. Sorry. Great to hear you got it sorted out, I have been a bit busy again. My gaming PC is still tucked away in a bag, I have been busy writing a simple mod installing tool for Morrowind, because my laptop chokes on everything newer. It is coming along so nicely that I probably will not setup my gaming PC before the tool is finished - after which I can finally play Morrowind.

 

If someone is worried about my Oblivion and Skyrim projects: I will continue them when I have the time, worry not, I am not going anywhere. :P

 

As for messageboxes, as forli said, using an activator or another sort of a reference in the game world is probably one of the most unintuitive ways to achieve it (as in, a constant menu not tied to spell effect durations, and not tied to ref loaded/unloaded states). If the tutorial works, that is great, but there are better ways to do it, too. As for quests maybe having their variables reset (I have not thought if that is the case, but it was said in the tutorial I think): the menu quest could be separate from the actual core mod maintenance quest, and the variables could always be kept in the core maintenance quest, also to keep things in one place.

 

Anyway, good to hear you got it sorted. Apologies for my reading comprehension failure. :blush:

Link to comment
Share on other sites

Actually, I disagree.

 

A remote activator looks a perfect way. Easy to start with a simple script line and easy to stop with a simple variable assignment; it resist cell resets and pretty much work as expected. The whole problem was because **I** was not aware of the game modes; not for anti-intuitive behaviors.

Link to comment
Share on other sites

Little comparison
remove activator <--> quest

Easy to start
Set working to 1 <--> StartQuest MyMenuQuest

Easy to stop
Set working to 0 <--> StopQuest MyMenuQuest

Resist cell resets
No, if the attached object is erased by cell reset <--> Yes, always. Doesn't care about cell resets

Require assignments every frame to keep it running
Yes <--> No

Run and consume CPU power even when not used
Yes (check "Working" every frame) <--> No (doesn't run at all)

Can adjust script running speed to decrease performance hit
No <--> Yes (fQuestDelayTime)

Link to comment
Share on other sites

What can I say...

Perhaps you should read it the tutorial and the see the points they make there. It's an interesting read.



Some of your points are a little weird though.

Easy to start and stop, I can agree. In the tutorials they mention that Quests have have no clear start. But the workaround is so easy, that is not really an issue.

Resist cell resets, activators use a persistent reference so they do resist.

 

Require assignment every frame. Yes, it does... what the problem? What is the time needed to assign a constant to a variable? You speak about speed, did you measure? How quests are implemented, anyway? The fact we cannot see the details it does not mean it's free.

 

Run and consume CPU when not used? I am failing to get what you mean, the activator stops running in one frame when you stop it.

 

Can adjust speed? Actually, you can with ease using GetSecondsPassed. Quests also need to use a timer underneath (to execute every fQuestDelayTime seconds) that is probably implemented the same way.

 

 

However, as also stated in the tutorial each approach has advantages and disadvantages. So I guess to everyone it's own at the end it's more a matter of habit and taste.

Edited by zdswulyx
Link to comment
Share on other sites

The game is built with C++ code, but CS Scripts are not C++ code. Every command you use in CS Script runs tons of C++ commands, which makes CS Scripts very inefficient (look at the OBSE source code if you don't believe me). Since we can't optimize internally the commands, we can only optimize our scripts and our algorithms. It's true many other scripts (not just quests) are poorly optimized, but this is not an excuse to write more poorly optimized ones.

The Oblivion script engine is poorly optimized, and it's single thread: it only runs on a single core of your CPU (don't ask: there's no way to make it multi-thread without the Oblivion source code, which we will never have, and without breaking half of scripts in the entire community). With the tons of heavy scripted mods out there, the engine is already overloaded and the CPU core which must execute all those script is already busy enough. Adding more pounds won't make anything better. Even modern CPU can be crippled by this, as they are powerful, but split the power among many cores. Since Oblivion put the script engine on one core, it only uses a fraction of the power of the CPU. It's counter-intuitive, but it's possible old CPU which concentrate their power in only one or two cores may run Oblivion better than new CPU.

 

 

Require assignment every frame. Yes, it does... what the problem? What is the time needed to assign a constant to a variable? You speak about speed, did you measure? How quests are implemented, anyway? The fact we cannot see the details it does not mean it's free.

Normally, assigning a variable every frame is not expensive, but assigning a variable in CS Scripts is 10 times more expensive than assigning a variable in C++ code. You're right it's not a performance killer... but why waste your precious CPU? As stated above, CPU is a valuable resource in Oblivion.

 

 

Run and consume CPU when not used? I am failing to get what you mean, the activator stops running in one frame when you stop it.

While an object is loaded, it keeps running every frame all its GameMode/MenuMode blocks (depending if in game or menu mode), either directly attached to the object or attached to any object in its inventory.

You can't "stop" the activator. You only set a variable to prevent it runs the code. The check you make at the beginning "If Working == 1" is necessary as that GameMode block runs (which means it's not stopped). But still, the game need to allocate all necessary resources, call the GameMode script, check that condition with the fast ( :confused: ) CS Scripts, reach "End" and deallocate resources. Repeat every frame.

Sure, the performance hit is far from being noticeable, but the sum of many poorly optimized scripts may cause a noticeable (and sometimes, serious) performance hit. In programming you always need to think about optimization.

 

Even if a quest is always loaded, it only runs its script when flagged as Running.

When a quest is stopped, its script is not even considered, so it cause no performance hit at all.

Think about the Running flag as an internal "Working" variable, but directly checked with C++ instead of CS Scripts, and before any resource is allocated for the script. If the Running flag is false, no resource is allocated/deallocated at all and no CS command is executed.

 

 

Can adjust speed? Actually, you can with ease using GetSecondsPassed. Quests also need to use a timer underneath (to execute every fQuestDelayTime seconds) that is probably implemented the same way.

Not implemented in the same way!

GetSecondsPassed is a CS command, and as you learnt, it costs tons of C++ commands.

This means the script effectively run every frame to update the timer and check whatever it's time to run the rest of the code.

Every frame the cost is: allocate + GetSecondsPassed (script) + update timer variable (script) + check if time passed (script) + deallocate.

 

Quests are timed internally by Oblivion, again, with C++ code and before any resource is allocated. If the time has not yet come, no resource is allocated/deallocated and the script doesn't run at all.

Every frame the cost is: get time passed (C++) + update timer variable (C++) + check if time passed (C++).

 

 

 

One final advice:

If Working == 1

"== 1" is not necessary. The game consider TRUE any condition whose result is != 0.

Basically, the game does this:

If (<condition>) != 0

In your case it's like this

If (Working == 1) != 0

As you may guess, "== 1" is a redundant operation in this case, since Working is either 0 or 1.

If Working could assume value 2, and you want to consider "2" differently, then == 1 and == 2 are necessary. but in your case you can just drop that == 1. It makes the code more readable: "If Working". Ah, and there's a very tiny tiny tiny performance gain too!

Edited by forli
Link to comment
Share on other sites

What is your computer science experience? Your post is a strange mixture of interesting points, misconceptions, and weird assumptions...

 

Just one:

Quests are timed internally by Oblivion, again, with C++ code and before any resource is allocated. If the time has not yet come, no resource is allocated/deallocated and the script doesn't run at all.

Every frame the cost is: get time passed (C++) + update timer variable (C++) + check if time passed (C++).

We cannot know for sure without the code, but how do you think in C++ the timing would happen? As you said, you not allow to use any resource. Edited by zdswulyx
Link to comment
Share on other sites

Thinking about it... quests are always loaded (they are persistent objects), so there's no need to allocate/deallocate anything, but I admit this can be true also for the remote activator, if flagged as persistent.

 

 

Still, if we take a simple CS Script code like this:

Begin GameMode
...
   If Working
    Let timer -= GetSecondsPassed
        If timer <= 0   
            Let timer := 0.25   ;0.25 seconds
            ;do stuff
        EndIf
    EndIf
...
End
The command "Let", alone, runs the following wall of C++ code:

 

 

static bool Cmd_Let_Execute(COMMAND_ARGS)
{
    ExpressionEvaluator evaluator(PASS_COMMAND_ARGS);
    evaluator.ExtractArgs();
    return true;
}
ExpressionEvaluator::ExpressionEvaluator(COMMAND_ARGS) : m_opcodeOffsetPtr(opcodeOffsetPtr), m_result(result),
m_thisObj(thisObj), script(scriptObj), eventList(eventList), m_params(paramInfo), m_numArgsExtracted(0), m_baseOffset(0),
m_expectedReturnType(kRetnType_Default)
{
    m_scriptData = (UInt8*)arg1;
    m_data = m_scriptData + *m_opcodeOffsetPtr;
 
    memset(m_args, 0, sizeof(m_args));
 
    m_containingObj = (TESObjectREFR*)arg3;
    m_baseOffset = *opcodeOffsetPtr - 4;
 
    m_flags.Clear();

    PushOnStack();
}
bool ExpressionEvaluator::ExtractArgs()
{
    UInt32 numArgs = ReadByte();
    UInt32 curArg = 0;
 
    while (curArg < numArgs)
    {
        ScriptToken* arg = Evaluate();
        if (!arg)
            break;

        m_args[curArg++] = arg;
    }

    if (numArgs == curArg) // all args extracted
    {
        m_numArgsExtracted = curArg;
        return true;
    }
    else
        return false;
}

//The function Evaluate() used in this last piece of code is even bigger than this whole spoiler!

 

And this is only the Let command!

I can't take the If command, because it's a Vanilla command and so we don't have its code.

 

 

while a possible implementation of the check for the Running flag and the fQuestDelayTimer for the quests is as simple as the following one (and the entire code is much faster than the single Let command above):

#include <chrono>
#include "something else"

using namespace chrono;
milliseconds currentTime;

void checkAndRunQuest(Quest& quest, other params){
    ...
    if (running) {
        currentTime = duration_cast<milliseconds>(system_clock::now ().time_since_epoch ());
        if (quest.nextRunTime <= currentTime){
            quest.nextRunTime = currentTime + quest.getVariable["fQuestDelayTime"];
            ;run GameMode/MenuMode block
        }
    }
}
The difference is big enough to make me think about optimization for every single line I write with CS scripts, as each one may run a wall of text like that. Edited by forli
Link to comment
Share on other sites

  • Recently Browsing   0 members

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