Quirks of the Skyrim engine #2

in nsfw •  7 years ago 

Welcome to another episode of...

...drum roll...

...Quirks of the Skyrim engine!

 

Today, we'll look at:

The mysterious case of the lying event

Because I try not to go overly technical and keep it on a level that even non-modders can understand, I first need to do some explaining to set up the story. If you already know what "events" are in terms of Skyrim and Papyrus, feel free to skip the following two sections.

What's an event?

To make a long story short and probably oversimplify it grossly, an event is a special function that is called whenever something specific happens in the game.

If you don't know what a "function" is, just think of it as a predefined set of instructions that you can call whenever you want something to happen. Sort of like a cooking or baking recipe. Every time you want your computer to make you an apple pie, you can repeat the same instructions (take flour, add milk, add eggs, etc. etc.) Or you could just give these instructions only once and give them a unique name. Like: "Ok, here's the recipe for apple pie." And then every time you want an apple pie, you can just tell the computer: "Make me an apple pie!" And the computer will then look up the instructions you provided earlier and execute them. Again, and again, and again. No need to write out the instructions every single time. Huge time saver and also much less prone to errors (only one single copy of the recipe you could screw up). Of course, in real life, instead of a pie, you get a computation result or something like that. Maybe pi instead of pie (see what I did there?)

But ideally, it would be something rubber related. Who would ever want to waste a perfectly good function on anything else, right?

And an event is very similar. Only that it's not you who is calling it, it's the engine itself. Whenever that particular event occurs, it will call that function in your mod. All by itself.

But... why?

This is little bit off-topic, but ok. Let's take an example. There's an event called "OnSit" that is triggered every time the actor that it is attached to sits on something. If you didn't have that event but you wanted to respond to the actor sitting down, then you'd have to constantly query the game about it.

Is the actor sitting? No? Ok, is he sitting now? Still no? How about now? He's surely sitting now, right? No? And now?

This would use up a lot of resources because the engine would have to constantly answer your questions instead of actually rendering the game (and the result would be "no" most of the time). That's called "polling", and generally, you don't want it because it's bad for performance. No rule without exceptions, of course, but I'm not going to go into that here.

So instead of having to act like the annoying child in the back seat of the car, constantly asking whether we're there yet, the game engine says: "Ok, you just sit down and wait. And I'll inform you when X happens!"
Unfortunately, that X can't be anything. The engine only knows about events that the programmers at Bethesda decided to provide. Sometimes, you find yourself in the situation that you want to react to something in the game, but there's no event available for it. And then you must find alternative solutions (or scrap the idea altogether).

The event OnCombatStateChanged

The event I want to talk about today is OnCombatStateChanged. And it does exactly that. It notifies you whenever an actors changes their combat state. When they enter into combat, the event fires. When they leave combat, the event fires. When they are alarmed and search around but have not found you yet, the event fires. And so on.

I'm sure you can spot the problem with having one single event for so many different combat states. What if you need to know what actually triggered the event? Is the actor entering combat and about to chop off the player's head? Or has he just sheathed his sword and decided to leave you alone? It's an important difference, right?

But fear not, for the developers thought of that. That is why they added a parameter called aeCombatState. This is just an integer number that you can use in your event function and the game will set it for you to reflect what actually happened. According to the CK wiki documentation, it can be either 0, or 1, or 2. A 0 means the actor isn't in combat, a 1 means he is, and 2 means he's searching around for whatever alerted him.

Sounds nice, so what's the problem?

Well, there would not be a problem. If the parameter aeCombatState actually worked as advertised. Thing is, it doesn't. Not completely, anyway.

Because during my tests with it, aeCombatState was never, ever set to 2. Even if the actor was still actively searching for the player and had not yet entered real combat, aeCombatState would nonetheless always have the value of 1.

Which, if you know about the Rubber Spy quest in TIR, is a bit of a problem. Because I only want the mission to fail if a guard actually detects the player. Not if they just thought they heard something. And there's no other or better way to do it (that I know of). This is where we come back to what I mentioned earlier about Skyrim not having an event for every conceivable situation, but only a select few. There is no "OnActorAIDetectedPlayer" event or something like that. There's an event that fires when there's a line of sight between the NPC and the player, but it doesn't take the AI into consideration. It's triggered whenever a straight line can be drawn from person A to person B without going through something. Even when when person A has his back turned towards person B. Not what you want for a sneak mission.

The only option I found was to make the guards aggressive so that they attack the player whenever they see them. And then wait for that attack to trigger a combat state change.

And then you test it and see that it doesn't work and you spend an embarrassing amount of time looking for what you did wrong. Only to find out that you probably didn't do anything wrong and the engine is just bugged.

Solution

Well, it's more of a workaround than a solution. The parameter is just always wrong when the actor is in search mode. There's nothing I can do about it.

But there is a function you can call to actively query the combat state of any actor. It's called GetCombatState and it happens to give you the correct value for the actor's combat state.

So the solution is to wait for the OnCombatStateChanged event, then ignore the aeCombatState parameter completely (even though it should have all the information you need) and instead query the engine a second time about the real combat state of the actor. And then act based on that.

You can't deduce or reasonable guess it. You just have to learn it.

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Congratulations @greyspammer! You received a personal award!

1 Year on Steemit

Click here to view your Board

Do not miss the last post from @steemitboard:

SteemWhales has officially moved to SteemitBoard Ranking
SteemitBoard - Witness Update

Support SteemitBoard's project! Vote for its witness and get one more award!

Congratulations @greyspammer! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 2 years!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Vote for @Steemitboard as a witness to get one more award and increased upvotes!