cookchar

DnD Character Sheet v1.2.4

Jan 19th, 2018
115
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import tkinter as tk
  2. import tkinter.simpledialog as sdg
  3. import tkinter.messagebox as mbx
  4. import tkinter.font as tkf
  5. import tkinter.tix as tix
  6. import random as rnd
  7. import operator as op
  8. import os
  9.  
  10. #Charlie Cook's Dungeons & Dragons Character Sheet Program
  11. #Begun early June 2017
  12. #Last updated mid January 2018
  13.  
  14. #Gets the DEX, STR, and INT scores to set the Reflex, Fortitude, and Will saving throw scores
  15. #Now accommodates for when Ability Scores are changed between base and racial/class bonus mode
  16. #Scores are indexed as follows: STR=0, DEX=1, CON=2, INT=3, WIS=4, CHA=5
  17. def updtSAVETHRW(regularMode = True, altMS = [], alsBS = []):
  18.     if regularMode:
  19.         savingThrows[0] = mainStats[2] + bonusStats[2]
  20.         savingThrows[1] = mainStats[1] + bonusStats[1]
  21.         savingThrows[2] = mainStats[4] + bonusStats[4]
  22.     else:
  23.         savingThrows[0] = altMS[2] + alsBS[2]
  24.         savingThrows[1] = altMS[1] + alsBS[1]
  25.         savingThrows[2] = altMS[4] + alsBS[4]
  26. #Increments the level of the player and checks to see if 5E players are overleveled
  27. #The level, current XP, and required XP are recorded in the experience list in the afore-noted order
  28. def levelUp():
  29.     showMessage = True
  30.     experience[0] += 1  #Increment the player's level
  31.     if LEVLVar.get():   #If the player is using the 5E brackets
  32.         if experience[0] == 20: #If the player has reached level 20, the maximum allowed in 5E
  33.                 mbx.showinfo("Level Up!", "You have reached the maximum level allowed in 5E! Well played!")
  34.                 experience[2] = 999999  #Theoretically, no 5E player should reach such ridiculous XP
  35.         elif experience[0] > 20:        #If they do, be snide to them because they must be cheating
  36.                 mbx.showinfo("Level Up?", "You really should make a new character by now, 5E isn't meant to work with this much XP. To prevent errors, your current XP will be modulated to a six-digit number.")
  37.                 experience[1] = experience[1] % 999999
  38.                 experience[0] -= 1
  39.                 showMessage = False
  40.         else:   #If the player can still level up in the future, set the required XP to the proper amount
  41.             experience[2] = brackets5E[experience[0]]
  42.     else:   #See prior comment
  43.         experience[2] += 100 + 10 * experience[0]
  44.  
  45.     if showMessage:
  46.         mbx.showinfo("Level Up!","Congratulations!\nYou are now Level " + str(experience[0]))
  47. #Refreshes the information in the Stats frame
  48. #Now accommodates for when Ability Scores are changed between base and racial/class bonus mode
  49. #Scores are indexed as follows: STR=0, DEX=1, CON=2, INT=3, WIS=4, CHA=5
  50. def refrSTAT(regularMode = True, altMS = [], altBS = []):
  51.     LEVLLbl.config(text = "Level: " + str(experience[0]))
  52.     HPLbl.config(text = "HP:" + str(health[0]) + "/" + str(health[1]))
  53.     XPLbl.config(text = "XP:" + str(experience[1]) + "/" + str(experience[2]))
  54.     #The below strings are evaluated to their python list objects via the eval() function.
  55.     #This allows for the block of code that updates the scores to not be written twice.
  56.     #Since a situation like this needs an if-else block, it's only worth it if the main code block
  57.     #Is longer than the lines needed to assign the names of the strings to be eval'd
  58.     if regularMode:
  59.         mainStr = "mainStats"
  60.         bonusStr = "bonusStats"
  61.         updtSAVETHRW()
  62.     else:
  63.         mainStr = "altMS"
  64.         bonusStr = "altBS"
  65.         updtSAVETHRW(False, altMS, altBS)
  66.  
  67.     STRLbl.config(text = "STR: " + str(eval(mainStr)[0]))
  68.     DEXLbl.config(text = "DEX: " + str(eval(mainStr)[1]))
  69.     CONLbl.config(text = "CON: " + str(eval(mainStr)[2]))
  70.     INTLbl.config(text = "INT: " + str(eval(mainStr)[3]))
  71.     WISLbl.config(text = "WIS: " + str(eval(mainStr)[4]))
  72.     CHALbl.config(text = "CHA: " + str(eval(mainStr)[5]))
  73.  
  74.     if bonusStats[0] > 0:
  75.         STRBLbl.config(text = "+" + str(eval(bonusStr)[0]))
  76.     else:
  77.         STRBLbl.config(text = str(eval(bonusStr)[0]))
  78.  
  79.     if bonusStats[1] > 0:
  80.         DEXBLbl.config(text = "+" + str(eval(bonusStr)[1]))
  81.     else:
  82.         DEXBLbl.config(text = str(eval(bonusStr)[1]))
  83.  
  84.     if bonusStats[2] > 0:
  85.         CONBLbl.config(text = "+" + str(eval(bonusStr)[2]))
  86.     else:
  87.         CONBLbl.config(text = str(eval(bonusStr)[2]))
  88.  
  89.     if bonusStats[3] > 0:
  90.         INTBLbl.config(text = "+" + str(eval(bonusStr)[3]))
  91.     else:
  92.         INTBLbl.config(text = str(eval(bonusStr)[3]))
  93.  
  94.     if bonusStats[4] > 0:
  95.         WISBLbl.config(text = "+" + str(eval(bonusStr)[4]))
  96.     else:
  97.         WISBLbl.config(text = str(eval(bonusStr)[4]))
  98.  
  99.     if bonusStats[5] > 0:
  100.         CHABLbl.config(text = "+" + str(eval(bonusStr)[5]))
  101.     else:
  102.         CHABLbl.config(text = str(eval(bonusStr)[5]))
  103.  
  104.     FORTLbl.config(text = "Fortitude: " + str(savingThrows[0]))
  105.     REFLLbl.config(text = "Reflex: " + str(savingThrows[1]))
  106.     WILLLbl.config(text = "Will: " + str(savingThrows[2]))
  107. #Increments the current HP if possible
  108. def HPUp():
  109.     if health[0] < health[1]:   #If the current health is less than the max
  110.         health[0] += 1          #Increment it
  111.         if SKILSCORVar.get():   #See the changeScores() function for more information on this block
  112.             changeScores()
  113.         else:
  114.             refrSTAT()
  115. #Decrements the current HP if possible
  116. def HPDown():
  117.     if health[0] > -10:     #If the player won't die by losing health
  118.         health[0] -= 1      #Decrement it
  119.         if SKILSCORVar.get():   #See the changeScores() function for more information on this block
  120.             changeScores()
  121.         else:
  122.             refrSTAT()
  123. #Increments the current XP and levels up if necessary
  124. def XPUp():
  125.     temp = sdg.askinteger("Add XP", "Enter how many experience points you've earned:")
  126.     if type(temp) == int:
  127.         experience[1] += temp
  128.  
  129.     while experience[1] >= experience[2]:   #While the current XP exceeds the required XP
  130.         levelUp()
  131.         if SKILSCORVar.get():   #See the changeScores() function for more information on this block
  132.             changeScores()
  133.         else:
  134.             refrSTAT()
  135.  
  136.     if SKILSCORVar.get():
  137.         changeScores()
  138.     else:
  139.         refrSTAT()
  140. #Changes the leveling requirements between 5E and the 100 + 10 * Current Level formula and re-levels the player
  141. def ChangeXP():
  142.     if mbx.askokcancel("Warning!", "Changing the level up brackets may make things weird. Is it okay to continue?"):
  143.         if LEVLVar.get():   #Switching to 5E
  144.             experience[0] = 1
  145.             experience[2] = brackets5E[1]
  146.  
  147.         else:               #Switching to C^2's homebrew method
  148.             experience[0] = 1
  149.             experience[2] = 110
  150.  
  151.         while experience[1] >= experience[2]:   #See comment in XPUp()
  152.             levelUp()
  153.             if SKILSCORVar.get():   #See the changeScores() function for more information on this block
  154.                 changeScores()
  155.             else:
  156.                 refrSTAT()
  157.  
  158.         if SKILSCORVar.get():
  159.             changeScores()
  160.         else:
  161.             refrSTAT()
  162.     #The below else condition is present because the checkbox that invokes this function
  163.     #will change regardless of the yes or no in the above message box, thus it must be reset if no is selected
  164.     else:
  165.         LEVLVar.set(not LEVLVar.get())
  166. #Root dialog for editing stats
  167. class editStatRoot(sdg.Dialog):
  168.     def body(self, master):
  169.         self.title("Edit Stats")
  170.         self.resizable(False, False)
  171.         self.choice = tk.IntVar()
  172.         self.choice.set(1)
  173.         tk.Label(master, text = "Choose a stat item to edit:", font = ftNormal).grid(row = 0)
  174.        
  175.         STR = tk.Radiobutton(master, text = "Strength", value = 1, variable = self.choice, cursor = "hand2", font = ftNormal)
  176.         DEX = tk.Radiobutton(master, text = "Dexterity", value = 2, variable = self.choice, cursor = "hand2", font = ftNormal)
  177.         CON = tk.Radiobutton(master, text = "Constitution", value = 3, variable = self.choice, cursor = "hand2", font = ftNormal)
  178.         INT = tk.Radiobutton(master, text = "Intelligence", value = 4, variable = self.choice, cursor = "hand2", font = ftNormal)
  179.         WIS = tk.Radiobutton(master, text = "Wisdom", value = 5, variable = self.choice, cursor = "hand2", font = ftNormal)
  180.         CHA = tk.Radiobutton(master, text = "Charisma", value = 6, variable = self.choice, cursor = "hand2", font = ftNormal)
  181.  
  182.         LEV = tk.Radiobutton(master, text = "Level", value = 7, variable = self.choice, cursor = "hand2", font = ftNormal)
  183.         XP = tk.Radiobutton(master, text = "Experience", value = 8, variable = self.choice, cursor = "hand2", font = ftNormal)
  184.         HPC = tk.Radiobutton(master, text = "Current Health", value = 9, variable = self.choice, cursor = "hand2", font = ftNormal)
  185.         HPM = tk.Radiobutton(master, text = "Maximum Health", value = 10, variable = self.choice, cursor = "hand2", font = ftNormal)
  186.         ALL = tk.Radiobutton(master, text = "Ability Re-roll", value = 11, variable = self.choice, cursor = "hand2", font = ftNormal)
  187.  
  188.         STR.grid(row = 6, sticky = "w")
  189.         DEX.grid(row = 7, sticky = "w")
  190.         CON.grid(row = 8, sticky = "w")
  191.         INT.grid(row = 9, sticky = "w")
  192.         WIS.grid(row = 10, sticky = "w")
  193.         CHA.grid(row = 11, sticky = "w")
  194.  
  195.         LEV.grid(row = 1, sticky = "w")
  196.         XP.grid(row = 2, sticky = "w")
  197.         HPC.grid(row = 3, sticky = "w")
  198.         HPM.grid(row = 4, sticky = "w")
  199.         ALL.grid(row = 5, sticky = "w")
  200.  
  201.     def apply(self):
  202.         self.result = self.choice.get()
  203.  
  204. #Edits attributes, health, level, and XP
  205. def editStat():
  206.     choice = editStatRoot(base).result
  207.  
  208.     if type(choice) == int: #If the user didn't hit Cancel in the root dialog
  209.         choices = ["Strength", "Dexterity", "Constitution", "Intelligence", "Wisdom", "Charisma"]
  210.         if choice < 7#If the user wants to edit an Ability Score
  211.  
  212.             temp = []
  213.             try:
  214.                 temp.append(sdg.askinteger("Edit " + choices[choice - 1], "Enter your new " + choices[choice - 1] + ":"))
  215.                 temp.append((temp[0] - 10) // 2)
  216.             except TypeError:
  217.                 temp = None
  218.  
  219.         elif choice == 7:
  220.             temp = sdg.askinteger("Edit Level", "[Warning! This will change your XP!]\nEnter your new level:", initialvalue = experience[0])
  221.             if type(temp) == int and temp > 0:
  222.                 experience[0] = temp
  223.                 if LEVLVar.get():
  224.                     if experience[0] > 20:
  225.                         mbx.showinfo("Warning!", "When using the 5E level up brackets, you cannot have a level greater than 20. To prevent errors, your entered level will now be modulated to a number from 1 to 20.")
  226.                         experience[0] = experience[0] % 20
  227.  
  228.                     if experience[0] == 20:
  229.                         experience[2] = 999999
  230.                     else:
  231.                         experience[2] = brackets5E[experience[0]]
  232.                     experience[1] = brackets5E[experience[0] - 1]
  233.                 else:
  234.                     experience[2] = 0
  235.                     for i in range(1, experience[0] + 1):
  236.                         experience[2] += 100 + 10 * i
  237.  
  238.                     experience[1] = experience[2] - (100 + 10 * experience[0])
  239.  
  240.             elif type(temp) == int and temp <= 0:
  241.                 mbx.showerror("Error", "You can't have a negative level!")
  242.  
  243.         elif choice == 8:
  244.             temp = sdg.askinteger("Edit Experience", "[Warning! This will change your level!]\nEnter your new current experience points:", initialvalue = experience[1])
  245.             if type(temp) == int and temp >= 0:
  246.                 experience[1] = temp
  247.                 experience[0] = 1
  248.  
  249.                 if LEVLVar.get():
  250.                     experience[2] = 300
  251.                 else:
  252.                     experience[2] = 110
  253.  
  254.                 while experience[1] >= experience[2]:
  255.                     levelUp()
  256.                     if SKILSCORVar.get():
  257.                         changeScores()
  258.                     else:
  259.                         refrSTAT()
  260.             elif type(temp) == int and temp < 0:
  261.                 mbx.showerror("Error", "You can't have negative experience points!")
  262.  
  263.         elif choice == 9:
  264.             temp = health[1] + 1
  265.  
  266.             while temp > health[1]:
  267.                 temp = sdg.askinteger("Edit Health", "Enter your new current health:", initialvalue = health[0])
  268.                 if type(temp) != int:
  269.                     return
  270.                 elif temp > health[1]:
  271.                     mbx.showerror("Error", "You can't overcharge your health!")
  272.  
  273.             if type(temp) == int and temp >= -10:
  274.                 health[0] = temp
  275.             elif type(temp) == int and temp < -10:
  276.                 mbx.showerror("Error", "You can't have your health drop below -10, you'd be dead then!")
  277.  
  278.         elif choice == 10:
  279.             temp = sdg.askinteger("Edit Health", "Enter your new maximum health:", initialvalue = health[1])
  280.             if type(temp) == int and temp > 0:
  281.                 health[1] = temp
  282.                 if health[0] > health[1]:
  283.                     health[0] = health[1]
  284.             elif type(temp) == int and temp <= 0:
  285.                 mbx.showerror("Error", "You can't have a negative maximum health!")
  286.  
  287.         elif choice == 11:
  288.             tempMStats = []
  289.             tempBStats = []
  290.             tempStr = ""
  291.  
  292.             temp = newCharacterStatRoot(base).result
  293.  
  294.             if type(temp) != int:
  295.                 return
  296.        
  297.             if temp == 0:
  298.                 tempStr = pointBuyRoot(base).result
  299.             elif temp == 1:
  300.                 tempStr = diceRollStatsRoot(base).result
  301.        
  302.             if type(tempStr) != str:
  303.                 return
  304.        
  305.             if temp in (0, 1):
  306.                 for s in tempStr.split()[0].split('|'):
  307.                     tempMStats.append(int(s))
  308.  
  309.                 for s in tempStr.split()[1].split('|'):
  310.                     tempBStats.append(int(s))
  311.             elif temp == 2:
  312.                 tempCount = 0
  313.  
  314.                 while len(tempMStats) < 6 and len(tempBStats) < 6:
  315.                     temp = sdg.askinteger("New Character: Manual Stats", "Enter your " + choices[tempCount] + ":")
  316.                     if type(temp) != int:
  317.                         return
  318.  
  319.                     tempMStats.append(temp)
  320.                     tempBStats.append((temp - 10) // 2)
  321.                     tempCount += 1
  322.  
  323.             tempCount = 0
  324.  
  325.             for bit in tempMStats:
  326.                 mainStats[tempCount] = bit
  327.                 tempCount += 1
  328.  
  329.             tempCount = 0
  330.  
  331.             for bit in tempBStats:
  332.                 bonusStats[tempCount] = bit
  333.                 tempCount += 1
  334.  
  335.         if type(temp) == list:
  336.             if type(temp[0]) == int:
  337.                 mainStats[choice - 1] = temp[0]
  338.  
  339.             if type(temp[1]) == int:
  340.                 bonusStats[choice - 1] = temp[1]
  341.  
  342.         if type(temp) in (int, list):
  343.             if SKILSCORVar.get():
  344.                 changeScores()
  345.             else:
  346.                 refrSTAT()
  347. #Refreshes the information frame
  348. def refrINFO():
  349.     NAMELbl.config(text = info[0])
  350.     CLSSLbl.config(text = "Class: " + info[1])
  351.     DETYLbl.config(text = "Deity: " + info[2])
  352.     RACELbl.config(text = "Race: " + info[3])
  353.     GNDRLbl.config(text = "Gender: " + info[4])
  354.     AYGELbl.config(text = "Age: " + str(info[5]))
  355.     HGHTLbl.config(text = str(info[6]) + " " + info[7])
  356.     WGHTLbl.config(text = str(info[8]) + " " + info[9])
  357.     HAIRLbl.config(text = "Hair: " + info[10])
  358.     EYESLbl.config(text = "Eyes: " + info[11])
  359.     MONYLbl.config(text = str(info[13]) + " " + info[12])
  360. #Changes the amount of money
  361. def changeMoney()#Money is kept as an integer to prevent floating-point-arithmetic errors
  362.     debit = sdg.askinteger("Change Money", "How much money to add/remove?")
  363.     if type(debit) == int:
  364.         info[13] += debit
  365.         refrINFO()
  366. #Root dialog for editing information
  367. class editInfoRoot(sdg.Dialog):
  368.     def body(self, master):
  369.         self.title("Edit Info")
  370.         self.resizable(False, False)
  371.         self.choice = tk.IntVar()
  372.         self.choice.set(1)
  373.         tk.Label(master, text = "Choose an info item to edit:", font = ftNormal).grid(row = 0)
  374.        
  375.         Name = tk.Radiobutton(master, text = "Name", value = 1, variable = self.choice, cursor = "hand2", font = ftNormal)
  376.         Class = tk.Radiobutton(master, text = "Class", value = 2, variable = self.choice, cursor = "hand2", font = ftNormal)
  377.         Deity = tk.Radiobutton(master, text = "Deity", value = 3, variable = self.choice, cursor = "hand2", font = ftNormal)
  378.         Race = tk.Radiobutton(master, text = "Race", value = 4, variable = self.choice, cursor = "hand2", font = ftNormal)
  379.         Gender = tk.Radiobutton(master, text = "Gender", value = 5, variable = self.choice, cursor = "hand2", font = ftNormal)
  380.         Age = tk.Radiobutton(master, text = "Age", value = 6, variable = self.choice, cursor = "hand2", font = ftNormal)
  381.         Height = tk.Radiobutton(master, text = "Height", value = 7, variable = self.choice, cursor = "hand2", font = ftNormal)
  382.         Weight = tk.Radiobutton(master, text = "Weight", value = 8, variable = self.choice, cursor = "hand2", font = ftNormal)
  383.         HairColor = tk.Radiobutton(master, text = "Hair Color", value = 9, variable = self.choice, cursor = "hand2", font = ftNormal)
  384.         EyeColor = tk.Radiobutton(master, text = "Eye Color", value = 10, variable = self.choice, cursor = "hand2", font = ftNormal)
  385.         Currency = tk.Radiobutton(master, text = "Currency", value = 11, variable = self.choice, cursor = "hand2", font = ftNormal)
  386.  
  387.         Name.grid(row = 1, sticky = "w")
  388.         Class.grid(row = 2, sticky = "w")
  389.         Deity.grid(row = 3, sticky = "w")
  390.         Race.grid(row = 4, sticky = "w")
  391.         Gender.grid(row = 5, sticky = "w")
  392.         Age.grid(row = 6, sticky = "w")
  393.         Height.grid(row = 7, sticky = "w")
  394.         Weight.grid(row = 8, sticky = "w")
  395.         HairColor.grid(row = 9, sticky = "w")
  396.         EyeColor.grid(row = 10, sticky = "w")
  397.         Currency.grid(row = 11, sticky = "w")
  398.  
  399.     def apply(self):
  400.         self.result = self.choice.get()
  401. #Dialog for editing height and/or weight
  402. class editWH(sdg.Dialog):
  403.     def __init__(self, master = None, m = 9):
  404.         self.mode = m - 7
  405.         super().__init__(master)
  406.  
  407.     def toggleUnit(self):
  408.         if self.ChangeUnit.get() == 1:
  409.             self.Unit.config(state = "normal")
  410.         else:
  411.             self.Unit.config(state = "disabled")
  412.  
  413.     def body(self, master):
  414.         choices = ["height", "weight"]
  415.         self.title("Edit " + choices[self.mode].title())
  416.         self.resizable(False, False)
  417.         self.ChangeUnit = tk.IntVar()
  418.         self.ChangeUnit.set(0)
  419.         tk.Label(master, text = "Enter your new " + choices[self.mode] + ":", font = ftNormal).grid(row = 0)
  420.  
  421.         self.Value = tk.Entry(master, font = ftNormal)
  422.         self.Unit = tk.Entry(master, font = ftNormal)
  423.  
  424.         if self.mode == 0:
  425.             self.Value.insert(0, info[6])
  426.             self.Unit.insert(0, info[7])
  427.         elif self.mode == 1:
  428.             self.Value.insert(0, info[8])
  429.             self.Unit.insert(0, info[9])
  430.  
  431.         self.Unit.config(state = "disabled")
  432.  
  433.         ChangeUnitChk = tk.Checkbutton(master, text = "Check to change unit", variable = self.ChangeUnit, command = self.toggleUnit, font = ftNormal)
  434.  
  435.         self.Value.grid(row = 1)
  436.         self.Unit.grid(row = 3)
  437.         ChangeUnitChk.grid(row = 2)
  438.  
  439.     def apply(self):
  440.         try:
  441.             self.result = [int(self.Value.get()), self.Unit.get(), self.ChangeUnit.get()]
  442.         except:
  443.             mbx.showerror("Error","Invalid Measurement")
  444.             self.result = []
  445. #Wrapper function (?) for editing information
  446. def editInfo():
  447.     editInfoSelect(editInfoRoot(base).result)
  448.     refrINFO()
  449. #Function for editing information
  450. def editInfoSelect(choice):
  451.     if choice in range(1, 7) or choice in range(9, 12):
  452.         if choice < 8:
  453.             index = choice - 1
  454.         else:
  455.             index = choice + 1
  456.  
  457.         choices = ["", "name", "class", "deity", "race", "gender", "age", "", "", "hair color", "eye color", "currency"]
  458.        
  459.         if choice == 6:
  460.             temp = sdg.askinteger("Edit " + choices[choice].title(), "Enter your new " + choices[choice], initialvalue = info[index])
  461.    
  462.             if type(temp) == int:
  463.                 info[index] = temp
  464.  
  465.         elif choice != 6:
  466.             temp = sdg.askstring("Edit " + choices[choice].title(), "Enter your new " + choices[choice], initialvalue = info[index])
  467.    
  468.             if type(temp) == str:
  469.                 info[index] = temp
  470.  
  471.     elif choice in range(7, 9):
  472.         n = choice
  473.         if n < 8:
  474.             n -= 1
  475.  
  476.         temp = editWH(base, choice).result
  477.  
  478.         if type(temp) == list and len(temp) > 0:
  479.             info[n] = temp[0]
  480.             if temp[2] == 1:
  481.                 info[n + 1] = temp[1]
  482. #Refreshes the inventory frame
  483. def refrINVT():
  484.     INVTTxt.delete(0, INVTTxt.size())
  485.  
  486.     tempWGHT = 0.0
  487.  
  488.     for item in inventory:
  489.  
  490.         tempWGHT += round(item[2] * item[3], 4)
  491.  
  492.         tempStr = ""
  493.         tempStr += item[0][:36]
  494.         if len(item[0]) > 36:
  495.             tempStr += "... "
  496.         else:
  497.             tempStr += (40 - len(item[0])) * " "
  498.  
  499.         tempStr += str(item[2])
  500.         tempStr += (8 - len(str(item[2]))) * " "
  501.         tempStr += str(item[3])
  502.         tempStr += (8 - len(str(item[3]))) * " "
  503.  
  504.         if item[4] != 0 and item[5]:
  505.             tempStr += "d" + str(item[4]) + " x" + str(item[5])
  506.             tempStr += (8 - len("d" + str(item[4]) + " x" + str(item[5]))) * " "
  507.         else:
  508.             tempStr += 8 * " "
  509.        
  510.         if item[7] != 0:
  511.             tempStr += str(item[6]) + "/" + str(item[7])
  512.             tempStr += (8 - len(str(item[6]) + "/" + str(item[7]))) * " "
  513.         else:
  514.             tempStr += 8 * " "
  515.  
  516.         if item[8]:
  517.             tempStr += "Yes"
  518.         else:
  519.             tempStr += "No"
  520.  
  521.         INVTTxt.insert("end", tempStr)
  522.  
  523.     INVTENCMLbl.config(text = "Encumberance: " + str(tempWGHT))
  524. #Creates a new item
  525. def newItem():
  526.     tempItem = []
  527.     tempStr = ""
  528.  
  529.     try:
  530.         while len(tempStr) == 0:
  531.             tempStr = sdg.askstring("New Item", "Enter the new item's name:")
  532.             if len(tempStr) == 0:
  533.                 mbx.showerror("Error", "You must name your item!")
  534.  
  535.         tempItem.append(tempStr)
  536.  
  537.         tempItem.append(sdg.askstring("New Item", "[Optional] Enter the new item's bonus(es) or description:"))
  538.         if None in tempItem:
  539.             return
  540.  
  541.         tempItem.append(sdg.askinteger("New Item", "Enter how many of the new item you have:", initialvalue = 1))
  542.         if None in tempItem:
  543.             return
  544.  
  545.         tempItem.append(sdg.askfloat("New Item", "Enter the weight of the item:", initialvalue = 0.0))
  546.         if None in tempItem:
  547.             return
  548.  
  549.         tempItem.append(sdg.askinteger("New Item", "[Optional] Enter the type of dice the item uses:", initialvalue = 0))
  550.         if None in tempItem:
  551.             return
  552.  
  553.         tempItem.append(sdg.askinteger("New Item", "[Optional] Enter the amount of dice rolls for the item:", initialvalue = 0))
  554.         if None in tempItem:
  555.             return
  556.        
  557.         tempItem.append(sdg.askinteger("New Item", "[Optional] Enter the maximum magical charges the item has:", initialvalue = 0))
  558.         if None in tempItem:
  559.             return
  560.  
  561.         tempInt = tempItem[6] + 1
  562.         while tempInt > tempItem[6]:
  563.             tempInt = sdg.askinteger("New Item", "[Optional] Enter the current magical charges the item has:", initialvalue = 0)
  564.             if tempInt > tempItem[6]:
  565.                 mbx.showerror("Error", "You can't overcharge your item!")
  566.         tempItem.insert(6, tempInt)
  567.                
  568.         tempItem.append(mbx.askyesno("New Item", "Is the item equipped?"))
  569.  
  570.         inventory.append(tempItem)
  571.         refrINVT()
  572.  
  573.     except TypeError:
  574.         pass
  575. #Root dialog for editing items
  576. class editItemRoot(sdg.Dialog):
  577.     def __init__(self, master = None, i = ""):
  578.         self.item = i
  579.         super().__init__(master)
  580.  
  581.     def body(self, master):
  582.         self.title("Edit " + self.item)
  583.         self.resizable(False, False)
  584.         self.choice = tk.IntVar()
  585.         self.choice.set(0)
  586.         tk.Label(master, text = "Choose an item attribute to edit:", font = ftNormal).grid(row = 0)
  587.  
  588.         Name = tk.Radiobutton(master, text = "Name", value = 0, variable = self.choice, cursor = "hand2", font = ftNormal)
  589.         Bonus = tk.Radiobutton(master, text = "Bonus(es)", value = 1, variable = self.choice, cursor = "hand2", font = ftNormal)
  590.         Count = tk.Radiobutton(master, text = "Count", value = 2, variable = self.choice, cursor = "hand2", font = ftNormal)
  591.         Weight = tk.Radiobutton(master, text = "Weight", value = 3, variable = self.choice, cursor = "hand2", font = ftNormal)
  592.         dType = tk.Radiobutton(master, text = "Dice Type", value = 4, variable = self.choice, cursor = "hand2", font = ftNormal)
  593.         dCount = tk.Radiobutton(master, text = "Dice Count", value = 5, variable = self.choice, cursor = "hand2", font = ftNormal)
  594.         Charges = tk.Radiobutton(master, text = "Charges", value = 6, variable = self.choice, cursor = "hand2", font = ftNormal)
  595.         MaxCharges = tk.Radiobutton(master, text = "Max Charges", value = 7, variable = self.choice, cursor = "hand2", font = ftNormal)
  596.         Equipped = tk.Radiobutton(master, text = "Equipped", value = 8, variable = self.choice, cursor = "hand2", font = ftNormal)
  597.  
  598.         Name.grid(row = 1, sticky = "w")
  599.         Bonus.grid(row = 2, sticky = "w")
  600.         Count.grid(row = 3, sticky = "w")
  601.         Weight.grid(row = 4, sticky = "w")
  602.         dType.grid(row = 5, sticky = "w")
  603.         dCount.grid(row = 6, sticky = "w")
  604.         Charges.grid(row = 7, sticky = "w")
  605.         MaxCharges.grid(row = 8, sticky = "w")
  606.         Equipped.grid(row = 9, sticky = "w")
  607.  
  608.     def apply(self):
  609.         self.result = self.choice.get()
  610. #Changes edited item attributes
  611. def editItem():
  612.     try:
  613.         index = INVTTxt.curselection()[0]
  614.         item = inventory[index]
  615.         attr = editItemRoot(base, item[0]).result
  616.  
  617.         if type(attr) == int:
  618.             choices = ["Name", "Bonus(es)", "Count", "Weight", "Dice Type", "Dice Count", "Charges", "Max Charges", ""]
  619.             title = "Edit " + item[0]
  620.             prompt = "Enter the item's new " + choices[attr] + ":"
  621.  
  622.             if attr < 2:
  623.                 temp = sdg.askstring(title, prompt, initialvalue = item[attr])
  624.  
  625.                 if type(temp) == str and len(temp) > 0 and temp != item[attr]:
  626.                     inventory[index][attr] = temp
  627.  
  628.             elif attr < 6 and attr != 3:
  629.                 temp = sdg.askinteger(title, prompt, initialvalue = item[attr])
  630.  
  631.                 if type(temp) == int and temp != item[attr]:
  632.                     inventory[index][attr] = temp
  633.  
  634.             elif attr == 3:
  635.                 temp = sdg.askfloat(title, prompt, initialvalue = item[attr])
  636.  
  637.                 if type(temp) == float and temp != item[attr]:
  638.                     inventory[index][attr] = temp
  639.  
  640.             elif attr == 6:
  641.                 temp = item[7] + 1
  642.                 while type(temp) == int and temp > item[7]:
  643.                     temp = sdg.askinteger(title, prompt, initialvalue = item[attr])
  644.                    
  645.                     if type(temp) == int and temp > item[7]:
  646.                         mbx.showerror("Error", "You can't overcharge your item!")
  647.  
  648.                 if type(temp) == int and temp != item[attr]:
  649.                     inventory[index][attr] = temp
  650.  
  651.             elif attr == 7:
  652.                 temp = sdg.askinteger(title, prompt, initialvalue = item[attr])
  653.  
  654.                 if type(temp) == int and temp != item[attr]:
  655.                     if temp < item[6]:
  656.                         inventory[index][6] = temp
  657.  
  658.                     inventory[index][attr] = temp
  659.  
  660.             else:
  661.                 inventory[index][attr] =  not inventory[index][attr]
  662.  
  663.             refrINVT()
  664.  
  665.     except IndexError:
  666.         mbx.showerror("Error", "No item selected!")
  667. #Shows an item's bonus(es) or description
  668. def inspectItem():
  669.     try:
  670.         if len(inventory[INVTTxt.curselection()[0]][1]) > 0:
  671.             mbx.showinfo("Item: " + inventory[INVTTxt.curselection()[0]][0], "Bonus:\n" + inventory[INVTTxt.curselection()[0]][1])
  672.         else:
  673.             mbx.showinfo("Item: " + inventory[INVTTxt.curselection()[0]][0], "Bonus:\n[None]")
  674.  
  675.     except IndexError:
  676.         mbx.showerror("Error", "No item selected!")
  677. #Deletes an item
  678. def deleteItem():
  679.     try:
  680.         if mbx.askyesno("Delete Item: " + inventory[INVTTxt.curselection()[0]][0], "Are you sure you want to delete this item?"):
  681.             del inventory[INVTTxt.curselection()[0]]
  682.             refrINVT()
  683.  
  684.     except IndexError:
  685.         mbx.showerror("Error", "No item selected!")
  686. #Root dialog for sorting items
  687. class sortItemsRoot(sdg.Dialog):
  688.     def body(self, master):
  689.         self.title("Sort Items")
  690.         self.resizable(False, False)
  691.         self.choice = tk.IntVar()
  692.         self.order = tk.IntVar()
  693.         self.choice.set(0)
  694.         self.order.set(0)
  695.         tk.Label(master, text = "Choose an attribute to sort by:", font = ftNormal).grid(row = 0)
  696.         tk.Label(master, text = "Choose an order to sort by:", font = ftNormal).grid(row = 9)
  697.  
  698.         Name = tk.Radiobutton(master, text = "Name", value = 0, variable = self.choice, cursor = "hand2", font = ftNormal)
  699.         Count = tk.Radiobutton(master, text = "Count", value = 2, variable = self.choice, cursor = "hand2", font = ftNormal)
  700.         Weight = tk.Radiobutton(master, text = "Weight", value = 3, variable = self.choice, cursor = "hand2", font = ftNormal)
  701.         dType = tk.Radiobutton(master, text = "Dice Type", value = 4, variable = self.choice, cursor = "hand2", font = ftNormal)
  702.         dCount = tk.Radiobutton(master, text = "Dice Count", value = 5, variable = self.choice, cursor = "hand2", font = ftNormal)
  703.         Charges = tk.Radiobutton(master, text = "Charges", value = 6, variable = self.choice, cursor = "hand2", font = ftNormal)
  704.         MaxCharges = tk.Radiobutton(master, text = "Max Charges", value = 7, variable = self.choice, cursor = "hand2", font = ftNormal)
  705.         Equipped = tk.Radiobutton(master, text = "Equipped", value = 8, variable = self.choice, cursor = "hand2", font = ftNormal)
  706.         Ascending = tk.Radiobutton(master, text = "Ascending", value = 1, variable = self.order, cursor = "hand2", font = ftNormal)
  707.         Descending = tk.Radiobutton(master, text = "Descending", value = 0, variable = self.order, cursor = "hand2", font = ftNormal)
  708.  
  709.         Name.grid(row = 1, sticky = "w")
  710.         Count.grid(row = 2, sticky = "w")
  711.         Weight.grid(row = 3, sticky = "w")
  712.         dType.grid(row = 4, sticky = "w")
  713.         dCount.grid(row = 5, sticky = "w")
  714.         Charges.grid(row = 6, sticky = "w")
  715.         MaxCharges.grid(row = 7, sticky = "w")
  716.         Equipped.grid(row = 8, sticky = "w")
  717.         Ascending.grid(row = 10, sticky = "w")
  718.         Descending.grid(row = 11, sticky = "w")
  719.  
  720.     def apply(self):
  721.         self.result = [self.choice.get(), self.order.get()]
  722. #Sorts inventory by various attributes
  723. def sortItems():
  724.     choice = sortItemsRoot(base).result
  725.    
  726.     if type(choice) == list:
  727.         if choice[1] == 1:
  728.             order = True
  729.         else:
  730.             order = False
  731.  
  732.         inventory.sort(key = op.itemgetter(choice[0]), reverse = order)
  733.         refrINVT()
  734. #Rolls virtual dice of various types
  735. def rollDice(d):
  736.     def rollDiceWrap(dice = d):
  737.         if dice == 0:
  738.             DICELbl.config(text = "0 | d?")
  739.         elif dice == 2:
  740.             if rnd.randint(0,1):
  741.                 DICELbl.config(text = "Heads | Coin")
  742.             else:
  743.                 DICELbl.config(text = "Tails | Coin")
  744.         elif dice < 100:
  745.             DICELbl.config(text = str(rnd.randint(1, dice)) + " | d" + str(dice))
  746.         else:
  747.             DICELbl.config(text = str(rnd.randint(0, 100)) + " | d%")
  748.     return rollDiceWrap
  749. #Refreshes the skills frame
  750. def refrSKIL():
  751.     SKILTxt.delete(0, SKILTxt.size())
  752.     stats = ["STR", "DEX", "CON", "INT", "WIS", "CHA"]
  753.  
  754.     for sk in skills:
  755.         SKILTxt.insert("end", stats[sk[0]] + "|" + sk[1])
  756. #Dialog for selecting the attribute stat associated with a skill or ability
  757. class skillStat(sdg.Dialog):
  758.     def __init__(self, master = None, m = "", sa = "", i = 0):
  759.         self.mode = m
  760.         self.skab = sa
  761.         self.index = i
  762.         super().__init__(master)
  763.  
  764.     def body(self, master):
  765.         self.resizable(False, False)
  766.         self.stat = tk.IntVar()
  767.  
  768.         if self.skab == "skill":
  769.             if self.mode == "edit":
  770.                 self.title("Edit " + skills[self.index][1])
  771.                 self.stat.set(skills[self.index][0])
  772.  
  773.             elif self.mode == "new":
  774.                 self.title("New Skill")
  775.                 self.stat.set(0)
  776.  
  777.         elif self.skab == "ability":
  778.             if self.mode == "edit":
  779.                 self.title("Edit " + abilities[self.index][1])
  780.                 self.stat.set(abilities[self.index][0])
  781.  
  782.             elif self.mode == "new":
  783.                 self.title("New Ability")
  784.                 self.stat.set(0)
  785.  
  786.  
  787.         PRMPLbl = tk.Label(master, font = ftSmall)
  788.         PRMPLbl.grid(row = 0)
  789.         if self.skab == "skill":
  790.             if self.mode == "edit":
  791.                 PRMPLbl.config(text = "[Optional] Choose a new stat for the skill:")
  792.             elif self.mode == "new":
  793.                 PRMPLbl.config(text = "Choose a stat for the skill:")
  794.         elif self.skab == "ability":
  795.             if self.mode == "edit":
  796.                 PRMPLbl.config(text = "[Optional] Choose a new stat for the ability:")
  797.             elif self.mode == "new":
  798.                 PRMPLbl.config(text = "Choose a stat for the ability:")
  799.  
  800.         STRBtn = tk.Radiobutton(master, text = "Strength", value = 0, variable = self.stat, font = ftNormal)
  801.         DEXBtn = tk.Radiobutton(master, text = "Dexterity", value = 1, variable = self.stat, font = ftNormal)
  802.         CONBtn = tk.Radiobutton(master, text = "Constitution", value = 2, variable = self.stat, font = ftNormal)
  803.         INTBtn = tk.Radiobutton(master, text = "Intelligence", value = 3, variable = self.stat, font = ftNormal)
  804.         WISBtn = tk.Radiobutton(master, text = "Wisdom", value = 4, variable = self.stat, font = ftNormal)
  805.         CHABtn = tk.Radiobutton(master, text = "Charisma", value = 5, variable = self.stat, font = ftNormal)
  806.        
  807.         STRBtn.grid(row = 1, sticky = "w")
  808.         DEXBtn.grid(row = 2, sticky = "w")
  809.         CONBtn.grid(row = 3, sticky = "w")
  810.         INTBtn.grid(row = 4, sticky = "w")
  811.         WISBtn.grid(row = 5, sticky = "w")
  812.         CHABtn.grid(row = 6, sticky = "w")
  813.  
  814.     def apply(self):
  815.         self.result = self.stat.get()
  816. #Creates a new skill
  817. def newSkill():
  818.     tempStr = ""
  819.     while type(tempStr) == str and len(tempStr) < 1:
  820.         tempStr = sdg.askstring("New Skill", "Enter the new skill's name:")
  821.         if type(tempStr) == str and len(tempStr) < 1:
  822.             mbx.showerror("Error", "You must name this skill!")
  823.  
  824.     if type(tempStr) == str:
  825.         tempInt = skillStat(base, "new", "skill").result
  826.         if type(tempInt) == int:
  827.             skills.append([tempInt, tempStr])
  828.             refrSKIL()
  829.             if SKILSCORVar.get():
  830.                 changeScores()
  831. #Edits a skill
  832. def editSkill():
  833.     try:
  834.         index = SKILTxt.curselection()[0]
  835.         sk = skills[index]
  836.         tempStr = sdg.askstring("Edit " + sk[1], "[Optional] Enter the skill's new name:", initialvalue = sk[1])
  837.         if type(tempStr) == str:
  838.             if len(tempStr) > 0 and tempStr != sk[1]:
  839.                 skills[index][1] = tempStr
  840.            
  841.             tempInt = skillStat(base, "edit", "skill", index).result
  842.             if type(tempInt) == int and tempInt != sk[0]:
  843.                 skills[index][0] = tempInt
  844.  
  845.             refrSKIL()
  846.             if SKILSCORVar.get():
  847.                 changeScores()
  848.     except IndexError:
  849.         mbx.showerror("Error", "No skill selected!")
  850. #Deletes a skill
  851. def deleteSkill():
  852.     try:
  853.         if mbx.askyesno("Delete " + skills[SKILTxt.curselection()[0]][1], "Are you sure you want to delete this skill?"):
  854.             del skills[SKILTxt.curselection()[0]]
  855.             refrSKIL()
  856.             if SKILSCORVar.get():
  857.                 changeScores()
  858.  
  859.     except IndexError:
  860.         mbx.showerror("Error", "No skill selected!")
  861. #Dialog for skills and/or abilities
  862. class sortSkillsAbils(sdg.Dialog):
  863.     def __init__(self, master = None, sa = ""):
  864.         self.skab = sa
  865.         super().__init__(master)
  866.  
  867.     def body(self, master):
  868.         if self.skab == "skill":
  869.             self.title("Sort Skills")
  870.         elif self.skab == "ability":
  871.             self.title("Sort Abilities")
  872.         self.resizable(False, False)
  873.         self.choice = tk.IntVar()
  874.         self.order = tk.IntVar()
  875.         self.choice.set(0)
  876.         self.order.set(0)
  877.         tk.Label(master, text = "Choose an attribute to sort by:", font = ftNormal).grid(row = 0)
  878.         tk.Label(master, text = "Choose an order to sort by:", font = ftNormal).grid(row = 3)
  879.  
  880.         Name = tk.Radiobutton(master, text = "Name", value = 1, variable = self.choice, cursor = "hand2", font = ftNormal)
  881.         Stat = tk.Radiobutton(master, text = "Stat", value = 0, variable = self.choice, cursor = "hand2", font = ftNormal)
  882.         Ascending = tk.Radiobutton(master, text = "Ascending", value = 1, variable = self.order, cursor = "hand2", font = ftNormal)
  883.         Descending = tk.Radiobutton(master, text = "Descending", value = 0, variable = self.order, cursor = "hand2", font = ftNormal)
  884.  
  885.         Name.grid(row = 1, sticky = "w")
  886.         Stat.grid(row = 2, sticky = "w")
  887.         Ascending.grid(row = 4, sticky = "w")
  888.         Descending.grid(row = 5, sticky = "w")
  889.  
  890.     def apply(self):
  891.         self.result = [self.choice.get(), self.order.get()]
  892. #Sorts skills by name or attribute stat
  893. def sortSkills():
  894.     choice = sortSkillsAbils(base, "skill").result
  895.  
  896.     if type(choice) == list:
  897.         if choice[1] == 1:
  898.             order = True
  899.         else:
  900.             order = False
  901.  
  902.         skills.sort(key = op.itemgetter(choice[0]), reverse = order)
  903.         refrSKIL()
  904. #Toggles between including racial/class ability score bonuses into the Stats display
  905. def changeScores():
  906.     if SKILSCORVar.get():
  907.         newScores = []
  908.  
  909.         for i in range(len(mainStats)):
  910.             newScores.append(mainStats[i])
  911.        
  912.         for sk in skills:
  913.             if sk[1][0] in "+-":
  914.                 try:
  915.                     if sk[1][0] == '+':
  916.                         newScores[sk[0]] += int(sk[1][1:].split()[0])
  917.                     else:
  918.                         newScores[sk[0]] += int(sk[1][0:].split()[0])
  919.                 except ValueError:
  920.                     pass
  921.  
  922.         newMods = [0, 0, 0, 0, 0, 0]
  923.         for i in range(len(newScores)):
  924.             newMods[i] = (newScores[i] - 10) // 2
  925.  
  926.         refrSTAT(False, newScores, newMods)
  927.     else:
  928.         refrSTAT()
  929. #Shows the information box regarding the above functionality
  930. def showPrefixInfo():
  931.     mbx.showinfo("Skills: Apply Prefixes", "When checked, all skills whose name have a leading +/- character \
  932. followed by an integer then a space will be compiled and added to your base Ability Scores without changing said scores. \
  933. This is useful for adding temporary skill entries to act as buffs from equipment or spells.")
  934. #Refreshes the abilities frame
  935. def refrABIL():
  936.     ABILTxt.delete(0, ABILTxt.size())
  937.     stats = ["STR", "DEX", "CON", "INT", "WIS", "CHA"]
  938.  
  939.     for ab in abilities:
  940.         ABILTxt.insert("end", stats[ab[0]] + "|" + ab[1])
  941. #Creates a new ability
  942. def newAbility():
  943.     tempStrA = ""
  944.     while type(tempStrA) == str and len(tempStrA) < 1:
  945.         tempStrA = sdg.askstring("New Ability", "Enter the new ability's name:")
  946.        
  947.         if type(tempStrA) == str and len(tempStrA) < 1:
  948.             mbx.showerror("Error", "You must name this ability!")
  949.  
  950.     if type(tempStrA) == str:
  951.         tempInt = skillStat(base, "new", "ability").result
  952.        
  953.         if type(tempInt) == int:
  954.             tempStrB = sdg.askstring("New Ability", "Describe the new ability:")
  955.            
  956.             if type(tempStrB) == str:
  957.                 abilities.append([tempInt, tempStrA, tempStrB])
  958.                 refrABIL()
  959. #Edits an ability
  960. def editAbility():
  961.     try:
  962.         index = ABILTxt.curselection()[0]
  963.         ab = abilities[index]
  964.         tempStr = sdg.askstring("Edit " + ab[1], "[Optional] Enter the ability's new name:", initialvalue = ab[1])
  965.         if type(tempStr) == str:
  966.             if len(tempStr) > 0 and tempStr != ab[1]:
  967.                 abilities[index][1] = tempStr
  968.            
  969.             tempInt = skillStat(base, "edit", "ability", index).result
  970.             if type(tempInt) == int:
  971.                 if tempInt != ab[0]:
  972.                     abilities[index][0] = tempInt
  973.  
  974.                 tempStr = sdg.askstring("Edit " + ab[1], "[Optional] Describe the ability again:", initialvalue = ab[2])
  975.                 if type(tempStr) == str and len(tempStr) > 0 and tempStr != ab[2]:
  976.                     abilities[index][2] = tempStr
  977.  
  978.             refrABIL()
  979.     except IndexError:
  980.         mbx.showerror("Error", "No ability selected!")
  981. #Deletes an ability
  982. def deleteAbility():
  983.     try:
  984.         if mbx.askyesno("Delete " + abilities[ABILTxt.curselection()[0]][1], "Are you sure you want to delete this ability?"):
  985.             del abilities[ABILTxt.curselection()[0]]
  986.             refrABIL()
  987.  
  988.     except IndexError:
  989.         mbx.showerror("Error", "No ability selected!")
  990. #Shows an ability's description
  991. def inspectAbility():
  992.     try:
  993.         if len(abilities[ABILTxt.curselection()[0]][2]) > 0:
  994.             mbx.showinfo("Ability: " + abilities[ABILTxt.curselection()[0]][1], "Description:\n" + abilities[ABILTxt.curselection()[0]][2])
  995.         else:
  996.             mbx.showinfo("Ability: " + abilities[ABILTxt.curselection()[0]][1], "Description:\n[None]")
  997.  
  998.     except IndexError:
  999.         mbx.showerror("Error", "No ability selected!")
  1000. #Sorts abilities by name or attribute stat
  1001. def sortAbilities():
  1002.     choice = sortSkillsAbils(base, "ability").result
  1003.  
  1004.     if type(choice) == list:
  1005.         if choice[1] == 1:
  1006.             order = True
  1007.         else:
  1008.             order = False
  1009.  
  1010.         abilities.sort(key = op.itemgetter(choice[0]), reverse = order)
  1011.         refrABIL()
  1012. #Prevents the program from exiting on one click
  1013. def exitOverride():
  1014.     if mbx.askyesno("Warning! Data Loss Possibility Detected!", "Unsaved data will be lost if you quit. Are you sure you want to quit?"):
  1015.         base.destroy()
  1016. #Dialog for choosing a method to set main stats
  1017. class newCharacterStatRoot(sdg.Dialog):
  1018.     def body(self, master):
  1019.         self.title("New Character: Stats")
  1020.         self.resizable(False, False)
  1021.         self.choice = tk.IntVar()
  1022.         self.choice.set(0)
  1023.  
  1024.         tk.Label(master, text = "It is now time to set your ability scores / main stats.\nChoose a method below:", font = ftNormal).grid(row = 0)
  1025.         PB = tk.Radiobutton(master, text = "5E Point-Buy", value = 0, variable =  self.choice, cursor = "hand2", font = ftNormal)
  1026.         DR = tk.Radiobutton(master, text = "Dice Rolls", value = 1, variable =  self.choice, cursor = "hand2", font = ftNormal)
  1027.         MAN = tk.Radiobutton(master, text = "Manual Entry", value = 2, variable =  self.choice, cursor = "hand2", font = ftNormal)
  1028.  
  1029.         PB.grid(row = 1, sticky = "w")
  1030.         DR.grid(row = 2, sticky = "w")
  1031.         MAN.grid(row = 3, sticky = "w")
  1032.  
  1033.     def apply(self):
  1034.         self.result = self.choice.get()
  1035. #Dialog for a point-buy calculator
  1036. class pointBuyRoot(sdg.Dialog):
  1037.     def body(self, master):
  1038.         self.title("New Character: Point-Buy Stats")
  1039.         self.resizable(False, False)
  1040.  
  1041.         self.STR = tk.Scale(master, from_ = 8, to = 15, orient = "horizontal", command = self.calculatePoints, font = ftNormal)
  1042.         self.DEX = tk.Scale(master, from_ = 8, to = 15, orient = "horizontal", command = self.calculatePoints, font = ftNormal)
  1043.         self.CON = tk.Scale(master, from_ = 8, to = 15, orient = "horizontal", command = self.calculatePoints, font = ftNormal)
  1044.         self.INT = tk.Scale(master, from_ = 8, to = 15, orient = "horizontal", command = self.calculatePoints, font = ftNormal)
  1045.         self.WIS = tk.Scale(master, from_ = 8, to = 15, orient = "horizontal", command = self.calculatePoints, font = ftNormal)
  1046.         self.CHA = tk.Scale(master, from_ = 8, to = 15, orient = "horizontal", command = self.calculatePoints, font = ftNormal)
  1047.  
  1048.         self.bSTRVar = tk.IntVar()
  1049.         self.bDEXVar = tk.IntVar()
  1050.         self.bCONVar = tk.IntVar()
  1051.         self.bINTVar = tk.IntVar()
  1052.         self.bWISVar = tk.IntVar()
  1053.         self.bCHAVar = tk.IntVar()
  1054.  
  1055.         self.bSTRVar.set(-1)
  1056.         self.bDEXVar.set(-1)
  1057.         self.bCONVar.set(-1)
  1058.         self.bINTVar.set(-1)
  1059.         self.bWISVar.set(-1)
  1060.         self.bCHAVar.set(-1)
  1061.  
  1062.         bSTR = tk.Label(master, textvariable = self.bSTRVar, width = 2, relief = "ridge", font = ftNormal)
  1063.         bDEX = tk.Label(master, textvariable = self.bDEXVar, width = 2, relief = "ridge", font = ftNormal)
  1064.         bCON = tk.Label(master, textvariable = self.bCONVar, width = 2, relief = "ridge", font = ftNormal)
  1065.         bINT = tk.Label(master, textvariable = self.bINTVar, width = 2, relief = "ridge", font = ftNormal)
  1066.         bWIS = tk.Label(master, textvariable = self.bWISVar, width = 2, relief = "ridge", font = ftNormal)
  1067.         bCHA = tk.Label(master, textvariable = self.bCHAVar, width = 2, relief = "ridge", font = ftNormal)
  1068.  
  1069.         bSTR.grid(row = 2, column = 2, sticky = "s")
  1070.         bDEX.grid(row = 3, column = 2, sticky = "s")
  1071.         bCON.grid(row = 4, column = 2, sticky = "s")
  1072.         bINT.grid(row = 5, column = 2, sticky = "s")
  1073.         bWIS.grid(row = 6, column = 2, sticky = "s")
  1074.         bCHA.grid(row = 7, column = 2, sticky = "s")
  1075.  
  1076.         self.currentPoints = tk.IntVar()
  1077.         self.currentPoints.set(0)
  1078.  
  1079.         tk.Label(master, text = "Point Total:", font = ftLarge).grid(row = 0, column = 0, columnspan = 2)
  1080.         tk.Label(master, textvariable = self.currentPoints, relief = "ridge", width = 2, font = ftLarge).grid(row = 0, column = 1, sticky = "e")
  1081.         tk.Label(master, text = "/27", font = ftLarge).grid(row = 0, column = 2)
  1082.         tk.Label(master, text = "Attribute", font = ftSmall).grid(row = 1, column = 0)
  1083.         tk.Label(master, text = "Value", font = ftSmall).grid(row = 1, column = 1)
  1084.         tk.Label(master, text = "Modifier", font = ftSmall).grid(row = 1, column = 2)
  1085.         tk.Label(master, text = "Strength", width = 16, font = ftNormal).grid(row = 2, column = 0, sticky = "s")
  1086.         tk.Label(master, text = "Dexterity", width = 16, font = ftNormal).grid(row = 3, column = 0, sticky = "s")
  1087.         tk.Label(master, text = "Constitution", width = 16, font = ftNormal).grid(row = 4, column = 0, sticky = "s")
  1088.         tk.Label(master, text = "Intelligence", width = 16, font = ftNormal).grid(row = 5, column = 0, sticky = "s")
  1089.         tk.Label(master, text = "Wisdom", width = 16, font = ftNormal).grid(row = 6, column = 0, sticky = "s")
  1090.         tk.Label(master, text = "Charisma", width = 16, font = ftNormal).grid(row = 7, column = 0, sticky = "s")
  1091.  
  1092.         self.STR.grid(row = 2, column = 1)
  1093.         self.DEX.grid(row = 3, column = 1)
  1094.         self.CON.grid(row = 4, column = 1)
  1095.         self.INT.grid(row = 5, column = 1)
  1096.         self.WIS.grid(row = 6, column = 1)
  1097.         self.CHA.grid(row = 7, column = 1)
  1098.  
  1099.     def validate(self):
  1100.         if self.currentPoints.get() == 27 and mbx.askyesno("Confirmation", "Are you sure you want these values for your main stats?"):
  1101.             return 1
  1102.         elif self.currentPoints.get() != 27:
  1103.             mbx.showerror("Error", "You need to have a Point Total of 27 to have valid values!")
  1104.             return 0
  1105.         else:
  1106.             return 0
  1107.  
  1108.     def apply(self):
  1109.         resultString = ""
  1110.  
  1111.         resultString += str(self.STR.get()) + "|"
  1112.         resultString += str(self.DEX.get()) + "|"
  1113.         resultString += str(self.CON.get()) + "|"
  1114.         resultString += str(self.INT.get()) + "|"
  1115.         resultString += str(self.WIS.get()) + "|"
  1116.         resultString += str(self.CHA.get()) + "\n"
  1117.         resultString += str(self.bSTRVar.get()) + "|"
  1118.         resultString += str(self.bDEXVar.get()) + "|"
  1119.         resultString += str(self.bCONVar.get()) + "|"
  1120.         resultString += str(self.bINTVar.get()) + "|"
  1121.         resultString += str(self.bWISVar.get()) + "|"
  1122.         resultString += str(self.bCHAVar.get())
  1123.  
  1124.         self.result = resultString
  1125.  
  1126.     def calculatePoints(self, changedSlider):
  1127.         self.bSTRVar.set((self.STR.get() - 10) // 2)
  1128.         self.bDEXVar.set((self.DEX.get() - 10) // 2)
  1129.         self.bCONVar.set((self.CON.get() - 10) // 2)
  1130.         self.bINTVar.set((self.INT.get() - 10) // 2)
  1131.         self.bWISVar.set((self.WIS.get() - 10) // 2)
  1132.         self.bCHAVar.set((self.CHA.get() - 10) // 2)
  1133.  
  1134.         currentScores = []
  1135.  
  1136.         currentScores.append(self.STR.get())
  1137.         currentScores.append(self.DEX.get())
  1138.         currentScores.append(self.CON.get())
  1139.         currentScores.append(self.INT.get())
  1140.         currentScores.append(self.WIS.get())
  1141.         currentScores.append(self.CHA.get())
  1142.  
  1143.         pointsUsed = 0
  1144.  
  1145.         for score in currentScores:
  1146.             if score < 14:
  1147.                 pointsUsed += score - 8
  1148.             elif score == 14:
  1149.                 pointsUsed += 7
  1150.             elif score == 15:
  1151.                 pointsUsed += 9
  1152.  
  1153.         self.currentPoints.set(pointsUsed)
  1154. #Dialog for a dice-roll stat selector
  1155. class diceRollStatsRoot(sdg.Dialog):
  1156.     def body(self, master):
  1157.         self.title("New Character: Dice Roll Stats")
  1158.         self.resizable(False, False)
  1159.         self.letters = ('A', 'B', 'C', 'D', 'E', 'F')
  1160.         self.stats = ('STR', 'CHA', 'WIS', 'INT', 'CON', 'DEX')
  1161.         self.rolled = False
  1162.         self.rolls = [0, 0, 0, 0, 0, 0]
  1163.         #self.rollsVar = tk.StringVar()
  1164.         #self.rollsVar.set("Press \"Roll\" to begin!")
  1165.  
  1166.         tk.Label(master, text = "You can swap values between attributes below.\nIn the \"six 4d6\" roll mode,\nthe lowest roll of each batch is not counted.", font = ftSmall).grid(row = 0, columnspan = 3)
  1167.  
  1168.         #rollsLbl = tk.Label(master, textvariable = self.rollsVar, width = 36, relief = "ridge", font = ftNormal)
  1169.         #rollsLbl.grid(row = 1, column = 0, columnspan = 2)
  1170.         self.swapOne = tk.Spinbox(master, width = 4, values = self.stats, wrap = True, font = ftNormal)
  1171.         self.swapTwo = tk.Spinbox(master, width = 4, values = self.stats, wrap = True, font = ftNormal)
  1172.         self.rollMode = tk.Spinbox(master, width = 8, values = ("six 1d20", "six 4d6"), wrap = True, font = ftNormal)
  1173.  
  1174.         self.swapOne.grid(row = 1, column = 0)
  1175.         self.swapTwo.grid(row = 1, column = 1)
  1176.         self.rollMode.grid(row = 2, column = 1)
  1177.  
  1178.         tk.Button(master, text = "Swap", command = self.swap, cursor = "hand2", font = ftNormal).grid(row = 1, column = 2)
  1179.         tk.Label(master, text = "Roll Mode", font = ftNormal).grid(row = 2, column = 0)
  1180.         tk.Button(master, text = "Roll", command = self.rollStats, cursor = "hand2", font = ftNormal).grid(row = 2, column = 2)
  1181.  
  1182.         tk.Label(master, text = "Attribute", font = ftSmall).grid(row = 3, column = 0)
  1183.         tk.Label(master, text = "Value", font = ftSmall).grid(row = 3, column = 1)
  1184.         tk.Label(master, text = "Modifier", font = ftSmall).grid(row = 3, column = 2)
  1185.  
  1186.         tk.Label(master, text = "Strength", font = ftNormal).grid(row = 4, column = 0)
  1187.         tk.Label(master, text = "Dexterity", font = ftNormal).grid(row = 5, column = 0)
  1188.         tk.Label(master, text = "Constitution", font = ftNormal).grid(row = 6, column = 0)
  1189.         tk.Label(master, text = "Intelligence", font = ftNormal).grid(row = 7, column = 0)
  1190.         tk.Label(master, text = "Wisdom", font = ftNormal).grid(row = 8, column = 0)
  1191.         tk.Label(master, text = "Charisma", font = ftNormal).grid(row = 9, column = 0)
  1192.  
  1193.         self.STR = tk.Label(master, text = "?", width = 4, relief = "ridge", font = ftNormal)
  1194.         self.DEX = tk.Label(master, text = "?", width = 4, relief = "ridge", font = ftNormal)
  1195.         self.CON = tk.Label(master, text = "?", width = 4, relief = "ridge", font = ftNormal)
  1196.         self.INT = tk.Label(master, text = "?", width = 4, relief = "ridge", font = ftNormal)
  1197.         self.WIS = tk.Label(master, text = "?", width = 4, relief = "ridge", font = ftNormal)
  1198.         self.CHA = tk.Label(master, text = "?", width = 4, relief = "ridge", font = ftNormal)
  1199.  
  1200.         self.bSTR = tk.Label(master, text = "?", width = 4, relief = "ridge", font = ftNormal)
  1201.         self.bDEX = tk.Label(master, text = "?", width = 4, relief = "ridge", font = ftNormal)
  1202.         self.bCON = tk.Label(master, text = "?", width = 4, relief = "ridge", font = ftNormal)
  1203.         self.bINT = tk.Label(master, text = "?", width = 4, relief = "ridge", font = ftNormal)
  1204.         self.bWIS = tk.Label(master, text = "?", width = 4, relief = "ridge", font = ftNormal)
  1205.         self.bCHA = tk.Label(master, text = "?", width = 4, relief = "ridge", font = ftNormal)
  1206.  
  1207.         self.STR.grid(row = 4, column = 1)
  1208.         self.DEX.grid(row = 5, column = 1)
  1209.         self.CON.grid(row = 6, column = 1)
  1210.         self.INT.grid(row = 7, column = 1)
  1211.         self.WIS.grid(row = 8, column = 1)
  1212.         self.CHA.grid(row = 9, column = 1)
  1213.  
  1214.         self.bSTR.grid(row = 4, column = 2)
  1215.         self.bDEX.grid(row = 5, column = 2)
  1216.         self.bCON.grid(row = 6, column = 2)
  1217.         self.bINT.grid(row = 7, column = 2)
  1218.         self.bWIS.grid(row = 8, column = 2)
  1219.         self.bCHA.grid(row = 9, column = 2)
  1220.  
  1221.  
  1222.     def validate(self):
  1223.         if not self.rolled:
  1224.             mbx.showerror("Error", "You must roll at least once!")
  1225.             return 0
  1226.  
  1227.         elif not mbx.askyesno("Confirmation", "Are you sure you want these values for your main stats?"):
  1228.             return 0
  1229.  
  1230.         return 1
  1231.  
  1232.     def apply(self):
  1233.         resultString = ""
  1234.  
  1235.         resultString += self.STR["text"] + "|"
  1236.         resultString += self.DEX["text"] + "|"
  1237.         resultString += self.CON["text"] + "|"
  1238.         resultString += self.INT["text"] + "|"
  1239.         resultString += self.WIS["text"] + "|"
  1240.         resultString += self.CHA["text"] + "\n"
  1241.         resultString += self.bSTR["text"] + "|"
  1242.         resultString += self.bDEX["text"] + "|"
  1243.         resultString += self.bCON["text"] + "|"
  1244.         resultString += self.bINT["text"] + "|"
  1245.         resultString += self.bWIS["text"] + "|"
  1246.         resultString += self.bCHA["text"]
  1247.  
  1248.         self.result = resultString
  1249.  
  1250.     def setValues(self, master, tempStats):
  1251.         self.STR.config(text = str(tempStats[0]))
  1252.         self.DEX.config(text = str(tempStats[1]))
  1253.         self.CON.config(text = str(tempStats[2]))
  1254.         self.INT.config(text = str(tempStats[3]))
  1255.         self.WIS.config(text = str(tempStats[4]))
  1256.         self.CHA.config(text = str(tempStats[5]))
  1257.  
  1258.         self.bSTR.config(text = str((int(tempStats[0]) - 10) // 2))
  1259.         self.bDEX.config(text = str((int(tempStats[1]) - 10) // 2))
  1260.         self.bCON.config(text = str((int(tempStats[2]) - 10) // 2))
  1261.         self.bINT.config(text = str((int(tempStats[3]) - 10) // 2))
  1262.         self.bWIS.config(text = str((int(tempStats[4]) - 10) // 2))
  1263.         self.bCHA.config(text = str((int(tempStats[5]) - 10) // 2))
  1264.  
  1265.     def swap(self):
  1266.         if not self.rolled:
  1267.             mbx.showerror("Error", "You must roll at least once!")
  1268.             return
  1269.  
  1270.         tempStats = []
  1271.         localStats = ('STR', 'DEX', 'CON', 'INT', 'WIS', 'CHA')
  1272.  
  1273.         statA = localStats.index(self.swapOne.get())
  1274.         statB = localStats.index(self.swapTwo.get())
  1275.  
  1276.         if statA == statB:
  1277.             mbx.showerror("Error", "You can't swap a value with itself!")
  1278.             return
  1279.  
  1280.         tempStats.append(self.STR["text"])
  1281.         tempStats.append(self.DEX["text"])
  1282.         tempStats.append(self.CON["text"])
  1283.         tempStats.append(self.INT["text"])
  1284.         tempStats.append(self.WIS["text"])
  1285.         tempStats.append(self.CHA["text"])
  1286.  
  1287.         temp = tempStats[statA]
  1288.         tempStats[statA] = tempStats[statB]
  1289.         tempStats[statB] = temp
  1290.  
  1291.         self.setValues(self, tempStats)
  1292.  
  1293.     def rollStats(self):
  1294.         tempStats = [0, 0, 0, 0, 0, 0]
  1295.         tempRolls = []
  1296.         tempStr = ""
  1297.  
  1298.         if self.rollMode.get() == "six 1d20":
  1299.             for i in range(len(tempStats)):
  1300.                 tempStats[i] = rnd.randint(1, 20)
  1301.  
  1302.         else:
  1303.             for i in range(len(tempStats)):
  1304.                 for n in range(4):
  1305.                     tempRolls.append(rnd.randint(1,6))
  1306.  
  1307.                 tempRolls.remove(min(tempRolls))
  1308.  
  1309.                 tempStats[i] = sum(tempRolls)
  1310.  
  1311.                 tempRolls = []
  1312.  
  1313.         self.setValues(self, tempStats)
  1314.  
  1315.         self.rolled = True
  1316. #Guides the player through character creation
  1317. def newCharacter():
  1318.     if not mbx.askyesno("Warning! Data Loss Possibility Detected!", "Making a new character will erase all current data\nAre you sure you want to continue?"):
  1319.         return
  1320.  
  1321.     tempInfo = []
  1322.     tempHealth = []
  1323.     tempExp = []
  1324.     tempMStats = []
  1325.     tempBStats = []
  1326.     tempInt = 0
  1327.     tempIntB = 0
  1328.     tempStr = ""
  1329.     tempCount = 0
  1330.     infoChoices = ["name", "class", "deity", "race", "gender", "age", "height", "unit", "weight", "unit", "hair color", "eye color", "currency"]
  1331.     statChoices = ["Strength", "Dexterity", "Constitution", "Intelligence", "Wisdom", "Charisma"]
  1332.  
  1333.     while len(tempInfo) < 14:
  1334.         if tempCount < 5 or tempCount > 9:
  1335.             tempStr = sdg.askstring("New Character: Info", "Enter your " + infoChoices[tempCount] + ":")
  1336.             if type(tempStr) == str:
  1337.                 tempInfo.append(tempStr)
  1338.             else:
  1339.                 return
  1340.  
  1341.             if tempCount == 12:
  1342.                 tempInt = sdg.askinteger("New Character: Info", "Enter how much currency you have:")
  1343.                 if type(tempInt) == int:
  1344.                     tempInfo.append(tempInt)
  1345.                 else:
  1346.                     return
  1347.             else:
  1348.                 tempCount += 1
  1349.  
  1350.         elif tempCount > 5 and tempCount < 10:
  1351.             tempStr = sdg.askstring("New Character: Info", "Enter your unit of " + infoChoices[tempCount] + ":")
  1352.             if type(tempStr) != str:
  1353.                 return
  1354.  
  1355.             tempInt = sdg.askinteger("New Character: Info", "Enter your " + infoChoices[tempCount] + ":")
  1356.             if type(tempInt) == int:
  1357.                 tempInfo.append(tempInt)
  1358.                 tempInfo.append(tempStr)
  1359.                 tempCount += 2
  1360.             else:
  1361.                 return
  1362.  
  1363.         elif tempCount == 5:
  1364.             tempInt = sdg.askinteger("New Character: Info", "Enter your " + infoChoices[tempCount] + ":")
  1365.             if type(tempInt) == int:
  1366.                 tempInfo.append(tempInt)
  1367.                 tempCount += 1
  1368.             else:
  1369.                 return
  1370.  
  1371.     tempInt = sdg.askinteger("New Character: Health", "Enter your initial maximum health points:")
  1372.     if type(tempInt) != int:
  1373.         return
  1374.  
  1375.     tempHealth.append(tempInt)
  1376.     tempHealth.append(tempInt)
  1377.  
  1378.     tempExp.append(1)
  1379.     tempExp.append(0)
  1380.  
  1381.     if LEVLVar.get():
  1382.         tempExp.append(300)
  1383.     else:
  1384.         tempExp.append(110)
  1385.  
  1386.     tempInt = newCharacterStatRoot(base).result
  1387.     if type(tempInt) != int:
  1388.         return
  1389.  
  1390.     if tempInt == 0:
  1391.         tempStr = pointBuyRoot(base).result
  1392.     elif tempInt == 1:
  1393.         tempStr = diceRollStatsRoot(base).result
  1394.  
  1395.     if type(tempStr) != str:
  1396.         return
  1397.  
  1398.     if tempInt in (0, 1):
  1399.         for s in tempStr.split()[0].split('|'):
  1400.             tempMStats.append(int(s))
  1401.  
  1402.         for s in tempStr.split()[1].split('|'):
  1403.             tempBStats.append(int(s))
  1404.  
  1405.     elif tempInt == 2:
  1406.         tempCount = 0
  1407.  
  1408.         while len(tempMStats) < 6 and len(tempBStats) < 6:
  1409.             tempInt = sdg.askinteger("New Character: Manual Stats", "Enter your " + statChoices[tempCount] + ":")
  1410.             if type(tempInt) != int:
  1411.                 return
  1412.  
  1413.             tempIntB = (tempInt - 10) // 2
  1414.  
  1415.             tempMStats.append(tempInt)
  1416.             tempBStats.append(tempIntB)
  1417.             tempCount += 1
  1418.  
  1419.     tempCount = 0
  1420.  
  1421.     for bit in tempInfo:
  1422.         info[tempCount] = bit
  1423.         tempCount += 1
  1424.  
  1425.     tempCount = 0
  1426.  
  1427.     for bit in tempHealth:
  1428.         health[tempCount] = bit
  1429.         tempCount += 1
  1430.  
  1431.     tempCount = 0
  1432.  
  1433.     for bit in tempExp:
  1434.         experience[tempCount] = bit
  1435.         tempCount += 1
  1436.  
  1437.     tempCount = 0
  1438.  
  1439.     for bit in tempMStats:
  1440.         mainStats[tempCount] = bit
  1441.         tempCount += 1
  1442.  
  1443.     tempCount = 0
  1444.  
  1445.     for bit in tempBStats:
  1446.         bonusStats[tempCount] = bit
  1447.         tempCount += 1
  1448.  
  1449.     while len(skills) > 0:
  1450.         del skills[0]
  1451.  
  1452.     while len(abilities) > 0:
  1453.         del abilities[0]
  1454.  
  1455.     while len(inventory) > 0:
  1456.         del inventory[0]
  1457.  
  1458.     refrSTAT()
  1459.     refrINFO()
  1460.     refrSKIL()
  1461.     refrABIL()
  1462.     refrINVT()
  1463.  
  1464.     mbx.showinfo("New Character: Complete", "Done! Your character's main information and stats are ready. You might want to add some abilities or skills for additional bits of information, such as racial bonuses, character backstory, or starting abilities. Good luck, adventurer!")
  1465. #Saves the current character to a .dat file
  1466. def saveCharacter():
  1467.     fileName = sdg.askstring("Save Character", "[Files are saved to the directory\nwhere this program is run]\nEnter the name of the file to save to:")
  1468.     if type(fileName) != str:
  1469.         return
  1470.  
  1471.     fileName += ".dat"
  1472.  
  1473.     if os.path.isfile(fileName):
  1474.         if not mbx.askyesno("Warning! File Already Exists!", "Do you want to overwrite " + fileName + "?"):
  1475.             return
  1476.  
  1477.     saveFile = open(fileName, "w")
  1478.  
  1479.     dlm = " || "
  1480.     adlm = " |\n"
  1481.     nl = "\n\n"
  1482.  
  1483.     temp = 0
  1484.  
  1485.     for bit in info:
  1486.         saveFile.write(str(bit))
  1487.  
  1488.         if temp == 13:
  1489.             saveFile.write(nl)
  1490.         else:
  1491.             saveFile.write(dlm)
  1492.  
  1493.         temp += 1
  1494.  
  1495.     temp = 0
  1496.  
  1497.     for bit in health:
  1498.         saveFile.write(str(bit))
  1499.  
  1500.         if temp == 1:
  1501.             saveFile.write(nl)
  1502.         else:
  1503.             saveFile.write(dlm)
  1504.  
  1505.         temp += 1
  1506.  
  1507.     temp = 0
  1508.  
  1509.     for bit in experience:
  1510.         saveFile.write(str(bit))
  1511.  
  1512.         if temp == 2:
  1513.             saveFile.write(nl)
  1514.         else:
  1515.             saveFile.write(dlm)
  1516.  
  1517.         temp += 1
  1518.  
  1519.     temp = 0
  1520.  
  1521.     for bit in mainStats:
  1522.         saveFile.write(str(bit))
  1523.  
  1524.         if temp == 5:
  1525.             saveFile.write(nl)
  1526.         else:
  1527.             saveFile.write(dlm)
  1528.  
  1529.         temp += 1
  1530.  
  1531.     temp = 0
  1532.  
  1533.     for bit in bonusStats:
  1534.         saveFile.write(str(bit))
  1535.  
  1536.         if temp == 5:
  1537.             saveFile.write(nl)
  1538.         else:
  1539.             saveFile.write(dlm)
  1540.  
  1541.         temp += 1
  1542.  
  1543.     temp = 1
  1544.  
  1545.     for sk in skills:
  1546.         saveFile.write(str(sk[0]) + dlm + sk[1])
  1547.  
  1548.         if temp != len(skills):
  1549.             saveFile.write(adlm)
  1550.  
  1551.         temp += 1
  1552.  
  1553.     saveFile.write(nl)
  1554.  
  1555.     temp = 1
  1556.  
  1557.     for ab in abilities:
  1558.         saveFile.write(str(ab[0]) + dlm + ab[1] + dlm + ab[2])
  1559.  
  1560.         if temp != len(abilities):
  1561.             saveFile.write(adlm)
  1562.  
  1563.         temp += 1
  1564.  
  1565.     saveFile.write(nl)
  1566.  
  1567.     temp = 1
  1568.  
  1569.     for item in inventory:
  1570.         tempAux = 1
  1571.  
  1572.         for bit in item:
  1573.             saveFile.write(str(bit))
  1574.             if tempAux < 9:
  1575.                 saveFile.write(dlm)
  1576.  
  1577.             tempAux += 1
  1578.  
  1579.         if temp != len(inventory):
  1580.             saveFile.write(adlm)
  1581.  
  1582.         temp += 1
  1583.  
  1584.     saveFile.close()
  1585.  
  1586.     mbx.showinfo("Save Character", "Saved!\nCheck the working directory of this program for the file.")
  1587. #Loads a character from a .dat file
  1588. def loadCharacter():
  1589.     loaded = False
  1590.  
  1591.     while not loaded:
  1592.         try:
  1593.             fileName = sdg.askstring("Load Character", "[Files are loaded from the directory\nwhere this program is run]\nEnter the name of the file to load from:")
  1594.             if type(fileName) != str:
  1595.                 return
  1596.  
  1597.             fileName += ".dat"
  1598.             loadFile = open(fileName, "r")
  1599.             loaded = True
  1600.  
  1601.         except FileNotFoundError:
  1602.             mbx.showerror("Error", "File not found!")
  1603.  
  1604.     if mbx.askyesno("Leveling Standard", "Do you want to use the 5E Experience Point brackets instead of C^2's Experience Formula?"):
  1605.         LEVLVar.set(True)
  1606.     else:
  1607.         LEVLVar.set(False)
  1608.  
  1609.     dlm = " || "
  1610.     adlm = " |\n"
  1611.     nl = "\n\n"
  1612.  
  1613.     contents = loadFile.read().split(nl)
  1614.  
  1615.     tempCountA = 0
  1616.  
  1617.     try:
  1618.         for bit in contents:
  1619.             tempList = []
  1620.  
  1621.             if adlm in bit:
  1622.                 tempList = bit.split(adlm)
  1623.                 tempCountB = 0
  1624.  
  1625.                 for item in tempList:
  1626.                     tempList[tempCountB] = item.split(dlm)
  1627.                     tempCountC = 0
  1628.  
  1629.                     for el in tempList[tempCountB]:
  1630.                         if el == "True":
  1631.                             tempList[tempCountB][tempCountC] = True
  1632.                         elif el == "False":
  1633.                             tempList[tempCountB][tempCountC] = False
  1634.                         else:
  1635.                             try:
  1636.                                 tempList[tempCountB][tempCountC] = int(el)
  1637.                
  1638.                             except ValueError:
  1639.                                 try:
  1640.                                     tempList[tempCountB][tempCountC] = float(el)
  1641.                                 except ValueError:
  1642.                                     pass
  1643.    
  1644.                         tempCountC += 1
  1645.    
  1646.                     tempCountB += 1
  1647.  
  1648.             elif dlm in bit:
  1649.                 tempList = bit.split(dlm)
  1650.                 tempCountB = 0
  1651.  
  1652.                 for el in tempList:
  1653.                     try:
  1654.                         tempList[tempCountB] = int(el)
  1655.                
  1656.                     except ValueError:
  1657.                         try:
  1658.                             tempList[tempCountB] = float(el)
  1659.                         except ValueError:
  1660.                             pass
  1661.        
  1662.                     tempCountB += 1
  1663.    
  1664.             contents[tempCountA] = tempList
  1665.  
  1666.             tempCountA += 1
  1667.  
  1668.     except:
  1669.         mbx.showerror("Error", "Unable to Load File!")
  1670.         return
  1671.  
  1672.     tempCount = 0
  1673.  
  1674.     for bit in contents[0]:
  1675.         info[tempCount] = bit
  1676.         tempCount += 1
  1677.  
  1678.     tempCount = 0
  1679.  
  1680.     for bit in contents[1]:
  1681.         health[tempCount] = bit
  1682.         tempCount += 1
  1683.  
  1684.     tempCount = 0
  1685.  
  1686.     for bit in contents[2]:
  1687.         experience[tempCount] = bit
  1688.         tempCount += 1
  1689.  
  1690.     tempCount = 0
  1691.  
  1692.     for bit in contents[3]:
  1693.         mainStats[tempCount] = bit
  1694.         tempCount += 1
  1695.  
  1696.     tempCount = 0
  1697.  
  1698.     for bit in contents[4]:
  1699.         bonusStats[tempCount] = bit
  1700.         tempCount += 1
  1701.  
  1702.     while len(skills) > 0:
  1703.         del skills[0]
  1704.  
  1705.     if len(contents[5]) > 0:
  1706.         if type(contents[5][0]) is list:
  1707.             for bit in contents[5]:
  1708.                 skills.append(bit)
  1709.         else:
  1710.             skills.append(contents[5])
  1711.  
  1712.     while len(abilities) > 0:
  1713.         del abilities[0]
  1714.  
  1715.     if len(contents[6]) > 0:
  1716.         if type(contents[6][0]) is list:
  1717.             for bit in contents[6]:
  1718.                 abilities.append(bit)
  1719.         else:
  1720.             abilities.append(contents[6])
  1721.  
  1722.     while len(inventory) > 0:
  1723.         del inventory[0]
  1724.  
  1725.     if len(contents[7]) > 0:
  1726.         if type(contents[7][0]) is list:
  1727.             for bit in contents[7]:
  1728.                 inventory.append(bit)
  1729.         else:
  1730.             inventory.append(contents[7])
  1731.  
  1732.     mbx.showinfo("Load Character", "Loaded!\nYour journey continues...")
  1733.     refrSTAT()
  1734.     refrINFO()
  1735.     refrINVT()
  1736.     refrSKIL()
  1737.     refrABIL()
  1738. #Lists for main program data
  1739. mainStats = [10, 10, 10, 10, 10, 10]
  1740. bonusStats = [0, 0, 0, 0, 0, 0]
  1741. savingThrows = [0, 0, 0]
  1742. health = [0, 10]
  1743. experience = [1, 0, 300]
  1744. info = ["name", "class", "deity", "race", "gender", 18, 170, "cm", 70, "kg", "brown", "black", "currency", 0]
  1745. skills = []
  1746. abilities = []
  1747. inventory = []
  1748. brackets5E = [0, 300, 900, 2700, 6500, 14000, 23000, 34000, 48000, 64000, 85000, 100000, 120000, 140000, 165000, 195000, 225000, 265000, 305000, 355000]
  1749. #Creation of base tk window
  1750. base = tk.Tk()
  1751. base.protocol("WM_DELETE_WINDOW", exitOverride)
  1752. #Declaration of fonts
  1753. ftSmall = tkf.Font(family = "Consolas", size = 8)
  1754. ftNormal = tkf.Font(family = "Consolas", size = 10)
  1755. ftNormUnd = tkf.Font(family = "Consolas", size = 10, underline = 1)
  1756. ftLarge = tkf.Font(family = "Consolas", size = 12)
  1757. ftTitle = tkf.Font(family = "Consolas", size = 12, weight = "bold", slant = "italic")
  1758. ftBigTitle = tkf.Font(family = "Consolas", size = 16, weight = "bold", slant = "italic")
  1759. ftName = tkf.Font(family = "Consolas", size = 16, weight = "bold")
  1760. ftSubtitle = tkf.Font(family = "Consolas", size = 12, slant = "italic")
  1761. ftDice = tkf.Font(family = "Consolas", size = 12, weight = "bold")
  1762. #Frame for stats
  1763. STATPnl = tk.Frame(base, bd = 4, relief = "groove")
  1764. STATPnl.grid(row = 1, column = 0, rowspan = 2, padx = 8, sticky = "n")
  1765. #Stats labels and buttons
  1766. STATTitl = tk.Label(STATPnl, text = "Stats", width = 8, bd = 4, relief = "raised", font = ftTitle)
  1767. LEVLLbl = tk.Label(STATPnl, width = 10, relief = "ridge", font = ftLarge)
  1768. LEVLVar = tk.BooleanVar()
  1769. LEVLVar.set(True)
  1770. LEVLChk = tk.Checkbutton(STATPnl, text = "5E XP", command = ChangeXP, variable = LEVLVar, font = ftNormal)
  1771. XPLbl = tk.Label(STATPnl, width = 16, relief = "ridge", font = ftLarge, anchor = "w")
  1772. XPPlusBtn = tk.Button(STATPnl, text = "+", command = XPUp, cursor = "hand2", font = ftNormal)
  1773.  
  1774. HPLbl = tk.Label(STATPnl, width = 12, relief = "ridge", font = ftLarge, anchor = "w")
  1775. HPPLUSBtn = tk.Button(STATPnl, text = "+", command = HPUp, cursor = "hand2", font = ftNormal)
  1776. HPMINSBtn = tk.Button(STATPnl, text = "-", command = HPDown, cursor = "hand2", font = ftNormal)
  1777.  
  1778. STRLbl = tk.Label(STATPnl, width = 10, relief = "ridge", font = ftLarge)
  1779. DEXLbl = tk.Label(STATPnl, width = 10, relief = "ridge", font = ftLarge)
  1780. CONLbl = tk.Label(STATPnl, width = 10, relief = "ridge", font = ftLarge)
  1781. INTLbl = tk.Label(STATPnl, width = 10, relief = "ridge", font = ftLarge)
  1782. WISLbl = tk.Label(STATPnl, width = 10, relief = "ridge", font = ftLarge)
  1783. CHALbl = tk.Label(STATPnl, width = 10, relief = "ridge", font = ftLarge)
  1784.  
  1785. STRBLbl = tk.Label(STATPnl, width = 4, relief = "ridge", font = ftLarge)
  1786. DEXBLbl = tk.Label(STATPnl, width = 4, relief = "ridge", font = ftLarge)
  1787. CONBLbl = tk.Label(STATPnl, width = 4, relief = "ridge", font = ftLarge)
  1788. INTBLbl = tk.Label(STATPnl, width = 4, relief = "ridge", font = ftLarge)
  1789. WISBLbl = tk.Label(STATPnl, width = 4, relief = "ridge", font = ftLarge)
  1790. CHABLbl = tk.Label(STATPnl, width = 4, relief = "ridge", font = ftLarge)
  1791.  
  1792. FORTLbl = tk.Label(STATPnl, width = 16, anchor = "e", relief = "ridge", font = ftNormal)
  1793. REFLLbl = tk.Label(STATPnl, width = 16, anchor = "e", relief = "ridge", font = ftNormal)
  1794. WILLLbl = tk.Label(STATPnl, width = 16, anchor = "e", relief = "ridge", font = ftNormal)
  1795. STATEDITBtn = tk.Button(STATPnl, text = "Edit Stats", width = 10, command = editStat, cursor = "hand2", font = ftSmall)
  1796. #Grid assignments of stats elements
  1797. STATTitl.grid(row = 0, columnspan = 3, pady = 4)
  1798. LEVLLbl.grid(row = 1, column = 0, columnspan = 3, pady = 2)
  1799. XPLbl.grid(row = 2, column = 0, columnspan = 2, padx = 4, pady = 2, sticky = "W")
  1800. XPPlusBtn.grid(row = 2, column = 2)
  1801.  
  1802. HPLbl.grid(row = 3, column = 0, padx = 4, pady = 2, sticky = "W")
  1803. HPPLUSBtn.grid(row = 3, column = 1, padx = 2)
  1804. HPMINSBtn.grid(row = 3, column = 2, padx = 2)
  1805.  
  1806. STRLbl.grid(row = 4, column = 0, padx = 4, pady = 2, sticky = "E")
  1807. DEXLbl.grid(row = 5, column = 0, padx = 4, pady = 2, sticky = "E")
  1808. CONLbl.grid(row = 6, column = 0, padx = 4, pady = 2, sticky = "E")
  1809. INTLbl.grid(row = 7, column = 0, padx = 4, pady = 2, sticky = "E")
  1810. WISLbl.grid(row = 8, column = 0, padx = 4, pady = 2, sticky = "E")
  1811. CHALbl.grid(row = 9, column = 0, padx = 4, pady = 2, sticky = "E")
  1812.  
  1813. STRBLbl.grid(row = 4, column = 1, columnspan = 2, padx = 4, pady = 2)
  1814. DEXBLbl.grid(row = 5, column = 1, columnspan = 2, padx = 4, pady = 2)
  1815. CONBLbl.grid(row = 6, column = 1, columnspan = 2, padx = 4, pady = 2)
  1816. INTBLbl.grid(row = 7, column = 1, columnspan = 2, padx = 4, pady = 2)
  1817. WISBLbl.grid(row = 8, column = 1, columnspan = 2, padx = 4, pady = 2)
  1818. CHABLbl.grid(row = 9, column = 1, columnspan = 2, padx = 4, pady = 2)
  1819.  
  1820. FORTLbl.grid(row = 10, columnspan = 3, padx = 4, pady = 2)
  1821. REFLLbl.grid(row = 11, columnspan = 3, padx = 4, pady = 2)
  1822. WILLLbl.grid(row = 12, columnspan = 3, padx = 4, pady = 2)
  1823. STATEDITBtn.grid(row = 13, column = 0, padx = 4, pady = 2)
  1824. LEVLChk.grid(row = 13, column = 1, columnspan = 2)
  1825.  
  1826. refrSTAT()
  1827. #Frame for information
  1828. INFOPnl = tk.Frame(base, bd = 4, relief = "groove")
  1829. INFOPnl.grid(row = 1, column = 1, sticky = "nw")
  1830. #Information labels and buttons
  1831. INFOTitl = tk.Label(INFOPnl, text ="Information", width = 32, bd = 4, relief = "raised", font = ftTitle)
  1832. INFOTitl.grid(row = 0, columnspan = 6, pady = 4)
  1833.  
  1834. NAMELbl = tk.Label(INFOPnl, width = 36, bd = 4, relief = "ridge", font = ftName)
  1835. CLSSLbl = tk.Label(INFOPnl, width = 24, relief = "ridge", font = ftLarge)
  1836. DETYLbl = tk.Label(INFOPnl, width = 24, relief = "ridge", font = ftLarge)
  1837. RACELbl = tk.Label(INFOPnl, width = 24, relief = "ridge", font = ftLarge)
  1838. GNDRLbl = tk.Label(INFOPnl, width = 24, relief = "ridge", font = ftLarge)
  1839. AYGELbl = tk.Label(INFOPnl, width = 10, relief = "ridge", font = ftLarge)
  1840. HGHTLbl = tk.Label(INFOPnl, width = 10, relief = "ridge", font = ftLarge)
  1841. WGHTLbl = tk.Label(INFOPnl, width = 10, relief = "ridge", font = ftLarge)
  1842. INFOEDITBtn = tk.Button(INFOPnl, text = "Edit Info", width = 12, command = editInfo, cursor = "hand2", font = ftSmall)
  1843. HAIRLbl = tk.Label(INFOPnl, width = 24, relief = "ridge", font = ftLarge)
  1844. EYESLbl = tk.Label(INFOPnl, width = 24, relief = "ridge", font = ftLarge)
  1845. MONYLbl = tk.Label(INFOPnl, width = 32, relief = "ridge", font = ftLarge)
  1846. MONYCHNGBtn = tk.Button(INFOPnl, text = "Credit/Debit", width = 12, command = changeMoney, cursor = "hand2", font = ftSmall)
  1847. #Grid assignments of information elements
  1848. NAMELbl.grid(row = 1, columnspan = 4, padx = 16, pady = 4)
  1849. CLSSLbl.grid(row = 2, column = 0, columnspan = 2, pady = 4)
  1850. RACELbl.grid(row = 2, column = 2, columnspan = 2, padx = 6)
  1851. DETYLbl.grid(row = 3, column = 0, columnspan = 2, pady = 4)
  1852. GNDRLbl.grid(row = 3, column = 2, columnspan = 2, padx = 6)
  1853. HAIRLbl.grid(row = 4, column = 0, columnspan = 2, pady = 4)
  1854. EYESLbl.grid(row = 4, column = 2, columnspan = 2, padx = 6)
  1855. AYGELbl.grid(row = 5, column = 0, pady = 4)
  1856. HGHTLbl.grid(row = 5, column = 1)
  1857. WGHTLbl.grid(row = 5, column = 2)
  1858. INFOEDITBtn.grid(row = 5, column = 3)
  1859. MONYLbl.grid(row = 6, column = 0, columnspan = 3)
  1860. MONYCHNGBtn.grid(row = 6, column = 3, pady = 11)
  1861.  
  1862. refrINFO()
  1863. #Frame for inventory
  1864. INVTPnl = tk.Frame(base, bd = 4, relief = "groove")
  1865. INVTPnl.grid(row = 2, column = 1, rowspan = 2, columnspan = 2, pady = 8, sticky = "w")
  1866. #Inventory elements
  1867. INVTTitl = tk.Label(INVTPnl, text = "Inventory", width = 24, bd = 4, relief = "raised", font = ftTitle)
  1868. INVTNEWWBtn = tk.Button(INVTPnl, text = "Create Item", width = 12, command = newItem, cursor = "hand2", font = ftSmall)
  1869. INVTEDITBtn = tk.Button(INVTPnl, text = "Edit Item", width = 12, command = editItem, cursor = "hand2", font = ftSmall)
  1870. INVTDELTBtn = tk.Button(INVTPnl, text = "Delete Item", width = 12, command = deleteItem, cursor = "hand2", font = ftSmall)
  1871. INVTINSPBtn = tk.Button(INVTPnl, text = "Inspect Item", width = 12, command = inspectItem, cursor = "hand2", font = ftSmall)
  1872. INVTSORTBtn = tk.Button(INVTPnl, text = "Sort Items", width = 12, command = sortItems, cursor = "hand2", font = ftSmall)
  1873. INVTLbl = tk.Label(INVTPnl, text = "Name\t\t\t\t\tCount\tWeight\tDice\tCharges\tEquipped", font = ftNormUnd)
  1874. INVTTxt = tk.Listbox(INVTPnl, height = 11, width = 80, cursor = "cross", font = ftNormal)
  1875. IVNTScr = tk.Scrollbar(INVTPnl, orient = "vertical", command = INVTTxt.yview)
  1876. INVTTxt.config(yscrollcommand = IVNTScr.set)
  1877. INVTENCMLbl = tk.Label(INVTPnl, text = "Encumberance:", width = 24, anchor = "w", relief = "ridge", font = ftNormal)
  1878. #Grid assignments of inventory elements
  1879. INVTTitl.grid(row = 0, column = 0, pady = 4)
  1880. INVTNEWWBtn.grid(row = 0, column = 1)
  1881. INVTEDITBtn.grid(row = 0, column = 2)
  1882. INVTDELTBtn.grid(row = 1, column = 2, columnspan = 2)
  1883. INVTINSPBtn.grid(row = 1, column = 1, columnspan = 2)
  1884. INVTSORTBtn.grid(row = 0, column = 3)
  1885. INVTLbl.grid(row = 2, columnspan = 4, sticky = "w")
  1886. INVTTxt.grid(row = 3, column = 0, columnspan = 4)
  1887. IVNTScr.grid(row = 2, column = 4, rowspan = 2, sticky = "ns")
  1888. INVTENCMLbl.grid(row = 4, columnspan = 5, pady = 2, sticky = "e")
  1889.  
  1890. refrINVT()
  1891. #Frame for dice
  1892. DICEPnl = tk.Frame(base, bd = 4, relief = "groove")
  1893. DICEPnl.grid(row = 3, column = 0, padx = 8, pady = 8, sticky = "w")
  1894. #Dice labels and buttons
  1895. DICETitl = tk.Label(DICEPnl, text = "Dice", width = 8, bd = 4, relief = "raised", font = ftTitle)
  1896. DICELbl = tk.Label(DICEPnl, width = 13, text = "0 | d?", relief = "groove", font = ftDice)
  1897. DICEd2Btn = tk.Button(DICEPnl, width = 4, text = "Coin", command = rollDice(2), cursor = "hand2", font = ftNormal)
  1898. DICEd4Btn = tk.Button(DICEPnl, width = 4, text = "d4", command = rollDice(4), cursor = "hand2", font = ftNormal)
  1899. DICEd6Btn = tk.Button(DICEPnl, width = 4, text = "d6", command = rollDice(6), cursor = "hand2", font = ftNormal)
  1900. DICEd8Btn = tk.Button(DICEPnl, width = 4, text = "d8", command = rollDice(8), cursor = "hand2", font = ftNormal)
  1901. DICEd10Btn = tk.Button(DICEPnl, width = 4, text = "d10", command = rollDice(10), cursor = "hand2", font = ftNormal)
  1902. DICEd12Btn = tk.Button(DICEPnl, width = 4, text = "d12", command = rollDice(12), cursor = "hand2", font = ftNormal)
  1903. DICEd20Btn = tk.Button(DICEPnl, width = 4, text = "d20", command = rollDice(20), cursor = "hand2", font = ftNormal)
  1904. DICEd100Btn = tk.Button(DICEPnl, width = 4, text = "d100", command = rollDice(100), cursor = "hand2", font = ftNormal)
  1905. DICERstBtn = tk.Button(DICEPnl, width = 4, text = "Reset", command = rollDice(0), cursor = "hand2", font = ftNormal)
  1906. #Grid assignments of dice elements
  1907. DICETitl.grid(row = 0, column = 0, columnspan = 3, pady = 4)
  1908. DICELbl.grid(row = 1, column = 0, columnspan = 3, padx = 32, pady = 4)
  1909. DICEd2Btn.grid(row = 2, column = 0, pady = 2)
  1910. DICEd4Btn.grid(row = 2, column = 1)
  1911. DICEd6Btn.grid(row = 2, column = 2)
  1912. DICEd8Btn.grid(row = 3, column = 0, pady = 2)
  1913. DICEd10Btn.grid(row = 3, column = 1)
  1914. DICEd12Btn.grid(row = 3, column = 2)
  1915. DICERstBtn.grid(row = 4, column = 0, pady = 2)
  1916. DICEd20Btn.grid(row = 4, column = 1)
  1917. DICEd100Btn.grid(row = 4, column = 2)
  1918. #Panel for skills
  1919. SKILPnl = tk.Frame(base, bd = 4, relief = "groove")
  1920. SKILPnl.grid(row = 2, column = 3, rowspan = 2, padx = 8, pady = 8, sticky = "ne")
  1921. #Skill elements
  1922. SKILTitl = tk.Label(SKILPnl, text = "Skills", width = 8, bd = 4, relief = "raised", font = ftTitle)
  1923. SKILTxt = tk.Listbox(SKILPnl, height = 9, width = 32, cursor = "cross", font = ftNormal)
  1924. SKILScrV = tk.Scrollbar(SKILPnl, orient = "vertical", command = SKILTxt.yview)
  1925. SKILScrH = tk.Scrollbar(SKILPnl, orient = "horizontal", command = SKILTxt.xview)
  1926. SKILTxt.config(yscrollcommand = SKILScrV.set, xscrollcommand = SKILScrH.set)
  1927. SKILNEWWBtn = tk.Button(SKILPnl, text = "Create Skill", width = 12, command = newSkill, cursor = "hand2", font = ftSmall)
  1928. SKILEDITBtn = tk.Button(SKILPnl, text = "Edit Skill", width = 12, command = editSkill, cursor = "hand2", font = ftSmall)
  1929. SKILDELTBtn = tk.Button(SKILPnl, text = "Delete Skill", width = 12, command = deleteSkill, cursor = "hand2", font = ftSmall)
  1930. SKILSORTBtn = tk.Button(SKILPnl, text = "Sort Skills", width = 12, command = sortSkills, cursor = "hand2", font = ftSmall)
  1931. SKILSCORVar = tk.BooleanVar()
  1932. SKILSCORVar.set(False)
  1933. SKILSCORSwt = tk.Checkbutton(SKILPnl, text = "Apply +/- Skills To Stats", command = changeScores, variable = SKILSCORVar, font = ftSmall)
  1934. SKILSCORBtn = tk.Button(SKILPnl, text = "?", command = showPrefixInfo, cursor = "hand2", font = ftSmall)
  1935. #Grid assignments of skill elements
  1936. SKILTitl.grid(row = 0, columnspan = 3, pady = 4)
  1937. SKILTxt.grid(row = 4, column = 0, columnspan = 2)
  1938. SKILScrV.grid(row = 4, column = 2, rowspan = 2, sticky = "ns")
  1939. SKILScrH.grid(row = 5, column = 0, columnspan = 2, pady = 2, sticky = "we")
  1940. SKILNEWWBtn.grid(row = 1, column = 0)
  1941. SKILEDITBtn.grid(row = 1, column = 1)
  1942. SKILDELTBtn.grid(row = 2, column = 0)
  1943. SKILSORTBtn.grid(row = 2, column = 1)
  1944. SKILSCORSwt.grid(row = 3, column = 0, columnspan = 2, padx = 8, sticky = "w")
  1945. SKILSCORBtn.grid(row = 3, column = 1, padx = 8, sticky = "e")
  1946.  
  1947. refrSKIL()
  1948. #Frame for abilities
  1949. ABILPnl = tk.Frame(base, bd = 4, relief = "groove")
  1950. ABILPnl.grid(row = 1, column = 2, columnspan = 2, padx = 8, sticky = "n")
  1951. #Ability elements
  1952. ABILTitl = tk.Label(ABILPnl, text = "Abilities", width = 16, bd = 4, relief = "raised", font = ftTitle)
  1953. ABILTxt = tk.Listbox(ABILPnl, height = 10, width = 48, cursor = "cross", font = ftNormal)
  1954. ABILScrV = tk.Scrollbar(ABILPnl, orient = "vertical", command = ABILTxt.yview)
  1955. ABILScrH = tk.Scrollbar(ABILPnl, orient = "horizontal", command = ABILTxt.xview)
  1956. ABILTxt.config(yscrollcommand = ABILScrV.set, xscrollcommand = ABILScrH.set)
  1957. ABILNEWWBtn = tk.Button(ABILPnl, text = "Create Ability", width = 16, command = newAbility, cursor = "hand2", font = ftSmall)
  1958. ABILEDITBtn = tk.Button(ABILPnl, text = "Edit Ability", width = 16, command = editAbility, cursor = "hand2", font = ftSmall)
  1959. ABILDELTBtn = tk.Button(ABILPnl, text = "Delete Ability", width = 16, command = deleteAbility, cursor = "hand2", font = ftSmall)
  1960. ABILINSPBtn = tk.Button(ABILPnl, text = "Inspect Ability", width = 16, command = inspectAbility, cursor = "hand2", font = ftSmall)
  1961. ABILSORTBtn = tk.Button(ABILPnl, text = "Sort Abilities", width = 16, command = sortAbilities, cursor = "hand2", font = ftSmall)
  1962. #Grid assignments of ability elements
  1963. ABILTitl.grid(row = 0, columnspan = 4, pady = 4)
  1964. ABILTxt.grid(row = 3, column = 0, columnspan = 3)
  1965. ABILScrV.grid(row = 3, column = 3, rowspan = 2, sticky = "ns")
  1966. ABILScrH.grid(row = 4, columnspan = 3, sticky = "we")
  1967. ABILNEWWBtn.grid(row = 1, column = 0, sticky = "e")
  1968. ABILEDITBtn.grid(row = 1, column = 1, sticky = "e")
  1969. ABILSORTBtn.grid(row = 1, column = 2, sticky = "e")
  1970. ABILINSPBtn.grid(row = 2, column = 0, columnspan = 2)
  1971. ABILDELTBtn.grid(row = 2, column = 1, columnspan = 2)
  1972. #Frame for menu
  1973. MENUPnl = tk.Frame(base, bd = 4, relief = "groove")
  1974. MENUPnl.grid(row = 0, columnspan = 4, pady = 8)
  1975. #Menu labels and buttons
  1976. MENUTitl = tk.Label(MENUPnl, text = "C²'s D&D Character Sheet", width = 64, bd = 4, relief = "raised", font = ftBigTitle)
  1977. MENUNEWWBtn = tk.Button(MENUPnl, text = "New Character", width = 16, command = newCharacter, cursor = "hand2", font = ftLarge)
  1978. MENUSAVEBtn = tk.Button(MENUPnl, text = "Save Character", width = 16, command = saveCharacter, cursor = "hand2", font = ftLarge)
  1979. MENULOADBtn = tk.Button(MENUPnl, text = "Load Character", width = 16, command = loadCharacter, cursor = "hand2", font = ftLarge)
  1980. MENUVERS = tk.Label(MENUPnl, text = "Version 1.2.4", width = 16, bd = 4, relief = "ridge", font = ftSubtitle)
  1981. #Grid assignments of menu elements
  1982. MENUTitl.grid(row = 0, columnspan = 4, padx = 16, pady = 4)
  1983. MENUNEWWBtn.grid(row = 1, column = 0, pady = 4)
  1984. MENUSAVEBtn.grid(row = 1, column = 1)
  1985. MENULOADBtn.grid(row = 1, column = 2)
  1986. MENUVERS.grid(row = 1, column = 3)
  1987. #Initialization of main window and final settings
  1988. base.title("DnD Character Sheet")
  1989. base.resizable(False, False)
  1990. base.mainloop()
RAW Paste Data