niston Posted February 15, 2019 Share Posted February 15, 2019 (edited) 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 February 15, 2019 by niston Link to comment Share on other sites More sharing options...
DieFeM Posted February 15, 2019 Share Posted February 15, 2019 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 EndEventuse 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 More sharing options...
niston Posted February 15, 2019 Author Share Posted February 15, 2019 (edited) 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 February 15, 2019 by niston Link to comment Share on other sites More sharing options...
DieFeM Posted February 15, 2019 Share Posted February 15, 2019 (edited) 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 February 15, 2019 by DieFeM Link to comment Share on other sites More sharing options...
niston Posted February 15, 2019 Author Share Posted February 15, 2019 (edited) 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 February 15, 2019 by niston Link to comment Share on other sites More sharing options...
niston Posted February 15, 2019 Author Share Posted February 15, 2019 (edited) 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 February 15, 2019 by niston Link to comment Share on other sites More sharing options...
niston Posted February 15, 2019 Author Share Posted February 15, 2019 (edited) 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 February 15, 2019 by niston Link to comment Share on other sites More sharing options...
DieFeM Posted February 15, 2019 Share Posted February 15, 2019 I didn't tested it, but maybe this would do the job: https://pastebin.com/e3ViVT3p Link to comment Share on other sites More sharing options...
niston Posted February 15, 2019 Author Share Posted February 15, 2019 (edited) 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 February 15, 2019 by niston Link to comment Share on other sites More sharing options...
niston Posted February 15, 2019 Author Share Posted February 15, 2019 (edited) 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 February 20, 2019 by niston Link to comment Share on other sites More sharing options...
Recommended Posts