Jump to content

Scripting question


niston

Recommended Posts

Hi there

 

So I'm making this mod that has resources (furnitures) for settlers to mine, and that part works perfectly fine. Why, thank you BbaronX for your fabulous More Scavenging Stations mod!

 

Looking at the work Zorkaz has done, I made a custom script that allows the player to mine those resources, too. That works *mostly* quite well indeed, considering that it is my first ever attempt of writing anything from mostly scratch in Papyrus.

 

The problem that however remains to be solved is this: When there's already a settler working on such resource, and the player tries to mine it at the same time, the player wont enter furniture and instead get a message "you can't use this at this time". But the activation event still occurs and my script is thinking that the player is mining, when they are not!

 

To prevent this from happening, I have to do some sort of check on activation. So that I can detect when player tries to mine while a settler is already mining the furniture.

 

First, I tried IsFurnitureInUse(). Unfortunately, that'll always return true, because the furniture is of course not only InUse when a settler is working on it, but also when player has entered it by way of activation.

 

I'm right now using CanProduceForWorkshop(). If a settler is assigned, it'll return true and my script can then abort. While that sort of works in some very unsatisfactory way, I'm absolutely, totally not happy with this solution and the reasons are too numerous to list.

 

So I'm thinking: What if....I had a way to get a reference to the actor that is currently using my furniture...?

 

If I get back <none>, it would mean the furniture is not in use by anyone and my script can proceed.

If I get back <player>, then my script can proceed because it is the player that is using the furniture.

And if <some other reference> is returned, the script can abort, because the furniture is already in use by someone other than the player.

 

Yay!

But after looking for a while, I did not find any function or property that would return such a reference, to serve my purposes.

 

Yet, most certainly, such a thing must exist! How could it not! Can somebody thus simply point me in the right direction, please? Much appreciated, thank you!

 

If not, I figure I could always track activation / furniture exit by NPC (i.e. not player reference) and keep internal state. But I'd rather avoid doing this, if somehow possible at all.

Edited by niston
Link to comment
Share on other sites

If I understood correctly, your script starts doing something once the OnActivate event is triggered by the player, but if the object is in use the player doesn't actually get in to the furniture.

What you can do is register for the animation event called "idleChairSitting", so you make sure the script starts doing it's things when the player is actually "sitting" on the furniture.

 

For example, Instead of:

Event OnActivate()
    ;do something
EndEvent

use this:

Event OnActivate()
    RegisterForAnimationEvent(Game.GetPlayer(),"idleChairSitting")
EndEvent

Event OnAnimationEvent(ObjectReference akSource, string asEventName)
    If(akSource == Game.GetPlayer())
        If(asEventName == "idleChairSitting")
             ;do something
             UnregisterForAnimationEvent(Game.GetPlayer(),"idleChairSitting")
        EndIf
    EndIf
EndEvent
Link to comment
Share on other sites

Thanks DieFeM, yes that's right - The player mining starts with the OnActivate event, if akActionRef is Game().GetPlayer().

 

I'll give the event based solution a try.

 

Any side effects you'd like to warn me about, like, say, no OnAnimationEvent actually occuring after the RegisterForAnimationEvent() for some whatever reason? Also, what happens if RegisterForAnimationEvent() is called repeatedly, before the OnAnimationEvent fires?

Edited by niston
Link to comment
Share on other sites

Well, exactly what you said could happen actually, you may want to start a timer to unregister the animation in case the player don't trigger the animation event. It could happen that after activating it, and failing to get in to the furniture, the player sits on to other furniture and the event gets triggered.

 

Edit: to avoid registering for the animation multiple times you can make use of BlockActivation until it gets unregistered.

 

Edit 2: Actually using BlockActivation would fire the OnActivate anyway, so you would need to use a boolean variable and a conditional to avoid registering again until the timer ends.

 

For example:

Bool WaitingForTimer = False
Int TimerID = 101

Event OnActivate()
	If !WaitingForTimer
		RegisterForAnimationEvent(Game.GetPlayer(),"idleChairSitting")
		StartTimer(5, TimerID)
		WaitingForTimer = True
	EndIf
EndEvent

Event OnAnimationEvent(ObjectReference akSource, string asEventName)
	If(akSource == Game.GetPlayer())
		If(asEventName == "idleChairSitting")
			CancelTimer(TimerID)
			WaitingForTimer = False
			UnregisterForAnimationEvent(Game.GetPlayer(),"idleChairSitting")
                        ;do something
		EndIf
	EndIf
EndEvent

Event OnTimer(int aiTimerID)
	WaitingForTimer = False
	UnregisterForAnimationEvent(Game.GetPlayer(),"idleChairSitting")
EndEvent
Edited by DieFeM
Link to comment
Share on other sites

Hmm.. Is there any sort of limit on the number of running timers in the scripting environment? I'm asking because people could potentially have a lot of those mining resource (128 per settlement I think) and not sure if tons of running timers are a good thing. EDIT: Actually nevermind, only the one that has been activated by the player should be running at any time!

 

Regarding the second case, I see that the OnAnimationEvent has the akSource parameter. So if I'm not mistaken, I should be able to tell if the event source actually was my furniture (akSource == self) or if it came from some other furniture/whatever and should thus be disregarded by my code. EDIT: Hmm, no. akSource will actually be <player>. Have to think about this some more...

 

So, thank you =) I shall try this now.

Edited by niston
Link to comment
Share on other sites

Alright, so this is my code now:

 

https://pastebin.com/2FcGAkWX

 

It is extremely loosely based on the drinkfromFountain script, or whatever it's called.

 

Works reasonably well now, with the event on furniture enter. Better than before I think.

 

However... I'm back at sort of the same problem: When the animation event fires, I need some sort of way to check if it's actually the player using the particular mining furniture.

 

That is, if I assign an NPC, then start mining myself before the NPC arrives, then exit and sit on a nearby bench while the NPC walks up and commences mining... I end up getting stuff from the mine as the script thinks I'm now mining because the furniture is occupied by the NPC and the animation event occured in time because I sat down on the nearby bench.

 

Why, is there really no function that returns the actor that currently uses a specified furniture or animation marker? -.-

Edited by niston
Link to comment
Share on other sites

I resorted to adding that internal state variable I mentioned earlier. I basically set it when the furniture is activated by anything but the player, and reset it when the furniture is exited by anything but the player.

 

Seems a bit meh, but it allows me to filter the animation event: It certainly can't be due to the player mining the mine, while an NPC is actually working that mine.

 

Multiple activations are handled by the different states, as the event handler is only active in IDLE state.

 

As a bonus, I can now also exit the mining turn timer event handler, in case an NPC took over the mine during one of the timer's intervals.

 

From my limited debug testing, I found no detrimental side effects.

 

EDIT: This is probably the final code (for now), in case anyone cares:

 

https://pastebin.com/zmF8ziTw

 

 

Thanks again DieFeM, you've been very helpful!

Edited by niston
Link to comment
Share on other sites

heh, yeah I tried with the OnSit event, too :smile:

 

But the compiler would complain about the script being not native. Then, when I gave it the native modifier, all hell broke loose. So I gave up on that.

 

Here's my final-final (lol) version that appears to work just perfectly fine: https://pastebin.com/aYekJ00Z

 

Although one could probably make a perk for mining now, so player gets more material from mining, the more perk points they have. And then, with each 333 or so turns mined, give one perk point, where the cap would be at 999 turns - so 3 perk points. Maybe have 2 or so extra perk points only obtainable through magazines. "Making a profit from dirt" by whoever wrote the wasteland survival guides, and "Quarry Management & Operations - Best Practice Manual" by Mass Sand & Gravel. Heh!

 

But I'm actually more interested in learning about resource consumption first, as that will allow me to create -based on my MiningByNPC script- machinery that consumes resources from the settlement's workshop and adds its product there. Basically, a factory that keeps running while you're not in the factory's settlement. Which will be useful to continuously process all the mountains of gravel your miners mine all day. As long as the machine's script can acquire 4 gravel and perhaps 1 adhesive from the workshop, it will -in intervals- consume those, produce 4 concrete in turn and deposit them in the workshop.

 

In fact, I think the principle could likely be applied to actual contraptions workshop builders as well - If there was a method to determine whether the player is in the settlement factory builder is loaded or not - I guess Is3DLoaded() or whatever its called could be helpful there. The effect being that when you're at your factory, you can have all the stuff running over your crazy web of conveyor belts, but when the cell is unloaded because you travelled away, the background script takes over and production won't just grind to a halt (as long as the builder's background script finds the required resources in it's workshop).

Edited by niston
Link to comment
Share on other sites

Ok, so I looked at your OnSit handler again and I simply love the idea because that event is actually related to the furniture as well, and not only to the player.

 

So I incorporated your event handler into my code, and it works fine. Must be your use of Actor.OnSit, where I previously tried OnSit and failed.

 

I kept the OnActivate stuff, so I can easily tell when it's being used by an NPC and also kept myPlayer none in IDLE state.

 

See here: https://pastebin.com/jstxAEn6

Edited by niston
Link to comment
Share on other sites

  • Recently Browsing   0 members

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