xGoReTHeRoNx Posted April 14, 2021 Share Posted April 14, 2021 Hi Nexus community! I've been out of modding a lot of time by now, and I need some help scripting a menu, simple in terms of purpose, but complicated when I have to write the script of it.My knowledge about Papyrus and programming in general is zero and normally I avoid as much as possible the scripting thing. But in this case, I see myself forced to use it, since there's no other way to do it properly. I have an idea that is a custom placeable shrine that gives you some blessings all at a time. I want that shrine to be able to be dropped and picked up whenever I want. The thing that came to my mind was through a menu that, when you activate it, it'll show 3 options: Receive the custom blessing, pick up the shrine, or cancel. I've been using a mod that I know of, that does the thing I want partially. It's called Your Market Stall, but its programming seems a bit complicated. I've managed to drop the shrine from my inventory, and stays put the way I want, and when I click on it, the menu shows correctly, and it gives me the blessing, but it fails when has to pick up the shrine or simply, cancelling the action. This is the code I've got. I'm learning from imitation, and it is far from being finish, so don't be hursh please. Scriptname ArtisanShrineScript extends ObjectReference MiscObject Property ArtisanShrine Auto Spell Property ArtisanSpellFortAlchemy Auto Spell Property ArtisanSpellFortEnchanting Auto Spell Property ArtisanSpellFortSmithing Auto Message Property ArtisanShrineMenu Auto Message Property ArtisanShrineAddBlessMsg Auto Message Property ArtisanShrineRemoveBlessMsg Auto Message Property ArtisanShrineRemoveFailMsg Auto Bool Property isPlaced = false Auto Hidden Event onLoad() if (is3DLoaded()) blockActivation(true) endIf endEvent Event OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer) if (akOldContainer == Game.getPlayer()) ObjectReference shrine = placeAtMe(ArtisanShrine, 1, false, true) shrine.setPosition(shrine.getPositionX(), shrine.getPositionY(), akOldContainer.getPositionZ()) shrine.setAngle(180, 180, akOldContainer.getAngleZ()) float px = shrine.getPositionX() float py = shrine.getPositionY() float pz = shrine.getPositionZ() float pA = shrine.getAngleZ() float distance = 50 distance = 60 shrine.enable() disable() delete() else disable() setPosition(getPositionX(), getPositionY(), akOldContainer.getPositionZ()) setAngle(180, 180, akOldContainer.getAngleZ()) enable() endIf endEvent int Button Event OnActivate(objectReference akActionRef) If akActionRef == Game.GetPlayer() Button = ArtisanShrineMenu.show() if Button == 0 ArtisanSpellFortAlchemy.Cast(akActionRef, akActionRef) ArtisanSpellFortEnchanting.Cast(akActionRef, akActionRef) ArtisanSpellFortSmithing.Cast(akActionRef, akActionRef) if akActionRef == Game.GetPlayer() ArtisanShrineRemoveBlessMsg.Show() ArtisanShrineAddBlessMsg.Show() elseif Button == 1 Event OnActivate(ObjectReference akActivator) if(isPlaced == FALSE) if akActivator.getItemCount(ArtisanShrine) >= 1 isPlaced = TRUE self.getLinkedRef().enable() (akActivator as actor).removeItem(ArtisanShrine, 1) else ArtisanShrineRemoveFailMsg.show() endif else isPlaced = FALSE self.getLinkedRef().disable() (akActivator as actor).addItem(ArtisanShrine, 1) endif endEvent As you can see it's a complete mess, but I know that someone with programming knowledge could help me. I've been with this several days with no luck at all.Any help would greatly appreciated. Link to comment Share on other sites More sharing options...
maxarturo Posted April 14, 2021 Share Posted April 14, 2021 (edited) You need to do this with 2 objects and 2 scripts. 1) The MISC object that upon dropping it from your inventory will place a SHRINE ACTIVATOR. Script on the MISC Object: Activator Property ShrineACTIVATOR Auto EVENT OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer) If ( akOldContainer && !akNewContainer ) ;The MISC has been droped and it's not in any containers float az = Game.GetPlayer().GetAngleZ() ObjectReference shrine = Self.placeAtMe(ShrineACTIVATOR, 1, false, true) Self.DisableNoWait() shrine.SetAngle(0.0, 0.0, az + 180.0) shrine.MoveTo(Game.GetPlayer(), 200.0 * Math.sin(az), 200.0 * Math.cos(az), 0.0, false) Self.Delete() endIf ENDEVENT 2) The SHRINE ACTIVATOR will handle the MENU. The script on the ACTIVATOR MiscObject Property ArtisanShrine Auto Spell Property ArtisanSpellFortAlchemy Auto Spell Property ArtisanSpellFortEnchanting Auto Spell Property ArtisanSpellFortSmithing Auto Message Property ArtisanShrineMenu Auto Message Property ArtisanShrineAddBlessMsg Auto Message Property ArtisanShrineRemoveBlessMsg Auto ;;; Message Property ArtisanShrineRemoveFailMsg Auto ;;; This is not used anywhere on your script EVENT OnActivate(objectReference akActionRef) If ( akActionRef == Game.GetPlayer() ) Int iButton = ArtisanShrineMenu.show() If iButton == 0 ; Cast Shrine's spells and show messages ArtisanSpellFortAlchemy.Cast(akActionRef, akActionRef) ArtisanSpellFortEnchanting.Cast(akActionRef, akActionRef) ArtisanSpellFortSmithing.Cast(akActionRef, akActionRef) ArtisanShrineRemoveBlessMsg.Show() ArtisanShrineAddBlessMsg.Show() ElseIf iButton == 1 ; Add Misc Shrine to inventory an Delete this activator (akActionRef as Actor).AddItem(ArtisanShrine, 1) Self.Disable() Self.Delete() ElseIf iButton == 2 Return ; Do Nothing EXIT Button EndIf EndIf ENDEVENT * I did it on my lunch break, so sorry in advance for any 'Typos' if there are any... I had no time to checked it. Have a happy Modding. Edited April 14, 2021 by maxarturo Link to comment Share on other sites More sharing options...
xGoReTHeRoNx Posted April 14, 2021 Author Share Posted April 14, 2021 Don't worry buddy! Thanks so much for answering! I'll try it as soon as I'll be able to do it. And there's no need to apologize if there is any typo. If I have any problem understanding or whatever, I'll return here. Cheers! Link to comment Share on other sites More sharing options...
xGoReTHeRoNx Posted April 14, 2021 Author Share Posted April 14, 2021 (edited) Hey Maxarturo! Well, I made a new pair of scripts and I couldn't say if it works or not, since, sadly, when I go to the inventory and I drop the shrine, it stands in the air, I recover it, I drop it again and it disappears in front of me. What could be the problem? Your code seems correct. Oh, a stupid question that came to my mind earlier: the second script, is it mandatory that has to be inside of an activator type object or can be used a miscellaneous object instead? I tried both without luck, but I was curious. Edited April 14, 2021 by xGoReTHeRoNx Link to comment Share on other sites More sharing options...
maxarturo Posted April 14, 2021 Share Posted April 14, 2021 (edited) You need to create 2 objects: 1) The first has to be a "MISC" object, and you add the first script to it in the base object. (Edit Base and add the script there) * The object should get imidiately disable after you drop it, but if you want the object to have "Havok Behaivior", then: The Misc object in order to have "Havok" behavior you need to edit it in NifSkope and add to its collision the corresponding data. Go to: - bhkCollisionObject - bhkRigidBody V Havok Col Filter > Layer > CLUTTER Havok Col Filter Copy > Layer > CLUTTER Motion System > MO_SYS_BOX_STABILIZED Quality Type > MO_QUAL_MOVING Now you add the object into CK as a NEW MISC Object. 2) You need to create an ACTIVATOR using the meshe's file path, so that the new activator can show up in game using your shrine 3D model, in this activator you add the second script in the base object. (Edit Base and add the script there) * Don't forget to fill in correctly all the properties in both scripts, in both objects. I hope it helps. Edited April 14, 2021 by maxarturo Link to comment Share on other sites More sharing options...
xGoReTHeRoNx Posted April 15, 2021 Author Share Posted April 15, 2021 Hey! I did the thing that you mentioned with NifSkope. Then, using your scripts and attaching properly the properties, it didn't work. The shrine keeps disappearing when I drop it. But, not all are bad news. Using your second script, and recycling the first part of my code (not mine, since I found it in another mod), it works smoothly. So, the thing ended like this in the Misc object, and its script: Scriptname ArtisanShrineMiscScript extends ObjectReference Activator Property ArtisanShrineActivator Auto Event OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer) if (akOldContainer == Game.getPlayer()) ObjectReference shrine = placeAtMe(ArtisanShrineActivator, 1, false, true) shrine.setPosition(shrine.getPositionX(), shrine.getPositionY(), akOldContainer.getPositionZ()) shrine.setAngle(180, 180, akOldContainer.getAngleZ()) float px = shrine.getPositionX() float py = shrine.getPositionY() float pz = shrine.getPositionZ() float pA = shrine.getAngleZ() float distance = 50 distance = 60 shrine.enable() disable() delete() else disable() setPosition(getPositionX(), getPositionY(), akOldContainer.getPositionZ()) setAngle(180, 180, akOldContainer.getAngleZ()) enable() endIf endEvent And, inside of the Activator object it's your script: Scriptname ArtisanShrineActivatorScript extends ObjectReference MiscObject Property ArtisanShrineMisc Auto Spell Property ArtisanSpellFortAlchemy Auto Spell Property ArtisanSpellFortEnchanting Auto Spell Property ArtisanSpellFortSmithing Auto Message Property ArtisanShrineMenu Auto Message Property ArtisanShrineAddBlessMsg Auto Message Property ArtisanShrineRemoveBlessMsg Auto EVENT OnActivate(objectReference akActionRef) If ( akActionRef == Game.GetPlayer() ) Int iButton = ArtisanShrineMenu.show() If iButton == 0 ; Cast Shrine's spells and show messages ArtisanSpellFortAlchemy.Cast(akActionRef, akActionRef) ArtisanSpellFortEnchanting.Cast(akActionRef, akActionRef) ArtisanSpellFortSmithing.Cast(akActionRef, akActionRef) ArtisanShrineRemoveBlessMsg.Show() ArtisanShrineAddBlessMsg.Show() ElseIf iButton == 1 ; Add Misc Shrine to inventory an Delete this activator (akActionRef as Actor).AddItem(ArtisanShrineMisc, 1) Self.Disable() Self.Delete() ElseIf iButton == 2 Return ; Do Nothing EXIT Button EndIf EndIf ENDEVENT I'm curious to know why your first script isn't working as expected, but, as I said, I tried it with the old one and works perfectly along with your script. That being said, thanks so much for your time and help! I'll give you credit for this and the same thing goes for the other pal that created Your Market Stall. Take care! Cheers! Link to comment Share on other sites More sharing options...
ReDragon2013 Posted April 15, 2021 Share Posted April 15, 2021 (edited) you wrote: "I'm curious to know why your first script isn't working as expected"maxarturo: ObjectReference shrine = Self.placeAtMe(ShrineACTIVATOR, 1, false, true) ; non-persistent, but disabledxGoReTHeRoNx: ObjectReference shrine = placeAtMe(ArtisanShrineActivator, 1, false, true) shrine.enable()and here is the wiki page: https://www.creationkit.com/index.php?title=PlaceAtMe_-_ObjectReference activator script Scriptname ArtisanShrineActivatorMenuScript extends ObjectReference ; https://forums.nexusmods.com/index.php?/topic/9895908-need-some-help-scripting-a-menu-with-three-options/ MiscObject PROPERTY ArtisanShrine auto Spell PROPERTY ArtisanSpellFortAlchemy auto Spell PROPERTY ArtisanSpellFortEnchanting auto Spell PROPERTY ArtisanSpellFortSmithing auto Message PROPERTY ArtisanShrineMenu auto Message PROPERTY ArtisanShrineAddBlessMsg auto Message PROPERTY ArtisanShrineRemoveBlessMsg auto Bool bAlive = TRUE ; *T* ; -- EVENTs -- 2 EVENT OnCellDetach() ; in case, player ignores the activator IF ( bAlive ) bAlive = False ; *** self.DisableNoWait() self.Delete() ENDIF ENDEVENT EVENT OnActivate(ObjectReference akActionRef) IF (akActionRef == Game.GetPlayer() as ObjectReference) myF_Menu(akActionRef) ENDIF ENDEVENT ; -- FUNCTION -- ;-------------------------------------- FUNCTION myF_Menu(ObjectReference oRef) ; outsourced code for better overview ;-------------------------------------- int i = ArtisanShrineMenu.Show() ; show main menu for selection IF (i < 1) RETURN ; - STOP - 0=cancel, nothing to do ENDIF ;--------------------- IF (i == 1) ; 1=blessing otion, Cast Shrines spells and show messages ArtisanShrineRemoveBlessMsg.show() ArtisanSpellFortAlchemy.Cast(oRef, oRef) ArtisanSpellFortEnchanting.Cast(oRef, oRef) ArtisanSpellFortSmithing.Cast(oRef, oRef) ArtisanShrineAddBlessMsg.show() ELSE ; 2=take the shrine, Add MiscObject Shrine to inventory IF (oRef.GetItemCount(ArtisanShrine as Form) < 1) oRef.AddItem(ArtisanShrine as Form, 1) ENDIF bAlive = False ; *** self.DisableNoWait() self.Delete() ; delete this created activator ENDIF ENDEVENT object script Scriptname ArtisanShrineScript extends ObjectReference ; https://forums.nexusmods.com/index.php?/topic/9895908-need-some-help-scripting-a-menu-with-three-options/ Activator PROPERTY ShrineACTIVATOR auto ; -- EVENTs -- 2 EVENT OnInit() Debug.Trace(" OnInit() - has been called for " +self) ENDEVENT EVENT OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer) IF ( akNewContainer ) RETURN ; - STOP - item transfer only ENDIF ;--------------------- IF (akOldContainer == Game.GetPlayer() as ObjectReference) ; shrine was dropped by player myF_Action(akOldContainer) self.DisableNoWait() self.Delete() ENDIF ENDEVENT ; -- FUNCTION -- ;--------------------------------------------- FUNCTION myF_Action(ObjectReference playerRef) ;--------------------------------------------- objectReference oRef = self.PlaceAtMe(ShrineACTIVATOR as Form, 1, False, TRUE) ; oRef = shrine ; placed non-persistent and disabled float f = 200.0 ; distance float aZ = self.GetAngleZ() + 180.0 ; add an angle of 180 degree float fx = Math.SIN(aZ) * f float fy = Math.COS(aZ) * f oRef.MoveTo(self, fx, fy, 0.0, False) ; move placed activator into proper position oRef.SetAngle(0.0, 0.0, aZ) oRef.Enable() ; enable activator to be touchable by player ENDFUNCTION Edited April 15, 2021 by ReDragon2013 Link to comment Share on other sites More sharing options...
xGoReTHeRoNx Posted April 15, 2021 Author Share Posted April 15, 2021 (edited) Well, as I said, I did it by imitation, so I always knew that it wouldn't be perfect from my part. And if wouldn't be any comments, I'd be doomed because my knowledge about this is zero. I appreciate the directions tho, and thanks both for the help. And others will do, I'm sure. Cheers! Edited April 15, 2021 by xGoReTHeRoNx Link to comment Share on other sites More sharing options...
maxarturo Posted April 15, 2021 Share Posted April 15, 2021 Well... sorry, but as i said "I did it on my lunch break" and i missed the "bool abInitiallyDisabled". The correct script should be: Activator Property ShrineACTIVATOR Auto EVENT OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer) If ( akOldContainer && !akNewContainer ) float az = Game.GetPlayer().GetAngleZ() ObjectReference shrine = Self.placeAtMe(ShrineACTIVATOR, 1, false, true) Self.DisableNoWait() shrine.SetAngle(0.0, 0.0, az + 180.0) shrine.MoveTo(Game.GetPlayer(), 200.0 * Math.sin(az), 200.0 * Math.cos(az), 0.0, false) shrine.Enable() Self.Delete() endIf ENDEVENT Have a happy modding. Link to comment Share on other sites More sharing options...
xGoReTHeRoNx Posted April 15, 2021 Author Share Posted April 15, 2021 Thanks, I've tried everything you both posted, I'll do the same with this. Btw, is there any way to fix the hitbox of the activator? I've scaled the .nif and now the box is huge. I can't find the correct property regarding this. Link to comment Share on other sites More sharing options...
Recommended Posts