ZombieNL Posted April 5, 2019 Author Share Posted April 5, 2019 I have doubt that next is changing the option text inside the MCM menu you like. Why?If you end the function VampireMenu() each temp array is thrown away. Function VampireMenu() ; New version. string[] VampireText = new string[6] ; Array with text labels for menu VampireText[0] = ". Cancel" VampireText[1] = ". Vampire Armor" VampireText[2] = ". Vampire Armor without fighting" VampireText[3] = ". Ancient Blood" VampireText[4] = ". Cure Vampirism and remove all powers" VampireText[5] = ". Cure Vampirism and Turn into a Lycan" int c = 0 ; counting the number of rows for the array VampireMenu int i = 1 ; adding mumbers to menu, starting with 1 int Index = 0 ; for array index and adding matching textlabels from array VampireText string[] VampireMenu = new string [128] ; Creating new menu array from bools who are True While Index < RemoteMCM.VampireVal.Length Debug.Notification("VampireVal " + Index + " = " + RemoteMCM.VampireVal[Index]) ; debugging only !!! Index += 1 EndWhile Index = 0 While Index < RemoteMCM.VampireVal.Length If RemoteMCM.VampireVal[Index] == True ; Check wich bools are True VampireMenu[Index] = i + VampireText[Index]; adding Textlabels + numbers to menu array ; adding actions to menu Debug.Notification("option " + i + VampireText[Index]) ; debugging only !!! functionSelect(Index) c += 1 EndIf Index += 1 i += 1 EndWhile ; At this point the new menu should be complete and stored in the array VampireMenu. Now i need to convert the array to a visible menu. ; The while loop below should display the array VampireMenu and all options in it. I only made this to see if everything works as intended. Index = 0 Debug.Notification("You have seleted " + c + " Options") ; debugging only !!! While Index < c Debug.Notification("VampireMenu " + Index + " = " + VampireMenu[Index]) ; debugging only !!! Index += 1 EndWhile EndFunction everything you made with string[] VampireText string[] VampireMenukeeps in this function and will never get transfer to MCM script The function VampireMenu() is not suppost to make changes to the mcm.Inside VampireMenu() i collect info from the mcm, and with that info i create fill the array VampireMenu. The mcm is only there to select wich options you want to see in the menu when you activate the shrine.After the menu is made and presented to the player, it is ok if the temp arrays get thrown away. It is true that i still need to figure out a way to convert the array to a menu, but giving the array back to the mcm was never my intention. So maybe i am stupid, but i don't understand your post. Link to comment Share on other sites More sharing options...
IsharaMeradin Posted April 5, 2019 Share Posted April 5, 2019 That is not how OnOptionHighlight works. It will only display text when an option is highlighted. If you put the mouse over any non-option area, no text will display. Unfortunately, it is working as intended.Thnx for explaining this, but in that case i just leave it as it is now.My mcm is easy enough to understand as it is. The InfoText was just a litle extra. If the code you showed is producing the message in screenshot 7/9 from your mod-page, then i don't understand why you made it. Isn't this the way all mcm menu's work?First you need to exit all menu's before you can notice any changes. I think i prefere to explain my mcm menu in the description of my mod page. That should be enough. The reason I needed to add that message box was for the following reason:When I made that MCM script I did not know that calling certain functions within the option select would cause the MCM menu to eventually cease functioning. It is not an issue if using just my mod's MCM. But it was causing an issue when going from my mod's MCM to another mod's MCM. I have since learned that it is better to utilize the OnConfigClose event to register a single update (which even with a short duration is safely delayed until the menu system is exited) to handle starting quests, setting variables etc depending upon user selections. My Inventory Management System Rebuilt mod uses this method. At any rate, however you choose to inform your users of the mod's intentions is up to you. Link to comment Share on other sites More sharing options...
foamyesque Posted April 5, 2019 Share Posted April 5, 2019 (edited) I'm not totally sure why there's the independent boolean variables at all. Why not the single array? Maybe another for reset-to-default information. Then you could just do VampireVal[n] = !VampireVal[n] The initial bools are there for when you load the mcm for the first time. The mcm should have some initial values to build the menu. I chose to make the initial values the same as the default values because i think that makes sense. I don't know how to implement your suggestion into my script. I don't understand your comment enough. You can set the initial bools in the Properties menu, since the boolean array is one. If you want a set to do the revert to default stuff, then you do need a second set of bools, but you can also make that a boolean array property and set them there. So instead of a bunch of VampireValN variables for your defaults, you could use DefaultVampireVal[N] and only need to declare or manipulate one thing, just as you're doing with VampireVal[N] for your active booleans. That way you can avoid hardcoding the defaults into the script and can replace the one-by-one copying into the active array in your OnInit block with a loop, which will be much more extensible should you decide to change the options in future. As for the other piece: Just as you can use ordinary arithmetic in an assignment (for example i = j + 1), you can use boolean arithmetic: EQUALS, NOT, AND, and OR, symbolically done as '==', '!', '&&', and '||'. Since all you're doing is flipping from one state to another you don't need an if-else-then sequence because you're always going to do the same operation: a NOT. This will turn trues into falses and falses into trues. You're doing this already with your operations ahead of the SetOptionToggle() call in your OnOptionSelect(), but you're then also doing explicit assignments of true and false to the boolean array, which you don't need to do. A revision showing the implementation: Scriptname VC_Script_MCM extends SKI_ConfigBase {MCM menu script} ; For all code in this file i used the MCM video tutorials from DarkFox127 as starting point and started working from there. bool bInitialized = false string Property sVampireOptionName[] ;titles for the various options bool Property bDefaultVampireVal[] ;fill this in the properties menu, replaces VampireValN below ;Bool VampireVal0 = True ; Cancel button. This does not not show in mcm menu and therefore always is True. ;Bool VampireVal1 = True ;Bool VampireVal2 = False ;Bool VampireVal3 = True ;Bool VampireVal4 = False ;Bool VampireVal5 = False int iVampireOption[] ;created & filled automatically ;int iVampire1 ;int iVampire2 ;int iVampire3 ;int iVampire4 ;int iVampire5 bool[] Property bVampireVal Auto ; Make the array available for VC_Script_Shrine_MolagBal Event OnConfigInit() ;Declare these in the property menu ;Pages = new string[1] ;Pages[0] = "Config" if !bInitialized int i = sVampireOptionName.Length int j = bDefaultVampireVal.Length iVampireOption = Utility.CreateIntArray(i) bVampireVal = Utility.CreateBoolArray(i) while i > 0 i-=1 if i < j ;precaution in case fewer defaults are specified than option names bVampireVal[i] = bDefaultVampireVal[i] endif endwhile endif EndEvent ;Functionality moved to OnConfigInit() ;Event OnInit() ; parent.OnInit() ; ; VampireVal = new bool[6] ; storing all bools in array ; ; VampireVal[0] = VampireVal0 ; VampireVal[1] = VampireVal1 ; VampireVal[2] = VampireVal2 ; VampireVal[3] = VampireVal3 ; VampireVal[4] = VampireVal4 ; VampireVal[5] = VampireVal5 ;EndEvent Event OnPageReset(string page) If (Page == "") LoadCustomContent("Imaginary_Image") Return Else UnLoadCustomContent() EndIf If (Page == "Config") SetCursorFillMode(TOP_TO_BOTTOM) AddHeaderOption("Vampire Menu") int i = sVampireOptionName.Length while i > 1 ;because of hidden cancel button? I do not understand the purpose of that i-=1 iVampireOption[i] = AddToggleOption(sVampireOptionName[i], bVampireOption[i]) endwhile ;iVampire1 = AddToggleOption("Vampire Armor", VampireVal1) ;iVampire2 = AddToggleOption("Vampire Armor without fighting", VampireVal2) ;iVampire3 = AddToggleOption("Ancient Blood", VampireVal3) ;iVampire4 = AddToggleOption("Cure Vampirism", VampireVal4) ;iVampire5 = AddToggleOption("Turn into a Lycan", VampireVal5) EndIf EndEvent Event OnOptionSelect(int option) If (CurrentPage == "Config") int iIndex = iVampireOption.Find(option) if iIndex < 0 return endif bVampireVal[iIndex] = !bVampireVal[iIndex] SetToggleOptionValue(iVampireOption[iIndex], bVampireVal[iIndex]) ;all of the below is now redundant ;If (option == iVampire1) ; VampireVal1 = !VampireVal1 ; SetToggleOptionValue(iVampire1, VampireVal1) ; If VampireVal1 == True ; VampireVal[1] = True ; Else ; VampireVal[1] = False ; EndIf ;ElseIf (option == iVampire2) ; VampireVal2 = !VampireVal2 ; SetToggleOptionValue(iVampire2, VampireVal2) ; VampireVal2 = VampireVal2 ; If VampireVal2 == True ; VampireVal[2] = True ; Else ; VampireVal[2] = False ; EndIf ;ElseIf (option == iVampire3) ; VampireVal3 = !VampireVal3 ; SetToggleOptionValue(iVampire3, VampireVal3) ; VampireVal3 = VampireVal3 ; If VampireVal3 == True ; VampireVal[3] = True ; Else ; VampireVal[3] = False ; EndIf ;ElseIf (option == iVampire4) ; VampireVal4 = !VampireVal4 ; SetToggleOptionValue(iVampire4, VampireVal4) ; VampireVal4 = VampireVal4 ; If VampireVal4 == True ; VampireVal[4] = True ; Else ; VampireVal[4] = False ; EndIf ;ElseIf (option == iVampire5) ; VampireVal5 = !VampireVal5 ; SetToggleOptionValue(iVampire5, VampireVal5) ; VampireVal5 = VampireVal5 ; If VampireVal5 == True ; VampireVal[5] = True ; Else ; VampireVal[5] = False ; EndIf ;EndIf EndIf EndEvent Event OnOptionDefault(int option) if (CurrentPage == "Config") ;this is a needed check that was missed in original code int iIndex = iVampireOption.Find(option) if iIndex < 0 return endif bVampireVal[iIndex] = bDefaultVampireVal[iIndex] SetToggleOptionValue(iVampireOption[iIndex], bVampireVal[iIndex]) endif ;all of the below is now redundant ;If (option == iVampire1) ; VampireVal1 = True ; SetToggleOptionValue(iVampire1, VampireVal1) ; VampireVal[1] = True ;ElseIf (option == iVampire2) ; VampireVal2 = False ; SetToggleOptionValue(iVampire2, VampireVal2) ; VampireVal[2] = False ;ElseIf (option == iVampire3) ; VampireVal3 = True ; SetToggleOptionValue(iVampire3, VampireVal3) ; VampireVal[3] = True ;ElseIf (option == iVampire4) ; VampireVal4 = False ; SetToggleOptionValue(iVampire4, VampireVal4) ; VampireVal[4] = False ;ElseIf (option == iVampire5) ; VampireVal5 = False ; SetToggleOptionValue(iVampire5, VampireVal5) ; VampireVal[5] = False ;EndIf EndEvent I'm not exactly sure what the purpose is behind the 'cancel button' because AFAICT you have no code whatsoever that implements it. Normally I'd just iterate the arrays all the way to zero, but it throws things out on actually adding the toggles, which is why that particular loop is set to stop at one instead. Provided you set up the properties correctly this should have the same functionality, but be much easier to add or remove options; you just need to modify a title and, optionally, a default value. I also added a missing CurrentPage check to the OnOptionDefault() block. Edited April 5, 2019 by foamyesque Link to comment Share on other sites More sharing options...
ZombieNL Posted April 6, 2019 Author Share Posted April 6, 2019 Your new version of the mcm script looks great, i like the idea of shrinking the wall of code, but don't forget: If (Page == "Config") SetCursorFillMode(TOP_TO_BOTTOM) AddHeaderOption("Vampire Menu") iVampire1 = AddToggleOption("Vampire Armor", VampireVal1) iVampire2 = AddToggleOption("Vampire Armor without fighting", VampireVal2) iVampire3 = AddToggleOption("Ancient Blood", VampireVal3) iVampire4 = AddToggleOption("Cure Vampirism", VampireVal4) iVampire5 = AddToggleOption("Turn into a Lycan", VampireVal5) EndIfis only a part of the total mcm i want to make. I think i mentioned this inside the script for the shrine in the added comments, but i am not sure it was mentioned in one of the forum posts. If not, then sorry for that. The plan was to get the vampire part working first and then add the mortal and lycan stuff later. The total menu layout will look something like this: If (Page == "Config") SetCursorFillMode(LEFT_TO_RIGHT) AddHeaderOption("Vampire Menu") AddHeaderOption("Mortal Menu") iVampire1 = AddToggleOption("Vampire Armor", VampireVal1) AddToggleOption("Cure All Disseases", True) iVampire2 = AddToggleOption("Vampire Armor without fighting", VampireVal2) AddToggleOption("Sanguinare Vampiris", True) iVampire3 = AddToggleOption("Ancient Blood", VampireVal3) AddToggleOption("Turn into a Vampire", True) iVampire4 = AddToggleOption("Cure Vampirism", VampireVal4) AddToggleOption("Turn into a Lycan", False) iVampire5 = AddToggleOption("Turn into a Lycan", VampireVal5) AddEmptyOption() AddEmptyOption() AddEmptyOption() AddHeaderOption("Lycan Menu") AddEmptyOption() AddToggleOption("Lycans can't use the shrine", False) AddEmptyOption() AddToggleOption("Cure Lycantrophy", True) AddEmptyOption() AddToggleOption("Turn into a Vampire", True) EndIfAs you can see i did not add any actions to the mortal and lycan options yet.My thought was, once i get the vampire menu working, the other 2 are just more of the same and easy to add in.Once i get the vampire stuff working and i completed the whole mcm menu, then it is a good idea to shrink the code with the code you suggested. But before i even start with the mortal and lycan stuff, i want to get the vampire stuff working first.I see you like to add all you can to the properties-window in the CK, is that personal preference, or is there another reason for doing that?I like to keep everything inside the script whenever possible because it's easier when you need to change/add something. If you store everything in the properties-window then you need to start CK for every change you want to make. If you keep everything inside the script, then you can just open notepad++ to edit and compile scripts. Or maybe i am doing something wrong, that is also possible.About the cancel button:I want to make sure the new menu always has a cancel button, this way you are not forced to choose one of the options if you activate the shrine by accident.In the function VampireMenu() inside VC_Script_Shrine_MolagBal i check wich bools are true.Only true bools are added to the new menu.Because there is no option to disable the cancel button in the mcm, Bool VampireVal0 is always True.I am sure this is not the best way to do this, but it works.Here is the script VC_Script_Shrine_MolagBal Scriptname VC_Script_Shrine_MolagBal extends ObjectReference {Menu Script for Molag Bal Shrine} VC_Script_addCoffin Property RemoteScript Auto ; All functions are stored in VC_Script_addCoffin. I call them when needed with RemoteScript. VC_Script_MCM Property RemoteMCM Auto ; Link to MCM menu script Event onActivate(ObjectReference akActionRef) CheckRace() EndEvent Function CheckRace() RemoteScript.VC_Player_Race() If (RemoteScript.vcRace == 1) ; Player is Vampire VampireMenu() ElseIf (RemoteScript.vcRace == 2) ; Player is Lycan LycanMenu() ElseIf (RemoteScript.vcRace == 3) ; Player is Mortal MortalMenu() EndIf EndFunction Function VampireMenu() ; New version. string[] VampireText = new string[6] ; Array with text labels for menu VampireText[0] = ". Cancel" VampireText[1] = ". Vampire Armor" VampireText[2] = ". Vampire Armor without fighting" VampireText[3] = ". Ancient Blood" VampireText[4] = ". Cure Vampirism and remove all powers" VampireText[5] = ". Cure Vampirism and Turn into a Lycan" int c = 0 ; counting the number of rows/buttons for the array VampireMenu int i = 1 ; adding mumbers to menu, starting with 1 int Index = 0 ; for array index and adding matching textlabels from array VampireText string[] VampireMenu = new string [128] ; Creating new menu array from bools who are True While Index < RemoteMCM.VampireVal.Length Debug.Notification("VampireVal " + Index + " = " + RemoteMCM.VampireVal[Index]) ; debugging only !!! Index += 1 EndWhile Index = 0 While Index < RemoteMCM.VampireVal.Length If RemoteMCM.VampireVal[Index] == True ; Check wich bools are True VampireMenu[Index] = i + VampireText[Index]; adding Textlabels + numbers to menu array ; Debug.Notification("option " + i + VampireText[Index]) ; debugging only !!! c += 1 EndIf Index += 1 i += 1 EndWhile ; At this point the new menu should be complete and stored in the array VampireMenu. Now i need to convert the array to a visible menu. ; The while loop below should display the array VampireMenu and all options in it. I only made this to see if everything works as intended. Debug.Notification("You have seleted " + c + " Options") ; debugging only !!! Index = 0 While Index < c Debug.Notification("Index "+ Index + " : " + VampireMenu[Index]) ; debugging only !!! Index += 1 EndWhile ;/ ; Disabled during testing. First i want to see the new menu, then i add the functions If Index == 0 ElseIf Index == 1 RemoteScript.VamipereArmor() ElseIf Index == 2 RemoteScript.VamipereArmorCoward() ElseIf Index == 3 RemoteScript.AncientBlood() ElseIf Index == 4 RemoteScript.CureVampire() ElseIf Index == 5 RemoteScript.CureVampire() RemoteScript.CreateLycan() EndIf /; EndFunction ; Old working versions for lycan and mortal menus. I don't update them until i get the new vampire menu to work. ; Messages for old menu version. In the new menu i should no longer need them. Message Property VC_Msg_ShrineMenu_Vampire Auto Message Property VC_Msg_ShrineMenu_Lycan Auto Message Property VC_Msg_ShrineMenu_Mortal Auto Function LycanMenu (int aiButton = 0) aiButton = VC_Msg_ShrineMenu_Lycan.Show() If aiButton == 0 ; 1. Cancel ElseIf aiButton == 1 ; 2. Cure my Lycantrophy RemoteScript.CureLycan() ElseIf aiButton == 2 ; 3. Cure my Lycantrophy and turn me into a Vampire RemoteScript.CureLycan() RemoteScript.CreateVampire() EndIf EndFunction Function MortalMenu (int aiButton = 0) aiButton = VC_Msg_ShrineMenu_Mortal.Show() If aiButton == 0 ; 1. Cancel ElseIf aiButton == 1 ; 2. Cure all my disseases RemoteScript.CureAllDisseases() ElseIf aiButton == 2 ; 3. Sanguinare Vampiris RemoteScript.DiseaseSV() ElseIf aiButton == 3 ; 4. Turn me into a vampire RemoteScript.CreateVampire() ElseIf aiButton == 4 ; 5. Turn me into a Lycan. This option is not in v1.1, but will be in next release. In the MCM menu i will add the option to toggle this option on or off. RemoteScript.CreateLycan() EndIf EndFunction Link to comment Share on other sites More sharing options...
foamyesque Posted April 6, 2019 Share Posted April 6, 2019 What I prefer to do is reduce the amount of repetition of stuff, particularly of magic numbers, and especially if there's chances I might want to change it in the future. Since I do pretty much all my coding and compiling in the CK anyway, kicking out to the property filling menus doesn't particularly impede the workflow, and the approach otherwise has a bunch of benefits in terms of flexibility, reuse, extension, and interaction with other scripts. As much as I am able, I try to keep explicit constant use in my scripts limited to comparisons to zero, true, false, and none. Nearly everything else comes from a variable, property, or function in one way or another. Link to comment Share on other sites More sharing options...
Recommended Posts