Jump to content

[LE] Need some help scripting a menu with three options


Recommended Posts

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

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 by maxarturo
Link to comment
Share on other sites

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

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 by xGoReTHeRoNx
Link to comment
Share on other sites

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 by maxarturo
Link to comment
Share on other sites

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

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 disabled

xGoReTHeRoNx:

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 by ReDragon2013
Link to comment
Share on other sites

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 by xGoReTHeRoNx
Link to comment
Share on other sites

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

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

  • Recently Browsing   0 members

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