Fin's AI Tweaks config exe

mail@pastecode.io avatar
unknown
plain_text
2 years ago
150 kB
10
Indexable
Never
#pyinstaller NAME OF FILE --onefile

import json
import tkinter as tk
from tkinter import ttk
from tkinter import *
import os

#Stolen valor https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter
#I won't pretend to know what the heck this is doing
class CreateToolTip(object):
    """
    create a tooltip for a given widget
    """
    def __init__(self, widget, text='widget info'):
        self.waittime = 150     #miliseconds
        self.wraplength = 280   #pixels
        self.widget = widget
        self.text = text
        self.widget.bind("<Enter>", self.enter)
        self.widget.bind("<Leave>", self.leave)
        self.widget.bind("<ButtonPress>", self.leave)
        self.id = None
        self.tw = None

    def enter(self, event=None):
        self.schedule()

    def leave(self, event=None):
        self.unschedule()
        self.hidetip()

    def schedule(self):
        self.unschedule()
        self.id = self.widget.after(self.waittime, self.showtip)

    def unschedule(self):
        id = self.id
        self.id = None
        if id:
            self.widget.after_cancel(id)

    def showtip(self, event=None):
        x = y = 0
        x, y, cx, cy = self.widget.bbox("insert")
        x += self.widget.winfo_rootx() + 25
        y += self.widget.winfo_rooty() + 20
        # creates a toplevel window
        self.tw = tk.Toplevel(self.widget)
        # Leaves only the label and removes the app window
        self.tw.wm_overrideredirect(True)
        self.tw.wm_geometry("+%d+%d" % (x, y))
        label = tk.Label(self.tw, text=self.text, justify='left',
                       background="#ffffff", relief='solid', borderwidth=1,
                       wraplength = self.wraplength)
        label.pack(ipadx=1)

    def hidetip(self):
        tw = self.tw
        self.tw= None
        if tw:
            tw.destroy()


def superHacky(input):
    # I know this is wrong. Deal with it.
    try:
        obj = input.get()
    except:
        try:
            obj = input.get("1.0", END)
        except:
            obj = input
    if (type(obj) == type("") and obj.lower() in ["true", "false"]) or type(obj) == type(True):
        if obj == True:
            obj = True
        elif obj == False:
            obj = False
        elif obj.lower() == "true":
            obj = True
        else:
            obj = False
    else:
        try:
            obj = float(obj)
            if obj % 1 == 0:
                obj = int(obj)
        except:
            obj = obj
    if type(obj) == type("") and any(x in obj for x in ["[", "]", " "]):
        obj = obj.replace("[", "")
        obj = obj.replace("]", "")
        obj = obj.replace("\"", "")
        obj = obj.replace("\'", "")
        obj = obj.replace(" ", "")
        obj = obj.replace("\n", "")
        output = list(obj.split(","))
        for i in range(len(output)):
            try:
                output[i] = float(output[i])
                if output[i] % 1 == 0:
                    output[i] = int(output[i])
            except:
                output[i] = output[i]
    else:
        output = obj
    return output

def parseArray(string):
    if str(string) == "True" or str(string) == "False":
        return str(string)
    if type(string) != "str":
        return str(string)
    if "[" in string:
        string = ""
        for ele in config["overallDifficultyMultipliers"][name]:
            string += "\"" + ele + "\", "
        string = string[:-2:]
    return string

# I should have done this five hundred lines ago
def makeRadioButton(frame, text, row, variable, path, tooltip):
    a = ttk.Label(frame, text=text)
    a.grid(column=0, row=row, padx=30, pady=5, sticky=W)
    CreateToolTip(a, tooltip)
    ttk.Radiobutton(frame,
                    text="", variable=variable, value=False
                    ).grid(column=1, row=row, padx=30, pady=5)
    ttk.Radiobutton(frame,
                    text="", variable=variable, value=True
                    ).grid(column=2, row=row, padx=30, pady=5)

    if path is False:
        variable.set(False)
    else:
        variable.set(True)

def makeRadioLabel(frame, text, row, variable, path, tooltip):
    a = ttk.Label(frame, text=text)
    a.grid(column=0, row=row, padx=30, pady=5, sticky=W)
    CreateToolTip(a, tooltip)
    ttk.Radiobutton(frame,
                    text="False", variable=variable, value=False
                    ).grid(column=1, row=row, padx=5, pady=5, sticky = W)
    ttk.Radiobutton(frame,
                    text="True", variable=variable, value=True
                    ).grid(column=2, row=row, padx=5, pady=5, sticky = W)

    if path is False:
        variable.set(False)
    else:
        variable.set(True)

def makeRadioLabelCUSTOM(frame, text, font, row, variable, path, tooltip):
    newFrame = ttk.Frame(frame)
    newFrame.grid(column=0, row=row)
    a = ttk.Label(newFrame, text=text, font=font)
    a.grid(column=0, row=0, padx=30, pady=5, sticky=W)
    CreateToolTip(a, tooltip)
    ttk.Radiobutton(newFrame,
                    text="False", variable=variable, value=False
                    ).grid(column=1, row=0, padx=5, pady=5, sticky = W)
    ttk.Radiobutton(newFrame,
                    text="True", variable=variable, value=True
                    ).grid(column=2, row=0, padx=5, pady=5, sticky = W)

    if path is False:
        variable.set(False)
    else:
        variable.set(True)

def makeLabelEntry(frame, text, row, span, path, tooltip):
    a = ttk.Label(frame, text=text)
    a.grid(column=0, row=row, padx=30, pady=5, sticky = W)
    CreateToolTip(a, tooltip)
    variable = Entry(frame, width=5)
    variable.grid(column=1, columnspan=span, row=row, padx=30, pady=5)
    if path is False:
        path = "False"
    if path is True:
        path = "True"
    variable.insert(END, path)

    return variable

def makeTextEntry(frame, text, row, span, width, height, path, tooltip):
    a = ttk.Label(frame, text=text)
    a.grid(column=0, row=row, padx=30, pady=5, sticky=W)
    CreateToolTip(a, tooltip)
    variable = Text(frame, width=width, height=height, wrap="word")
    variable.grid(column=1, columnspan=span, row=row, padx=30, pady=5)
    output = "['"
    for i in path:
        output += i + "', '"
    output = output[:-3] + "]"
    variable.insert(END, output)

    return variable

def makeLabelEntryCUSTOM(frame, sticky1, sticky2, pad1, pad2, text, row, span, path, width, tooltip):
    a = ttk.Label(frame, text=text)
    a.grid(column=0, row=row, padx=pad1, pady=5, sticky=sticky1)
    CreateToolTip(a, tooltip)
    variable = Entry(frame, width=width)
    variable.grid(column=1, columnspan=span, row=row, padx=pad2, pady=5, sticky=sticky2)
    if path is False:
        path = "False"
    if path is True:
        path = "True"
    variable.insert(END, path)

    return variable

def makeLabelSpinboxCUSTOM(frame, sticky, text, row, span, path, width, tooltip):
    a = ttk.Label(frame, text=text)
    a.grid(column=0, row=row, padx=30, pady=5, sticky=sticky)
    CreateToolTip(a, tooltip)
    b = StringVar()
    variable = Spinbox(frame, width=width, from_=0, to=1000, textvariable = b)
    variable.grid(column=1, columnspan=span, row=row, padx=30, pady=5)
    if path is False:
        path = "False"
    if path is True:
        path = "True"
    variable.insert(END, path)
    b.set(path)

    return variable


def formatJSON(obj):
    if type(obj) == type({}):
        for i in obj:
            if any(x in i for x in ["note", "Note", "###"]):
                continue
            a = 1
            obj[i] = formatJSON(obj[i])
            #a = formatJSON(obj[i], result)
            #obj[i] = a
    else:
        if type(obj) == type("") and any(x in obj for x in ["[", "]", " "]):
            obj = obj.replace("[", "")
            obj = obj.replace("]", "")
            obj = obj.replace("\"", "")
            obj = obj.replace("\'", "")
            obj = obj.replace(" ", "")
            obj = obj.replace("\n", "")
            output = list(obj.split(","))
            return output

def reorderConfig(obj, keys):
    with open(r"C:\Z Drive\EFT 12893\user\mods\zFin-AITweaks\config\config.json") as f:
        original = json.load(f)

def restoreDefault():
    try:
        with open('config\config.json', 'w') as outfile:
            json.dump(defaultConfig, outfile, indent=4)
        os.execl(sys.executable, sys.executable, *sys.argv)
    except:
        print ("No default config file available.")

def backupConfig():
    saveValues('config\Backup config.json')

def restoreBackup():
    with open(r'config\Backup config.json') as f:
        a = json.load(f)
    with open('config\config.json', 'w') as outfile:
        json.dump(a, outfile, indent=4)
    os.execl(sys.executable, sys.executable, *sys.argv)

def saveValues(saveLoc):
    config["disableAllSpawnChanges"] = superHacky(disabSpawn)
    config["disableAllGearChanges"] = superHacky(disabGear)
    config["disableAllAIChanges"] = superHacky(disabAI)
    config["silenceWeaponGenErrors"] = superHacky(disabErrors)

    config["aiChanges"]["changeBots"]["lowLevelAIs"] = superHacky(windowLowLevelAIs)
    config["aiChanges"]["changeBots"]["midLevelAIs"] = superHacky(windowMidLevelAIs)
    config["aiChanges"]["changeBots"]["highLevelAIs"] = superHacky(windowHighLevelAIs)

    config["AIbehaviourChanges"]["enabled"] = superHacky(enableBehaviourChanges)
    config["AIbehaviourChanges"]["highLevelAIs"]["default"] = superHacky(pmcDefault)
    config["AIbehaviourChanges"]["highLevelAIs"]["balanced"] = superHacky(pmcBalanced)
    config["AIbehaviourChanges"]["highLevelAIs"]["aggressive"] = superHacky(pmcAggressive)
    config["AIbehaviourChanges"]["highLevelAIs"]["ranged"] = superHacky(pmcSniper)
    config["AIbehaviourChanges"]["midLevelAIs"]["default"] = superHacky(raiderDefault)
    config["AIbehaviourChanges"]["midLevelAIs"]["balanced"] = superHacky(raiderBalanced)
    config["AIbehaviourChanges"]["midLevelAIs"]["aggressive"] = superHacky(raiderAggressive)
    config["AIbehaviourChanges"]["midLevelAIs"]["ranged"] = superHacky(raiderSniper)
    config["AIbehaviourChanges"]["lowLevelAIs"]["default"] = superHacky(scavDefault)
    config["AIbehaviourChanges"]["lowLevelAIs"]["balanced"] = superHacky(scavBalanced)
    config["AIbehaviourChanges"]["lowLevelAIs"]["aggressive"] = superHacky(scavAggressive)
    config["AIbehaviourChanges"]["lowLevelAIs"]["ranged"] = superHacky(scavSniper)

    config["aiChanges"]["overallAIDifficultyMod"] = superHacky(OLD)
    config["aiChanges"]["lowLevelAIDifficultyMod_Neg3_3"] = superHacky(LLD)
    config["aiChanges"]["midLevelAIDifficultyMod_Neg3_3"] = superHacky(MLD)
    config["aiChanges"]["highLevelAIDifficultyMod_Neg3_3"] = superHacky(HLD)

    config["aiChanges"]["IfYouDisableTheseThePMCSWontCountForQuestKills"]["scavsFightBEARBots"] = superHacky(fightBEAR)
    config["aiChanges"]["IfYouDisableTheseThePMCSWontCountForQuestKills"]["scavsFightUSECBots"] = superHacky(fightUSEC)

    config["overallDifficultyMultipliers"]["aiAimSpeedMult"] = superHacky(aiAimSpeedMult)
    config["overallDifficultyMultipliers"]["aiShotSpreadMult"] = superHacky(aiShotSpreadMult)
    config["overallDifficultyMultipliers"]["aiVisionSpeedMult"] = superHacky(aiVisionSpeedMult)
    config["overallDifficultyMultipliers"]["sniperBotAccuracyMult"] = superHacky(sniperBotAccuracyMult)
    config["overallDifficultyMultipliers"]["visibleDistanceMult"] = superHacky(visibleDistanceMult)
    config["overallDifficultyMultipliers"]["semiAutoFireRateMult"] = superHacky(semiAutoFireRateMult)
    config["overallDifficultyMultipliers"]["aiRecoilMult"] = superHacky(aiRecoilMult)
    config["overallDifficultyMultipliers"]["aiHearingMult"] = superHacky(aiHearingMult)
    config["overallDifficultyMultipliers"]["visibleAngleMult"] = superHacky(visibleAngleMult)
    config["overallDifficultyMultipliers"]["visibleAngleMax"] = superHacky(visibleAngleMax)
    config["overallDifficultyMultipliers"]["grenadePrecisionMult"] = superHacky(grenadePrecisionMult)
    config["overallDifficultyMultipliers"]["grenadeThrowRangeMax"] = superHacky(grenadeThrowRangeMax)
    config["overallDifficultyMultipliers"]["grenadeThrowRangeMult"] = superHacky(grenadeThrowRangeMult)
    config["overallDifficultyMultipliers"]["allowAimAtHead"] = superHacky(allowAimAtHead)
    config["overallDifficultyMultipliers"]["allowGrenades"] = superHacky(allowGrenades)
    config["overallDifficultyMultipliers"]["infiniteAIAmmo"] = superHacky(infiniteAIAmmo)
    config["overallDifficultyMultipliers"]["enableAutomaticDifficulty"] = superHacky(enableAutomaticDifficulty)
    config["overallDifficultyMultipliers"]["infiniteAIHealing_PMCs_All_false"] = superHacky(infiniteAIHealing_PMCs_All_false)
    config["overallDifficultyMultipliers"]["cultistsVisibleOnThermals"] = superHacky(cultistsVisibleOnThermals)
    config["overallDifficultyMultipliers"]["changeHeadHPValues"] = superHacky(changeHeadHPValues)
    config["overallDifficultyMultipliers"]["setTheseBotsToDefaultPlayerHPBeforeMultsAreUsed"] = superHacky(setTheseBotsToDefaultPlayerHPBeforeMultsAreUsed)
    config["overallDifficultyMultipliers"]["PMCHealthMult"] = superHacky(PMCHealthMult)
    config["overallDifficultyMultipliers"]["scavHealthMult"] = superHacky(scavHealthMult)
    config["overallDifficultyMultipliers"]["raiderHealthMult"] = superHacky(raiderHealthMult)
    config["overallDifficultyMultipliers"]["bossHealthMult"] = superHacky(bossHealthMult)
    config["overallDifficultyMultipliers"]["cultistHealthMult"] = superHacky(cultistHealthMult)

    #This will probably need to be improved at some point
    for name in gearOptionNames:
        index = superHacky(gearOptionNames.index(name))
        config["AIgearChanges"]["scavs"][name] = superHacky(scavGearOpt[index])
        config["AIgearChanges"]["raiders"][name] = superHacky(raidGearOpt[index])
        config["AIgearChanges"]["PMCs"][name] = superHacky(pmcGearOpt[index])

    config["AIgearChanges"]["scavBots"] = superHacky(scavGearCat)
    config["AIgearChanges"]["raiderBots"] = superHacky(raiderGearCat)
    config["AIgearChanges"]["pmcBots"] = superHacky(pmcGearCat)

    config["AIgearChanges"]["allAI"]["removeAIVogGrenades"] = superHacky(noVOGs)
    config["AIgearChanges"]["allAI"]["noHatsOnlyHelmets"] = superHacky(noHats)
    config["AIgearChanges"]["allAI"]["removeVanillaBotWeapons"] = superHacky(removeVanillaWeps)
    config["AIgearChanges"]["allAI"]["removeVanillaBotArmor"] = superHacky(removeVanillaArmor)
    config["AIgearChanges"]["allAI"]["botsWillNotSpawnWithTheseThings"] = superHacky(blackListGear)

    config["spawnChanges"]["extraWaves"]["scavWaves"] = superHacky(scavWaves)
    config["spawnChanges"]["extraWaves"]["scavWaveSizeMin"] = superHacky(scavWavesMin)
    config["spawnChanges"]["extraWaves"]["scavWaveSizeMax"] = superHacky(scavWavesMax)

    config["spawnChanges"]["extraWaves"]["PMCWaves"] = superHacky(pmcWaves)
    config["spawnChanges"]["extraWaves"]["PMCWaveSizeMin"] = superHacky(pmcWavesMin)
    config["spawnChanges"]["extraWaves"]["PMCWaveSizeMax"] = superHacky(pmcWavesMax)

    config["spawnChanges"]["extraWaves"]["raiderWaves"] = superHacky(raiderWaves)
    config["spawnChanges"]["extraWaves"]["raiderWaveSizeMin"] = superHacky(raiderWavesMin)
    config["spawnChanges"]["extraWaves"]["raiderWaveSizeMax"] = superHacky(raiderWavesMax)

    config["spawnChanges"]["extraWaves"]["gluharRaiderWaves"] = superHacky(gluharWaves)
    config["spawnChanges"]["extraWaves"]["gluharRaiderWaveSizeMin"] = superHacky(gluharWavesMin)
    config["spawnChanges"]["extraWaves"]["gluharRaiderWaveSizeMax"] = superHacky(gluharWavesMax)

    config["spawnChanges"]["extraWaves"]["OnlyWorksProperlyWhenTaggedAndCursedIsEnabled"][
        "waveSpawnChancePCT0_100"] = superHacky(cultistWavesChance)
    config["spawnChanges"]["extraWaves"]["OnlyWorksProperlyWhenTaggedAndCursedIsEnabled"]["cultistWaves"] = superHacky(
        cultistWaves)
    config["spawnChanges"]["extraWaves"]["OnlyWorksProperlyWhenTaggedAndCursedIsEnabled"][
        "cultistWaveSizeMin"] = superHacky(cultistWavesMin)
    config["spawnChanges"]["extraWaves"]["OnlyWorksProperlyWhenTaggedAndCursedIsEnabled"][
        "cultistWaveSizeMax"] = superHacky(cultistWavesMax)

    config["spawnChanges"]["controlOptions"]["doNotChangeTheseSPECIFICMaps"] = superHacky(doNotChangeMaps)
    #config["spawnChanges"]["controlOptions"]["spawnPMCsAsBosses"] = superHacky(pmcsAsBosses)
    config["spawnChanges"]["controlOptions"]["breakUpSpawnWaves"] = superHacky(breakUpWaves)
    config["spawnChanges"]["controlOptions"]["noScavsInLabs"] = superHacky(labrats)
    config["spawnChanges"]["controlOptions"]["removeVanillaSpawns"] = superHacky(clearVanillaSpawns)
    config["spawnChanges"]["controlOptions"]["removeVanillaPMCSpawns"] = superHacky(clearVanillaPMCSpawns)
    config["spawnChanges"]["controlOptions"]["removeSniperBots"] = superHacky(clearSniperScavs)
    config["spawnChanges"]["controlOptions"]["autoAddPMCsBasedOnMapSize"] = superHacky(autoPMCs)
    config["spawnChanges"]["controlOptions"]["allPMCsareBEARs"] = superHacky(allBears)
    config["spawnChanges"]["controlOptions"]["allPMCsareUSECs"] = superHacky(allUsecs)
    config["spawnChanges"]["controlOptions"]["scavReplaceWith___AffectsOnlyVanillaWaves"] = superHacky(
        replaceVanillaOnly)
    config["spawnChanges"]["controlOptions"]["scavReplaceWithRaidersPCT0_100"] = superHacky(scavReplace)
    config["spawnChanges"]["controlOptions"]["scavReplaceWithPMCsPCT0_100"] = superHacky(scavReplacePMC)
    config["spawnChanges"]["controlOptions"]["extraScavsPerVanillaWave"] = superHacky(extraVanillaScavs)

    mapMults = []
    mapMults.append(superHacky(interchangeMult))
    mapMults.append(superHacky(customsMult))
    mapMults.append(superHacky(reserveMult))
    mapMults.append(superHacky(woodsMult))
    mapMults.append(superHacky(shoreMult))
    mapMults.append(superHacky(labsMult))
    mapMults.append(superHacky(factoryMult))

    config["spawnChanges"]["controlOptions"]["mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact"] = mapMults
    config["spawnChanges"]["controlOptions"]["evenlySpreadAllSpawnLocations"] = superHacky(evenSpreadSpawns)

    config["spawnChanges"]["spawnTiming"]["evenlySpaceAllSpawnTimes"] = superHacky(evenSpreadSpawnTimes)
    config["spawnChanges"]["spawnTiming"]["extraWaveSpawnSpeedFactor"] = superHacky(speedFactor)
    config["spawnChanges"]["spawnTiming"]["extraWaveSpawnSpeedFactorMaxTime"] = superHacky(speedFactorCap)
    config["spawnChanges"]["spawnTiming"]["spawnExtraWavesImmediately"] = superHacky(spawnImmediately)
    config["spawnChanges"]["spawnTiming"]["spawn_ALL_WavesImmediately"] = superHacky(spawnImmediately2)

    config["allowBotsToTalk"]["scavs"] = superHacky(scavTalk)
    config["allowBotsToTalk"]["raiders"] = superHacky(raiderTalk)
    config["allowBotsToTalk"]["PMCs"] = superHacky(pmcTalk)

    config["AIgearChanges"]["CheckREADMEforInfo"]["optimizedLoading"] = superHacky(optimizeLoad)
    config["AIgearChanges"]["CheckREADMEforInfo"]["saveGearToFile"] = superHacky(saveFile)
    config["AIgearChanges"]["CheckREADMEforInfo"]["loadGearFromFile"] = superHacky(loadFile)

    config["AIgearChanges"]["allAI"]["factionIdentifiers"] = superHacky(factionIdentifiers)

    config["AIgearChanges"]["miscChanges"]["requiresOtherMods"]["AIO"]["AllowAIToUseBothArmoredVestsAndArmoredRigs"] = superHacky(rigsAndVests)

    #config["spawnChanges"]["maxBotsPerZone"] = superHacky(maxBotsPerZone)

    config["sillyChanges"]["allBulletsAreTracers"] = superHacky(tracerBullets)
    config["sillyChanges"]["tracerGrenades"] = superHacky(tracerGrenades)
    config["sillyChanges"]["pumpkinCultists"] = superHacky(pumpkinCultists)
    config["sillyChanges"]["COD_mode"] = superHacky(codMode)
    config["sillyChanges"]["COD_modeMaxHPPerLimb"] = superHacky(codModeHP)
    config["sillyChanges"]["gravestone"] = superHacky(gravestone)

    config["miscChanges"]["enableProgressiveGear"] = superHacky(enableProgressiveGear)
    config["miscChanges"]["startServerAtSpecificTime"] = superHacky(overrideServerTime)
    config["miscChanges"]["startTime"] = superHacky(startTime)
    config["miscChanges"]["timeAcceleration"] = superHacky(timeSpeed)

    config["debug"]["showSpawns"] = superHacky(showSpawns)
    config["debug"]["showBosses"] = superHacky(showBosses)
    config["debug"]["showBossPMCs"] = superHacky(showBossPMCs)
    config["debug"]["showGuns"] = superHacky(showGuns)
    config["debug"]["showArmor"] = superHacky(showArmor)
    config["debug"]["showAmmo"] = superHacky(showAmmo)
    config["debug"]["saveDebugFiles"] = superHacky(saveDebugFiles)
    config["debug"]["reportWavesBeforeRaid"] = superHacky(reportWavesBeforeRaid)

    print(json.dumps(config, indent=4))

    with open(saveLoc, 'w') as outfile:
        json.dump(config, outfile, indent=4)

dir_path = os.path.dirname(os.path.realpath(__file__))

with open(r'config\config.json') as f:
    config = json.load(f)

#The default config will need to be updated it any new things are added
#defaultConfig = {'Explanation': {"############## Set the values to true or false, or to a number if it's that type of value. It should be fairly self explanatory, but if there's anything weird I'll make a readme file that explains more. It should be in the mod's main folder.######################": '///', "############## For real, check the README file if anything here confuses you in the slighest. Heck, even if it doesn't confuse you, it can't hurt to glance over it anyways.######################": '///', 'Notes on Bot Names': 'Scav = assault; Sniper = marksman; Raider = pmcbot; Cultist leader = sectantpriest; Cultist = sectantwarrior; PMC = assaultgroup; Reshala = bossbully; Gluhar = bossgluhar; Killa = bosskilla; Shturman = bosskojaniy; Sanitar = bosssanitar; Boss minions = followerbully, followergluharassault, followergluharscout, followergluharsecurity, followergluharsnipe, followerkojaniy, followersanitar', 'Notes on Map Names': 'Customs = bigmap, Woods = woods, Reserve = rezervbase, Shoreline = shoreline, Factory (day) = factory4_day, Factory (night) = factory4_night, Interchange = interchange, Labs = laboratory'}, 'disableAllSpawnChanges': False, 'disableAllGearChanges': False, 'disableAllAIChanges': False, 'aiChanges': {'changeBots': {'lowLevelAIs': ['assault'], 'midLevelAIs': ['pmcbot', 'followergluharassault', 'followergluharscout', 'followergluharsecurity', 'followergluharsnipe', 'followerkojaniy', 'followersanitar', 'followerbully'], 'highLevelAIs': ['assaultgroup', 'cursedassault']}, 'Note': 'These are the MAIN values for adjusting difficulty. If you want an easier or harder time overall, increase or decrease these. For details on these or any other entries, please check the readme.', 'lowLevelAIDifficultyMod_Neg3_3': 0.85, 'midLevelAIDifficultyMod_Neg3_3': 0.85, 'highLevelAIDifficultyMod_Neg3_3': 1.1, 'IfYouDisableTheseThePMCSWontCountForQuestKills': {'scavsFightBEARBots': True, 'scavsFightUSECBots': True}}, 'overallDifficultyMultipliers': {'aiAimSpeedMult': 1, 'aiShotSpreadMult': 1, 'aiVisionSpeedMult': 1, 'sniperBotAccuracyMult': 1, 'visibleDistanceMult': 1, 'semiAutoFireRateMult': 1, 'aiRecoilMult': 1.1, 'aiHearingMult': 0.3, 'visibleAngleMult': 1, 'visibleAngleMax': 320, 'grenadePrecisionMult': 1, 'grenadeThrowRangeMax': 20, 'grenadeThrowRangeMult': 1, 'allowAimAtHead': 1, 'allowGrenades': True, 'infiniteAIAmmo': True, 'infiniteAIHealing_PMCs_All_false': 'PMCs', 'cultistsVisibleOnThermals': False, 'changeHeadHPValues': False, 'setTheseBotsToDefaultPlayerHPBeforeMultsAreUsed': ['botnameshere', 'seperatethemwithcommas'], 'PMCHealthMult': 2, 'scavHealthMult': 1, 'raiderHealthMult': 1, 'bossHealthMult': 1, 'cultistHealthMult': 1}, 'allowBotsToTalk': {'scavs': True, 'raiders': False, 'PMCs': False, 'bossMinions': True}, 'AIgearChanges': {'CheckREADMEforInfo': {'optimizedLoading': True, 'saveGearToFile': False, 'loadGearFromFile': False}, 'scavBots': ['assault'], 'raiderBots': ['pmcbot', 'followergluharassault', 'followergluharscout', 'followergluharsecurity', 'followergluharsnipe'], 'pmcBots': ['bear', 'usec', 'assaultgroup', 'cursedassault'], 'scavs': {'changeGear': True, 'moreModVariety': True, 'modQuality0_10': 0, 'modFrequency0_2': 0.25, 'magQuality0_10': 3, 'removeHatsPCT0_100': 25, 'armorRarityMinMax0_50': [0, 50], 'primaryWeaponRarityMinMax0_50': [0, 30], 'sidearmRarityMinMax0_50': [0, 10], 'armorLevelMin_Max': [1, 4], 'armorLevelWeights1_6': [1, 3, 4, 2, 1, 1], 'ammoRemovePCTGood_Bad': [35, 25], 'healingItemsMin_Max': [0, 4], 'magazinesInVestMin_Max': [2, 3], 'backpackSizeMin_Max': [-1, 16], 'maxTacticalDevices': 1, 'maxPrimaryOptics': 1, 'removeWeaponTypesSemiAutoPumpBolt': [''], 'specificWeapons': [''], 'useWeaponsFromOtherMods': False}, 'raiders': {'changeGear': True, 'moreModVariety': True, 'modQuality0_10': 7, 'modFrequency0_2': 0.6, 'magQuality0_10': 7, 'removeHatsPCT0_100': 0, 'armorRarityMinMax0_50': [0, 50], 'primaryWeaponRarityMinMax0_50': [10, 40], 'sidearmRarityMinMax0_50': [0, 30], 'armorLevelMin_Max': [3, 5], 'armorLevelWeights1_6': [1, 1, 1, 4, 2, 1], 'ammoRemovePCTGood_Bad': [20, 40], 'healingItemsMin_Max': [2, 4], 'magazinesInVestMin_Max': [2, 2], 'backpackSizeMin_Max': [-1, 20], 'maxTacticalDevices': 1, 'maxPrimaryOptics': 1, 'removeWeaponTypesSemiAutoPumpBolt': [''], 'specificWeapons': [''], 'useWeaponsFromOtherMods': False}, 'PMCs': {'changeGear': True, 'moreModVariety': True, 'modQuality0_10': 10, 'modFrequency0_2': 0.7, 'magQuality0_10': 10, 'removeHatsPCT0_100': 0, 'armorLevelMin_Max': [4, 6], 'armorLevelWeights1_6': [1, 1, 1, 1, 1, 1], 'armorRarityMinMax0_50': [0, 50], 'primaryWeaponRarityMinMax0_50': [20, 50], 'sidearmRarityMinMax0_50': [20, 50], 'ammoRemovePCTGood_Bad': [0, 60], 'healingItemsMin_Max': [3, 5], 'magazinesInVestMin_Max': [2, 2], 'backpackSizeMin_Max': [-1, 24], 'maxTacticalDevices': 2, 'maxPrimaryOptics': 1, 'removeWeaponTypesSemiAutoPumpBolt': ['pump', 'bolt'], 'specificWeapons': [''], 'useWeaponsFromOtherMods': False}, 'allAI': {'factionIdentifiers': True, 'removeAIVogGrenades': False, 'noHatsOnlyHelmets': False, 'removeVanillaBotWeapons': True, 'removeVanillaBotArmor': True, 'botsWillNotSpawnWithTheseThings': ['5d52cc5ba4b9367408500062', '5cdeb229d7f00c000e7ce174']}, 'miscChanges': {'requiresOtherMods': {'AIO': {'AllowAIToUseBothArmoredVestsAndArmoredRigs': True}}}}, 'spawnChanges': {'controlOptions': {'doNotChangeTheseSPECIFICMaps': ['mapnameshere', 'seperatethemwithcommas'], 'spawnPMCsAsBosses': False, 'breakUpSpawnWaves': False, 'noScavsInLabs': True, 'removeVanillaSpawns': False, 'removeVanillaPMCSpawns': False, 'removeSniperBots': False, 'autoAddPMCsBasedOnMapSize': True, 'allPMCsareBEARs': True, 'allPMCsareUSECs': False, 'scavReplaceWith___AffectsOnlyVanillaWaves': False, 'scavReplaceWithRaidersPCT0_100': 10, 'scavReplaceWithPMCsPCT0_100': 0, 'extraScavsPerVanillaWave': 1, 'mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact': [0.8, 1, 1, 1.2, 1.2, 0.5, 1], 'evenlySpreadAllSpawnLocations': True}, 'extraWaves': {'scavWaves': 5, 'scavWaveSizeMin': 2, 'scavWaveSizeMax': 4, 'PMCWaves': 1, 'PMCWaveSizeMin': 2, 'PMCWaveSizeMax': 4, 'raiderWaves': 2, 'raiderWaveSizeMin': 2, 'raiderWaveSizeMax': 2, 'gluharRaiderWaves': 2, 'gluharRaiderWaveSizeMin': 2, 'gluharRaiderWaveSizeMax': 2, 'OnlyWorksProperlyWhenTaggedAndCursedIsEnabled': {'waveSpawnChancePCT0_100': 50, 'cultistWaves': 2, 'cultistWaveSizeMin': 5, 'cultistWaveSizeMax': 5}}, 'spawnTiming': {'evenlySpaceAllSpawnTimes': False, 'extraWaveSpawnSpeedFactor': 2, 'extraWaveSpawnSpeedFactorMaxTime': 300, 'spawnExtraWavesImmediately': False, 'spawn_ALL_WavesImmediately': False}, 'maxBotsPerZone': 20}, 'sillyChanges': {'allBulletsAreTracers': False, 'tracerGrenades': True, 'pumpkinCultists': True, 'COD_mode': False, 'gravestone': True}, 'miscChanges': {'startServerAtSpecificTime': False, 'startTime': '05:55:00', 'timeAcceleration': 7}, 'debug': {'showSpawns': 'all', 'showBosses': 'all', 'showBossPMCs': 'all', 'showGuns': 'assaultGroup', 'showArmor': 'assaultGroup', 'showAmmo': 'assaultGroup', 'saveDebugFiles': True, 'reportWavesBeforeRaid': False}}
#defaultConfig = {'Explanation': {"############## Set the values to true or false, or to a number if it's that type of value. It should be fairly self explanatory, but if there's anything weird I'll make a readme file that explains more. It should be in the mod's main folder.######################": '///', "############## For real, check the README file if anything here confuses you in the slighest. Heck, even if it doesn't confuse you, it can't hurt to glance over it anyways.######################": '///', 'Notes on Bot Names': 'Scav = assault; Sniper = marksman; Raider = pmcbot; Cultist leader = sectantpriest; Cultist = sectantwarrior; PMC = assaultgroup; Reshala = bossbully; Gluhar = bossgluhar; Killa = bosskilla; Shturman = bosskojaniy; Sanitar = bosssanitar; Boss minions = followerbully, followergluharassault, followergluharscout, followergluharsecurity, followergluharsnipe, followerkojaniy, followersanitar', 'Notes on Map Names': 'Customs = bigmap, Woods = woods, Reserve = rezervbase, Shoreline = shoreline, Factory (day) = factory4_day, Factory (night) = factory4_night, Interchange = interchange, Labs = laboratory'}, 'disableAllSpawnChanges': False, 'disableAllGearChanges': False, 'disableAllAIChanges': False, 'aiChanges': {'changeBots': {'lowLevelAIs': ['assault'], 'midLevelAIs': ['pmcbot', 'followergluharassault', 'followergluharscout', 'followergluharsecurity', 'followergluharsnipe', 'followerkojaniy', 'followersanitar', 'followerbully'], 'highLevelAIs': ['assaultgroup']}, 'Note': 'These are the MAIN values for adjusting difficulty. If you want an easier or harder time overall, increase or decrease these. For details on these or any other entries, please check the readme.', 'lowLevelAIDifficultyMod_Neg3_3': 0.85, 'midLevelAIDifficultyMod_Neg3_3': 0.95, 'highLevelAIDifficultyMod_Neg3_3': 1.05, 'IfYouDisableTheseThePMCSWontCountForQuestKills': {'scavsFightBEARBots': True, 'scavsFightUSECBots': True}}, 'overallDifficultyMultipliers': {'aiAimSpeedMult': 1, 'aiShotSpreadMult': 1, 'aiVisionSpeedMult': 1, 'sniperBotAccuracyMult': 1, 'visibleDistanceMult': 1, 'semiAutoFireRateMult': 1, 'aiRecoilMult': 1.1, 'aiHearingMult': 0.3, 'visibleAngleMult': 1.15, 'visibleAngleMax': 320, 'grenadePrecisionMult': 1, 'grenadeThrowRangeMax': 20, 'grenadeThrowRangeMult': 1, 'allowAimAtHead': True, 'allowGrenades': True, 'infiniteAIAmmo': True, 'infiniteAIHealing_PMCs_All_false': 'PMCs', 'cultistsVisibleOnThermals': False, 'changeHeadHPValues': False, 'setTheseBotsToDefaultPlayerHPBeforeMultsAreUsed': ['botnameshere', 'seperatethemwithcommas'], 'PMCHealthMult': 2, 'scavHealthMult': 1, 'raiderHealthMult': 1, 'bossHealthMult': 1, 'cultistHealthMult': 1}, 'allowBotsToTalk': {'scavs': True, 'raiders': False, 'PMCs': False, 'bossMinions': True}, 'AIbehaviourChanges': {'enabled': False, 'highLevelAIs': {'default': 0, 'balanced': 10, 'aggressive': 2, 'sniper': 2, 'patroller': 2, 'scout': 2, 'super_aggressive': 1}, 'midLevelAIs': {'default': 1, 'balanced': 0, 'aggressive': 0, 'sniper': 0, 'patroller': 0, 'scout': 0, 'super_aggressive': 0}}, 'AIgearChanges': {'CheckREADMEforInfo': {'optimizedLoading': True, 'saveGearToFile': False, 'loadGearFromFile': False}, 'scavBots': ['assault'], 'raiderBots': ['pmcbot', 'followergluharassault', 'followergluharscout', 'followergluharsecurity', 'followergluharsnipe'], 'pmcBots': ['bear', 'usec', 'assaultgroup', 'cursedassault'], 'scavs': {'changeGear': True, 'moreModVariety': True, 'modQuality0_10': 0, 'modFrequency0_2': 0.25, 'magQuality0_10': 3, 'removeHatsPCT0_100': 25, 'armorRarityMinMax0_50': [0, 50], 'primaryWeaponRarityMinMax0_50': [0, 30], 'sidearmRarityMinMax0_50': [0, 10], 'armorLevelMin_Max': [1, 4], 'armorLevelWeights1_6': [1, 3, 4, 2, 1, 1], 'ammoRemovePCTGood_Bad': [35, 25], 'healingItemsMin_Max': [0, 4], 'magazinesInVestMin_Max': [2, 3], 'backpackSizeMin_Max': [-1, 16], 'maxTacticalDevices': 1, 'maxPrimaryOptics': 1, 'removeWeaponTypesSemiAutoPumpBolt': [''], 'specificWeapons': [''], 'useWeaponsFromOtherMods': False}, 'raiders': {'changeGear': True, 'moreModVariety': True, 'modQuality0_10': 7, 'modFrequency0_2': 0.6, 'magQuality0_10': 7, 'removeHatsPCT0_100': 0, 'armorRarityMinMax0_50': [0, 50], 'primaryWeaponRarityMinMax0_50': [10, 40], 'sidearmRarityMinMax0_50': [0, 30], 'armorLevelMin_Max': [3, 5], 'armorLevelWeights1_6': [1, 1, 1, 4, 2, 1], 'ammoRemovePCTGood_Bad': [20, 40], 'healingItemsMin_Max': [2, 4], 'magazinesInVestMin_Max': [2, 2], 'backpackSizeMin_Max': [-1, 20], 'maxTacticalDevices': 1, 'maxPrimaryOptics': 1, 'removeWeaponTypesSemiAutoPumpBolt': [''], 'specificWeapons': [''], 'useWeaponsFromOtherMods': False}, 'PMCs': {'changeGear': True, 'moreModVariety': True, 'modQuality0_10': 10, 'modFrequency0_2': 0.7, 'magQuality0_10': 10, 'removeHatsPCT0_100': 0, 'armorLevelMin_Max': [4, 6], 'armorLevelWeights1_6': [1, 1, 1, 1, 1, 1], 'armorRarityMinMax0_50': [0, 50], 'primaryWeaponRarityMinMax0_50': [20, 50], 'sidearmRarityMinMax0_50': [20, 50], 'ammoRemovePCTGood_Bad': [0, 60], 'healingItemsMin_Max': [3, 5], 'magazinesInVestMin_Max': [2, 2], 'backpackSizeMin_Max': [-1, 24], 'maxTacticalDevices': 2, 'maxPrimaryOptics': 1, 'removeWeaponTypesSemiAutoPumpBolt': ['pump', 'bolt'], 'specificWeapons': [''], 'useWeaponsFromOtherMods': False}, 'allAI': {'factionIdentifiers': True, 'removeAIVogGrenades': False, 'noHatsOnlyHelmets': False, 'removeVanillaBotWeapons': True, 'removeVanillaBotArmor': True, 'botsWillNotSpawnWithTheseThings': ['5d52cc5ba4b9367408500062', '5cdeb229d7f00c000e7ce174']}, 'miscChanges': {'requiresOtherMods': {'AIO': {'AllowAIToUseBothArmoredVestsAndArmoredRigs': True}}}}, 'spawnChanges': {'controlOptions': {'doNotChangeTheseSPECIFICMaps': ['mapnameshere', 'seperatethemwithcommas'], 'spawnPMCsAsBosses': False, 'breakUpSpawnWaves': False, 'noScavsInLabs': True, 'removeVanillaSpawns': False, 'removeVanillaPMCSpawns': False, 'removeSniperBots': False, 'autoAddPMCsBasedOnMapSize': True, 'allPMCsareBEARs': True, 'allPMCsareUSECs': False, 'scavReplaceWith___AffectsOnlyVanillaWaves': False, 'scavReplaceWithRaidersPCT0_100': 10, 'scavReplaceWithPMCsPCT0_100': 0, 'extraScavsPerVanillaWave': 1, 'mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact': [0.8, 1, 1, 1.2, 1.2, 1, 0.5], 'evenlySpreadAllSpawnLocations': False}, 'extraWaves': {'scavWaves': 1, 'scavWaveSizeMin': 2, 'scavWaveSizeMax': 4, 'PMCWaves': 1, 'PMCWaveSizeMin': 2, 'PMCWaveSizeMax': 4, 'raiderWaves': 2, 'raiderWaveSizeMin': 2, 'raiderWaveSizeMax': 2, 'gluharRaiderWaves': 1, 'gluharRaiderWaveSizeMin': 2, 'gluharRaiderWaveSizeMax': 2, 'OnlyWorksProperlyWhenTaggedAndCursedIsEnabled': {'waveSpawnChancePCT0_100': 50, 'cultistWaves': 2, 'cultistWaveSizeMin': 5, 'cultistWaveSizeMax': 5}}, 'spawnTiming': {'evenlySpaceAllSpawnTimes': False, 'extraWaveSpawnSpeedFactor': 2, 'extraWaveSpawnSpeedFactorMaxTime': 300, 'spawnExtraWavesImmediately': False, 'spawn_ALL_WavesImmediately': False}, 'maxBotsPerZone': 20}, 'sillyChanges': {'allBulletsAreTracers': False, 'tracerGrenades': True, 'pumpkinCultists': True, 'COD_mode': False, 'COD_modeMaxHPPerLimb': 200, 'gravestone': True}, 'miscChanges': {'startServerAtSpecificTime': False, 'startTime': '05:55:00', 'timeAcceleration': 7}, 'debug': {'showSpawns': 'all', 'showBosses': 'all', 'showBossPMCs': 'all', 'showGuns': 'assaultGroup', 'showArmor': 'assaultGroup', 'showAmmo': 'assaultGroup', 'saveDebugFiles': True, 'reportWavesBeforeRaid': False}}
#defaultConfig = {'Explanation': {"############## Set the values to true or false, or to a number if it's that type of value. It should be fairly self explanatory, but if there's anything weird I'll make a readme file that explains more. It should be in the mod's main folder.######################": '///', "############## For real, check the README file if anything here confuses you in the slighest. Heck, even if it doesn't confuse you, it can't hurt to glance over it anyways.######################": '///', 'Notes on Bot Names': 'Scav = assault; Sniper = marksman; Raider = pmcbot; Cultist leader = sectantpriest; Cultist = sectantwarrior; PMC = assaultgroup; Reshala = bossbully; Gluhar = bossgluhar; Killa = bosskilla; Shturman = bosskojaniy; Sanitar = bosssanitar; Boss minions = followerbully, followergluharassault, followergluharscout, followergluharsecurity, followergluharsnipe, followerkojaniy, followersanitar', 'Notes on Map Names': 'Customs = bigmap, Woods = woods, Reserve = rezervbase, Shoreline = shoreline, Factory (day) = factory4_day, Factory (night) = factory4_night, Interchange = interchange, Labs = laboratory'}, 'disableAllSpawnChanges': False, 'disableAllGearChanges': False, 'disableAllAIChanges': False, 'aiChanges': {'changeBots': {'lowLevelAIs': ['assault'], 'midLevelAIs': ['pmcbot', 'followergluharassault', 'followergluharscout', 'followergluharsecurity', 'followergluharsnipe', 'followerkojaniy', 'followersanitar', 'followerbully'], 'highLevelAIs': ['cursedassault', 'bear', 'usec']}, 'Note': 'These are the MAIN values for adjusting difficulty. If you want an easier or harder time overall, increase or decrease these. For details on these or any other entries, please check the readme.', 'overallAIDifficultyMod': -1.3, 'lowLevelAIDifficultyMod_Neg3_3': -0.15, 'midLevelAIDifficultyMod_Neg3_3': 1.55, 'highLevelAIDifficultyMod_Neg3_3': 3.05, 'IfYouDisableTheseThePMCSWontCountForQuestKills': {'scavsFightBEARBots': True, 'scavsFightUSECBots': True}}, 'overallDifficultyMultipliers': {'aiAimSpeedMult': 1, 'aiShotSpreadMult': 1, 'aiVisionSpeedMult': 1, 'sniperBotAccuracyMult': 1, 'visibleDistanceMult': 1, 'semiAutoFireRateMult': 1, 'aiRecoilMult': 1.1, 'aiHearingMult': 0.3, 'visibleAngleMult': 1, 'visibleAngleMax': 320, 'grenadePrecisionMult': 1, 'grenadeThrowRangeMax': 20, 'grenadeThrowRangeMult': 1, 'allowAimAtHead': True, 'allowGrenades': False, 'infiniteAIAmmo': True, 'infiniteAIHealing_PMCs_All_false': 'PMCs', 'cultistsVisibleOnThermals': False, 'changeHeadHPValues': False, 'setTheseBotsToDefaultPlayerHPBeforeMultsAreUsed': ['botnameshere', 'seperatethemwithcommas'], 'PMCHealthMult': 2, 'scavHealthMult': 1, 'raiderHealthMult': 1, 'bossHealthMult': 1, 'cultistHealthMult': 1}, 'allowBotsToTalk': {'scavs': True, 'raiders': False, 'PMCs': False}, 'AIbehaviourChanges': {'enabled': False, 'highLevelAIs': {'default': 0, 'balanced': 10, 'aggressive': 2, 'sniper': 2, 'patroller': 2, 'scout': 2, 'super_aggressive': 1}, 'midLevelAIs': {'default': 1, 'balanced': 0, 'aggressive': 0, 'sniper': 0, 'patroller': 0, 'scout': 0, 'super_aggressive': 0}, 'lowLevelAIs': {'default': 1, 'balanced': 0, 'aggressive': 0, 'sniper': 0, 'patroller': 0, 'scout': 0, 'super_aggressive': 0}}, 'AIgearChanges': {'CheckREADMEforInfo': {'optimizedLoading': True, 'saveGearToFile': False, 'loadGearFromFile': False}, 'scavBots': ['assault'], 'raiderBots': ['pmcbot', 'followergluharassault', 'followergluharscout', 'followergluharsecurity', 'followergluharsnipe'], 'pmcBots': ['bear', 'usec', 'cursedassault'], 'scavs': {'changeGear': True, 'moreModVariety': True, 'modQuality0_10': 0, 'modFrequency0_2': 0.25, 'magQuality0_10': 3, 'removeHatsPCT0_100': 25, 'armorRarityMinMax0_50': [0, 50], 'primaryWeaponRarityMinMax0_50': [0, 30], 'sidearmRarityMinMax0_50': [0, 10], 'armorLevelMin_Max': [1, 4], 'armorLevelWeights1_6': [1, 3, 4, 2, 1, 1], 'ammoRemovePCTGood_Bad': [35, 25], 'healingItemsMin_Max': [0, 4], 'magazinesInVestMin_Max': [2, 3], 'backpackSizeMin_Max': [-1, 16], 'maxTacticalDevices': 1, 'maxPrimaryOptics': 1, 'removeWeaponTypesSemiAutoPumpBolt': [''], 'specificWeapons': [''], 'useWeaponsFromOtherMods': False}, 'raiders': {'changeGear': True, 'moreModVariety': True, 'modQuality0_10': 7, 'modFrequency0_2': 0.6, 'magQuality0_10': 7, 'removeHatsPCT0_100': 0, 'armorRarityMinMax0_50': [0, 50], 'primaryWeaponRarityMinMax0_50': [10, 40], 'sidearmRarityMinMax0_50': [0, 30], 'armorLevelMin_Max': [3, 5], 'armorLevelWeights1_6': [1, 1, 1, 4, 2, 1], 'ammoRemovePCTGood_Bad': [20, 40], 'healingItemsMin_Max': [2, 4], 'magazinesInVestMin_Max': [2, 2], 'backpackSizeMin_Max': [-1, 20], 'maxTacticalDevices': 1, 'maxPrimaryOptics': 1, 'removeWeaponTypesSemiAutoPumpBolt': [''], 'specificWeapons': [''], 'useWeaponsFromOtherMods': False}, 'PMCs': {'changeGear': True, 'moreModVariety': True, 'modQuality0_10': 10, 'modFrequency0_2': 0.7, 'magQuality0_10': 10, 'removeHatsPCT0_100': 0, 'armorLevelMin_Max': [4, 6], 'armorLevelWeights1_6': [1, 1, 1, 1, 1, 1], 'armorRarityMinMax0_50': [0, 50], 'primaryWeaponRarityMinMax0_50': [20, 50], 'sidearmRarityMinMax0_50': [20, 50], 'ammoRemovePCTGood_Bad': [0, 60], 'healingItemsMin_Max': [3, 5], 'magazinesInVestMin_Max': [2, 2], 'backpackSizeMin_Max': [-1, 24], 'maxTacticalDevices': 2, 'maxPrimaryOptics': 1, 'removeWeaponTypesSemiAutoPumpBolt': ['pump', 'bolt'], 'specificWeapons': [''], 'useWeaponsFromOtherMods': False}, 'allAI': {'factionIdentifiers': True, 'removeAIVogGrenades': False, 'noHatsOnlyHelmets': False, 'removeVanillaBotWeapons': True, 'removeVanillaBotArmor': True, 'botsWillNotSpawnWithTheseThings': ['5d52cc5ba4b9367408500062', '5cdeb229d7f00c000e7ce174']}, 'miscChanges': {'requiresOtherMods': {'AIO': {'AllowAIToUseBothArmoredVestsAndArmoredRigs': True}}}}, 'spawnChanges': {'controlOptions': {'doNotChangeTheseSPECIFICMaps': ['mapnameshere', 'seperatethemwithcommas'], 'breakUpSpawnWaves': False, 'noScavsInLabs': True, 'removeVanillaSpawns': False, 'removeVanillaPMCSpawns': False, 'removeSniperBots': True, 'autoAddPMCsBasedOnMapSize': False, 'allPMCsareBEARs': True, 'allPMCsareUSECs': False, 'scavReplaceWith___AffectsOnlyVanillaWaves': False, 'scavReplaceWithRaidersPCT0_100': 5, 'scavReplaceWithPMCsPCT0_100': 0, 'extraScavsPerVanillaWave': 1, 'mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact': [0.8, 1, 1, 1.2, 1.2, 1, 0.5], 'evenlySpreadAllSpawnLocations': True}, 'extraWaves': {'scavWaves': 1, 'scavWaveSizeMin': 2, 'scavWaveSizeMax': 4, 'PMCWaves': 6, 'PMCWaveSizeMin': 1, 'PMCWaveSizeMax': 3, 'raiderWaves': 2, 'raiderWaveSizeMin': 2, 'raiderWaveSizeMax': 3, 'gluharRaiderWaves': 1, 'gluharRaiderWaveSizeMin': 2, 'gluharRaiderWaveSizeMax': 2, 'OnlyWorksProperlyWhenTaggedAndCursedIsEnabled': {'waveSpawnChancePCT0_100': 50, 'cultistWaves': 2, 'cultistWaveSizeMin': 5, 'cultistWaveSizeMax': 5}}, 'spawnTiming': {'evenlySpaceAllSpawnTimes': False, 'extraWaveSpawnSpeedFactor': 2, 'extraWaveSpawnSpeedFactorMaxTime': 300, 'spawnExtraWavesImmediately': False, 'spawn_ALL_WavesImmediately': False}, 'maxBotsPerZone': 20}, 'sillyChanges': {'allBulletsAreTracers': False, 'tracerGrenades': True, 'pumpkinCultists': True, 'COD_mode': False, 'COD_modeMaxHPPerLimb': 200, 'gravestone': True}, 'miscChanges': {'startServerAtSpecificTime': False, 'startTime': '05:55:00', 'timeAcceleration': 7}, 'debug': {'showSpawns': 'all', 'showBosses': 'all', 'showBossPMCs': 'all', 'showGuns': 'assaultGroup', 'showArmor': 'assaultGroup', 'showAmmo': 'assaultGroup', 'saveDebugFiles': True, 'reportWavesBeforeRaid': False}}
defaultConfig = {'Explanation': {"############## Set the values to true or false, or to a number if it's that type of value. It should be fairly self explanatory, but if there's anything weird I'll make a readme file that explains more. It should be in the mod's main folder.######################": '///', "############## For real, check the README file if anything here confuses you in the slighest. Heck, even if it doesn't confuse you, it can't hurt to glance over it anyways.######################": '///', 'Notes on Bot Names': 'Scav = assault; Sniper = marksman; Raider = pmcbot; Cultist leader = sectantpriest; Cultist = sectantwarrior; PMC = cursedassault; Reshala = bossbully; Gluhar = bossgluhar; Killa = bosskilla; Shturman = bosskojaniy; Sanitar = bosssanitar; Boss minions = followerbully, followergluharassault, followergluharscout, followergluharsecurity, followergluharsnipe, followerkojaniy, followersanitar', 'Notes on Map Names': 'Customs = bigmap, Woods = woods, Reserve = rezervbase, Shoreline = shoreline, Factory (day) = factory4_day, Factory (night) = factory4_night, Interchange = interchange, Labs = laboratory'}, 'disableAllSpawnChanges': False, 'disableAllGearChanges': False, 'disableAllAIChanges': False, 'silenceWeaponGenErrors': True, 'aiChanges': {'changeBots': {'lowLevelAIs': ['assault'], 'midLevelAIs': ['pmcbot', 'followergluharassault', 'followergluharscout', 'followergluharsecurity', 'followergluharsnipe', 'followerkojaniy', 'followersanitar', 'followerbully'], 'highLevelAIs': ['assaultgroup', 'bear', 'usec']}, 'Note': 'These are the MAIN values for adjusting difficulty. If you want an easier or harder time overall, increase or decrease these. For details on these or any other entries, please check the readme.', 'overallAIDifficultyMod': -0.5, 'lowLevelAIDifficultyMod_Neg3_3': 0, 'midLevelAIDifficultyMod_Neg3_3': 1.15, 'highLevelAIDifficultyMod_Neg3_3': 2.65, 'IfYouDisableTheseThePMCSWontCountForQuestKills': {'scavsFightBEARBots': True, 'scavsFightUSECBots': True}}, 'overallDifficultyMultipliers': {'aiAimSpeedMult': 1, 'aiShotSpreadMult': 1, 'aiVisionSpeedMult': 1, 'sniperBotAccuracyMult': 1, 'visibleDistanceMult': 1, 'semiAutoFireRateMult': 1, 'aiRecoilMult': 1.1, 'aiHearingMult': 0.3, 'visibleAngleMult': 1, 'visibleAngleMax': 320, 'grenadePrecisionMult': 1, 'grenadeThrowRangeMax': 20, 'grenadeThrowRangeMult': 1, 'allowAimAtHead': False, 'allowGrenades': True, 'infiniteAIAmmo': True, 'enableAutomaticDifficulty': False, 'infiniteAIHealing_PMCs_All_false': 'PMCs', 'cultistsVisibleOnThermals': False, 'changeHeadHPValues': False, 'setTheseBotsToDefaultPlayerHPBeforeMultsAreUsed': ['botnameshere', 'seperatethemwithcommas'], 'PMCHealthMult': 2, 'scavHealthMult': 1, 'raiderHealthMult': 1, 'bossHealthMult': 1, 'cultistHealthMult': 1}, 'allowBotsToTalk': {'scavs': True, 'raiders': True, 'PMCs': True}, 'AIbehaviourChanges': {'enabled': True, 'highLevelAIs': {'default': 1, 'balanced': 1, 'aggressive': 1, 'ranged': 1}, 'midLevelAIs': {'default': 1, 'balanced': 1, 'aggressive': 1, 'ranged': 1}, 'lowLevelAIs': {'default': 1, 'balanced': 1, 'aggressive': 1, 'ranged': 1}}, 'AIgearChanges': {'CheckREADMEforInfo': {'optimizedLoading': True, 'saveGearToFile': False, 'loadGearFromFile': False}, 'scavBots': ['assault'], 'raiderBots': ['pmcbot', 'followergluharassault', 'followergluharscout', 'followergluharsecurity', 'followergluharsnipe'], 'pmcBots': ['bear', 'usec', 'cursedassault', 'assaultgroup'], 'scavs': {'changeGear': True, 'moreModVariety': True, 'modQuality0_10': 1, 'modFrequency0_2': 0.6, 'magQuality0_10': 3, 'magDowngradeChance': 50, 'removeHatsPCT0_100': 25, 'armorRarityMinMax0_50': [0, 50], 'primaryWeaponRarityMinMax0_50': [0, 30], 'sidearmRarityMinMax0_50': [0, 10], 'armorLevelMin_Max': [1, 4], 'armorLevelWeights1_6': [1, 3, 4, 2, 1, 1], 'weaponMaxDurabilityMin_Max': [10, 65], 'weaponDurabilityMin_Max': [35, 65], 'ammoRemovePCTGood_Bad': [35, 25], 'healingItemsMin_Max': [0, 4], 'magazinesInVestMin_Max': [2, 3], 'backpackSizeMin_Max': [-1, 16], 'maxTacticalDevices': 1, 'maxPrimaryOptics': 1, 'removeWeaponTypesSemiAutoPumpBolt': [''], 'specificWeapons': [''], 'useWeaponsFromOtherMods': True}, 'raiders': {'changeGear': True, 'moreModVariety': True, 'modQuality0_10': 7, 'modFrequency0_2': 0.8, 'magQuality0_10': 7, 'magDowngradeChance': 50, 'removeHatsPCT0_100': 0, 'armorRarityMinMax0_50': [0, 50], 'primaryWeaponRarityMinMax0_50': [10, 40], 'sidearmRarityMinMax0_50': [0, 30], 'armorLevelMin_Max': [3, 5], 'armorLevelWeights1_6': [1, 1, 1, 4, 2, 1], 'weaponMaxDurabilityMin_Max': [35, 75], 'weaponDurabilityMin_Max': [35, 65], 'ammoRemovePCTGood_Bad': [20, 40], 'healingItemsMin_Max': [2, 4], 'magazinesInVestMin_Max': [2, 2], 'backpackSizeMin_Max': [-1, 20], 'maxTacticalDevices': 1, 'maxPrimaryOptics': 1, 'removeWeaponTypesSemiAutoPumpBolt': [''], 'specificWeapons': [''], 'useWeaponsFromOtherMods': True}, 'PMCs': {'changeGear': True, 'moreModVariety': True, 'modQuality0_10': 10, 'modFrequency0_2': 1, 'magQuality0_10': 10, 'magDowngradeChance': 50, 'removeHatsPCT0_100': 0, 'armorLevelMin_Max': [4, 6], 'armorLevelWeights1_6': [1, 1, 1, 1, 1, 1], 'weaponMaxDurabilityMin_Max': [50, 90], 'weaponDurabilityMin_Max': [35, 65], 'armorRarityMinMax0_50': [0, 50], 'primaryWeaponRarityMinMax0_50': [20, 50], 'sidearmRarityMinMax0_50': [20, 50], 'ammoRemovePCTGood_Bad': [0, 60], 'healingItemsMin_Max': [3, 5], 'magazinesInVestMin_Max': [2, 2], 'backpackSizeMin_Max': [-1, 24], 'maxTacticalDevices': 2, 'maxPrimaryOptics': 1, 'removeWeaponTypesSemiAutoPumpBolt': ['pump', 'bolt'], 'specificWeapons': [''], 'useWeaponsFromOtherMods': True}, 'allAI': {'factionIdentifiers': True, 'removeAIVogGrenades': False, 'noHatsOnlyHelmets': False, 'removeVanillaBotWeapons': True, 'removeVanillaBotArmor': True, 'botsWillNotSpawnWithTheseThings': ['5d52cc5ba4b9367408500062', '5cdeb229d7f00c000e7ce174']}, 'miscChanges': {'requiresOtherMods': {'AIO': {'AllowAIToUseBothArmoredVestsAndArmoredRigs': False}}}}, 'spawnChanges': {'controlOptions': {'doNotChangeTheseSPECIFICMaps': ['mapnameshere', 'seperatethemwithcommas'], 'spawnPMCsAsBosses': False, 'breakUpSpawnWaves': False, 'noScavsInLabs': True, 'removeVanillaSpawns': False, 'removeVanillaPMCSpawns': False, 'removeSniperBots': False, 'autoAddPMCsBasedOnMapSize': False, 'allPMCsareBEARs': True, 'allPMCsareUSECs': False, 'scavReplaceWith___AffectsOnlyVanillaWaves': False, 'scavReplaceWithRaidersPCT0_100': 5, 'scavReplaceWithPMCsPCT0_100': 0, 'extraScavsPerVanillaWave': 0, 'mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact': [0.8, 1, 1, 1.2, 1.2, 1, 0.6], 'evenlySpreadAllSpawnLocations': True}, 'extraWaves': {'scavWaves': 4, 'scavWaveSizeMin': 1, 'scavWaveSizeMax': 2, 'PMCWaves': 3, 'PMCWaveSizeMin': 1, 'PMCWaveSizeMax': 3, 'raiderWaves': 4, 'raiderWaveSizeMin': 1, 'raiderWaveSizeMax': 2, 'gluharRaiderWaves': 1, 'gluharRaiderWaveSizeMin': 2, 'gluharRaiderWaveSizeMax': 2, 'OnlyWorksProperlyWhenTaggedAndCursedIsEnabled': {'waveSpawnChancePCT0_100': 50, 'cultistWaves': 2, 'cultistWaveSizeMin': 5, 'cultistWaveSizeMax': 5}}, 'spawnTiming': {'evenlySpaceAllSpawnTimes': False, 'extraWaveSpawnSpeedFactor': 2, 'extraWaveSpawnSpeedFactorMaxTime': 300, 'spawnExtraWavesImmediately': False, 'spawn_ALL_WavesImmediately': False}, 'maxBotsPerZone': 20}, 'sillyChanges': {'allBulletsAreTracers': False, 'tracerGrenades': True, 'pumpkinCultists': True, 'COD_mode': False, 'COD_modeMaxHPPerLimb': 200, 'gravestone': True}, 'miscChanges': {'enableProgressiveGear': False, 'startServerAtSpecificTime': False, 'startTime': '05:55:00', 'timeAcceleration': 2}, 'debug': {'showSpawns': 'all', 'showBosses': 'all', 'showBossPMCs': 'all', 'showGuns': 'cursedassault', 'showArmor': 'cursedassault', 'showAmmo': 'cursedassault', 'saveDebugFiles': True, 'reportWavesBeforeRaid': False}}
#with open(r'C:\Users\Fin\PycharmProjects\AI Tweaks Configger\config\config.json') as q:
#   print(json.load(q))

a = list(config)

#print(config["Explanation"])

root = tk.Tk()
root.title("Fin's AI Tweaks Config Editor.")
tabControl = ttk.Notebook(root)

tab1 = ttk.Frame(tabControl)
tab2 = ttk.Frame(tabControl)
tab3 = ttk.Frame(tabControl)
tab4 = ttk.Frame(tabControl)
tab5 = ttk.Frame(tabControl)
tab6 = ttk.Frame(tabControl)
tab7 = ttk.Frame(tabControl)
tab8 = ttk.Frame(tabControl)

tabControl.add(tab1, text='General')
tabControl.add(tab2, text='Difficulty')
tabControl.add(tab8, text='Behaviour')
tabControl.add(tab3, text='Gear')
tabControl.add(tab4, text='Spawns')
tabControl.add(tab5, text='Misc')
tabControl.add(tab6, text='Debug')
tabControl.add(tab7, text='FAQ')
tabControl.pack(expand=1, fill="both")

disableAllSpawnChanges = config["disableAllSpawnChanges"]
disableAllGearChanges = config["disableAllGearChanges"]
disableAllAIChanges = config["disableAllAIChanges"]
silenceWeaponGenErrors = config["silenceWeaponGenErrors"]

#General tab

disableChanges = ttk.Frame(tab1)
disableChanges.grid(column=0, row=0)

ttk.Label(disableChanges,
          text="Mod functions: ", font=("Arial", 15)).grid(column=0, columnspan = 3, row=0, padx=30, pady=5)

ttk.Label(disableChanges,
          text="False:").grid(column=1, row=1, padx=30, pady=5)
ttk.Label(disableChanges,
          text="True:").grid(column=2, row=1, padx=30, pady=5)
dA1 = ttk.Label(disableChanges,text="Disable all gear changes:")
dA1.grid(column=0, row=2, padx=30, pady=5)
dA1_ttp = CreateToolTip(dA1, "Original config name: disableAllGearChanges\n\nDisables all gear changes")
dA2 = ttk.Label(disableChanges, text="Disable all AI changes:")
dA2.grid(column=0, row=3, padx=30, pady=5)
dA2_ttp = CreateToolTip(dA2, "Original config name: disableAllAIChanges\n\nDisables all AI changes")
dA3 = ttk.Label(disableChanges, text="Disable all spawn changes:")
dA3.grid(column=0, row=4, padx=30, pady=5)
dA3_ttp = CreateToolTip(dA3, "Original config name: disableAllSpawnChanges\n\nDisables all spawn changes")
dA4 = ttk.Label(disableChanges, text="Mute weapon generation errors:")
dA4.grid(column=0, row=5, padx=30, pady=5)
dA4_ttp = CreateToolTip(dA4, "Original config name: silenceWeaponGenErrors\n\nPrevents errors from being logged during weapon generation. This may hide other types of errors from the user, and should be disabled if anything unusual seems to be happening. However, in 99% of use cases, this option will only hide non-critical information from the player and prevent their server log from becoming unecessarily cluttered.")

disableRadios1 = ttk.Frame(disableChanges)
disableRadios1.grid(column=1, row=2, columnspan=2)
disableRadios2 = ttk.Frame(disableChanges)
disableRadios2.grid(column=1, row=3, columnspan=2)
disableRadios3 = ttk.Frame(disableChanges)
disableRadios3.grid(column=1, row=4, columnspan=2)
disableRadios4 = ttk.Frame(disableChanges)
disableRadios4.grid(column=1, row=5, columnspan=2)
disabGear = tk.BooleanVar()
disabAI = tk.BooleanVar()
disabSpawn = tk.BooleanVar()
disabErrors = tk.BooleanVar()
rad1 = ttk.Radiobutton(disableRadios1,
            text="", variable = disabGear, value = False
            ).grid(column=1, row=1, padx=30, pady=5)
rad2 = ttk.Radiobutton(disableRadios1,
            text="", variable = disabGear, value = True
            ).grid(column=2, row=1, padx=30, pady=5)
rad3 = ttk.Radiobutton(disableRadios2,
            text="", variable = disabAI, value = False
            ).grid(column=1, row=2, padx=30, pady=5)
rad4 = ttk.Radiobutton(disableRadios2,
            text="", variable = disabAI, value = True
            ).grid(column=2, row=2, padx=30, pady=5)
rad5 = ttk.Radiobutton(disableRadios3,
            text="", variable = disabSpawn, value = False
            ).grid(column=1, row=3, padx=30, pady=5)
rad6 = ttk.Radiobutton(disableRadios3,
            text="", variable = disabSpawn, value = True
            ).grid(column=2, row=3, padx=30, pady=5)
rad7 = ttk.Radiobutton(disableRadios4,
            text="", variable = disabErrors, value = False
            ).grid(column=1, row=3, padx=30, pady=5)
rad8 = ttk.Radiobutton(disableRadios4,
            text="", variable = disabErrors, value = True
            ).grid(column=2, row=3, padx=30, pady=5)
if config["disableAllGearChanges"] is False:
    disabGear.set(False)
else:
    disabGear.set(True)
if config["disableAllAIChanges"] is False:
    disabAI.set(False)
else:
    disabAI.set(True)
if config["disableAllSpawnChanges"] is False:
    disabSpawn.set(False)
else:
    disabSpawn.set(True)
if config["silenceWeaponGenErrors"] is False:
    disabErrors.set(False)
else:
    disabErrors.set(True)

aiTypes = ttk.Frame(tab1)
aiTypes.grid(column=0, row=1)

#changeBots
ttk.Label(aiTypes,
          text="AI categories\n(Used by difficulty settings):", font=("Arial", 15)).grid(column=0, columnspan = 2, row=1, padx=30, pady=5)
windowLowLevelAIs = makeTextEntry(aiTypes, "Low level AIs:", 2, 2, 50, 5, config["aiChanges"]["changeBots"]["lowLevelAIs"], "Original config name: lowLevelAIs\n\nThis is a list of all bots that will be assigned to the 'low level' difficulty category. By default this is for scavs.")
windowMidLevelAIs = makeTextEntry(aiTypes, "Mid level AIs:", 3, 2, 50, 5, config["aiChanges"]["changeBots"]["midLevelAIs"], "Original config name: midLevelAIs\n\nThis is a list of all bots that will be assigned to the 'mid level' difficulty category. By default this is for raiders and boss minions.")
windowHighLevelAIs = makeTextEntry(aiTypes, "High level AIs:", 4, 2, 50, 5, config["aiChanges"]["changeBots"]["highLevelAIs"], "Original config name: highLevelAIs\n\nThis is a list of all bots that will be assigned to the 'high level' difficulty category. By default this is only for PMCs.")

generalTabRight = ttk.Frame(tab1)
generalTabRight.grid(column=1, row=0, rowspan= 1)

ttk.Label(generalTabRight,
          text="Faction behaviour:", font=("Arial", 15)).grid(column=0, row=0, padx=30, pady=5, sticky = N)

factionBehaviour = ttk.Frame(tab1)
factionBehaviour.grid(column=1, row=1)
ttk.Label(factionBehaviour,
          text="Setting these to false will cause\nthe associated PMC bots to no longer\ncount as PMCs for the purposes of\nquest completion or dogtag turnins.", font=("Arial", 8)).grid(column=0, columnspan = 3, row=4, padx=30, pady=5, sticky = N)
ttk.Label(factionBehaviour,
          text="False:").grid(column=1, row=0, padx=30, pady=5)
ttk.Label(factionBehaviour,
          text="True:").grid(column=2, row=0, padx=30, pady=5)
sF1 = ttk.Label(factionBehaviour, text="Scavs fight with BEAR bots:")
sF1.grid(column=0, row=1, padx=30, pady=5)
sF1_ttp = CreateToolTip(sF1, "Original config name: scavsFightBEARBots\n\nIf DISABLED, this places BEAR bots on the same side as scavs. This will also disable BEAR bots from counting as PMCs for quest-completion purposes (IE: Kill PMCS / collect PMC dogtags), and will cause their dogtags to generate improperly.")
sF2 = ttk.Label(factionBehaviour, text="Scavs fight with USEC bots:")
sF2.grid(column=0, row=2, padx=30, pady=5)
sF2_ttp = CreateToolTip(sF2, "Original config name: scavsFightUSECBots\n\nIf DISABLED, this places USEC bots on the same side as scavs. This will also disable BEAR bots from counting as PMCs for quest-completion purposes (IE: Kill PMCS / collect PMC dogtags), and will cause their dogtags to generate improperly.")
fightUSEC = tk.BooleanVar()
fightBEAR = tk.BooleanVar()
fightUSECRad1 = ttk.Radiobutton(factionBehaviour,
            text="", variable = fightUSEC, value = False
            ).grid(column=1, row=1, padx=30, pady=5)
fightUSECRad2 = ttk.Radiobutton(factionBehaviour,
            text="", variable = fightUSEC, value = True
            ).grid(column=2, row=1, padx=30, pady=5)
fightBEARRad1 = ttk.Radiobutton(factionBehaviour,
            text="", variable = fightBEAR, value = False
            ).grid(column=1, row=2, padx=30, pady=5)
fightBEARRad2 = ttk.Radiobutton(factionBehaviour,
            text="", variable = fightBEAR, value = True
            ).grid(column=2, row=2, padx=30, pady=5)
if config["aiChanges"]["IfYouDisableTheseThePMCSWontCountForQuestKills"]["scavsFightBEARBots"] is False:
    fightBEAR.set(False)
else:
    fightBEAR.set(True)
if config["aiChanges"]["IfYouDisableTheseThePMCSWontCountForQuestKills"]["scavsFightUSECBots"] is False:
    fightUSEC.set(False)
else:
    fightUSEC.set(True)

botTypesLabel = ttk.Frame(tab1)
botTypesLabel.grid(column=0, row=2)

ttk.Label(botTypesLabel, text="Vanilla bot names:", font=("Arial", 15)).grid(column=0, row=0, padx=30, pady=5, sticky = W)
ttk.Label(botTypesLabel, justify = LEFT, wraplength = 600, text="Scav = assault; PMC = assaultgroup; Sniper = marksman; Raider = pmcbot; Cultist leader = sectantpriest; Cultist = sectantwarrior; Reshala = bossbully; Gluhar = bossgluhar; Killa = bosskilla; Shturman = bosskojaniy; Sanitar = bosssanitar; Boss minions = followerbully, followergluharassault, followergluharscout, followergluharsecurity, followergluharsnipe, followerkojaniy, followersanitar").grid(column=0, columnspan = 1, row=1, padx=30, pady=3, sticky=W)

ttk.Label(botTypesLabel, text="Map names:", font=("Arial", 15)).grid(column=0, row=2, padx=30, pady=5, sticky = W)
ttk.Label(botTypesLabel, justify = LEFT, wraplength = 600, text="Customs = bigmap, Woods = woods, Reserve = rezervbase, Shoreline = shoreline, Factory (day) = factory4_day, Factory (night) = factory4_night, Interchange = interchange, Labs = laboratory").grid(column=0, columnspan = 1, row=3, padx=30, pady=3, sticky=W)

#Difficulty tab

mainDiffSettings = ttk.Frame(tab2)
mainDiffSettings.grid(column=0, row=0)

#ttk.Label(mainDiffSettings,
#          text="The ingame difficulty options 'Easy', 'Normal', 'Hard' and 'Impossible' add 0,1,2 or 3 to ALL AI difficulties, so giving an AI +1 here is the same as if they were one ingame difficulty setting higher.", wraplength=350).grid(column=0, columnspan=5, row=6, padx=30, pady=10)

ttk.Label(mainDiffSettings,
          text="Difficulty settings:", font=("Arial", 15)).grid(column=0, columnspan=5, row=0, padx=30, pady=10)
ttk.Label(mainDiffSettings,
          text="(These can be negative values and decimals.)").grid(column=0, columnspan=5, row=1, padx=30, pady=10)

OLD =makeLabelEntryCUSTOM(mainDiffSettings, E, W, 5, 5, "Base difficulty level      =       ",2, 2, config["aiChanges"]["overallAIDifficultyMod"], 10, "Original config name: overallAIDifficultyMod\n\nThis is the base difficulty value. Increasing this by one has the same effect as increasing all three of the below values by one.")
ttk.Label(mainDiffSettings, text="").grid(column=0, columnspan=5, row=3, padx=30, pady=0)
LLD =makeLabelEntryCUSTOM(mainDiffSettings, E, W, 5, 5, "Low level AI difficulty    =    0 +",4, 2, config["aiChanges"]["lowLevelAIDifficultyMod_Neg3_3"], 10, "Original config name: lowLevelAIDifficultyMod_Neg3_3\n\nThis is the difficulty value for low level AIs. Higher values make them more difficult.")
MLD =makeLabelEntryCUSTOM(mainDiffSettings, E, W, 5, 5, "Mid level AI difficulty    =    1 +",5, 2, config["aiChanges"]["midLevelAIDifficultyMod_Neg3_3"], 10, "Original config name: midLevelAIDifficultyMod_Neg3_3\n\nThis is the difficulty value for mid level AIs. Higher values make them more difficult.")
HLD =makeLabelEntryCUSTOM(mainDiffSettings, E, W, 5, 5, "High level AI difficulty    =    2 +",6, 2, config["aiChanges"]["highLevelAIDifficultyMod_Neg3_3"], 10, "Original config name: highLevelAIDifficultyMod_Neg3_3\n\nThis is the difficulty value for high level AIs. Higher values make them more difficult.")

overallDiffSettings = ttk.Frame(tab2)
overallDiffSettings.grid(column=0, row=1)

overallDiff = []
row = 0
mainDiffSettingsRight = ttk.Frame(tab2)
mainDiffSettingsRight.grid(column=1, row=0, rowspan=2)

#testVar = tk.BooleanVar()
#makeRadioLabel(mainDiffSettingsRight, "test", 99, testVar, config["overallDifficultyMultipliers"]["aiAimSpeedMult"])

ttk.Label(mainDiffSettingsRight, text="Aiming and Recoil: ", font=("Arial", 15)).grid(column=0, row=0, padx=30, pady=10, sticky=N)
aiAimSpeedMult = makeLabelEntry(mainDiffSettingsRight, "AI aiming time multiplier: ",1, 1, config["overallDifficultyMultipliers"]["aiAimSpeedMult"], "Original config name: aiAimSpeedMult\n\nThis determines how long it will take the AI to aim at you. Higher values make them take longer to aim, while lower values make them take less time to aim.")
aiShotSpreadMult = makeLabelEntry(mainDiffSettingsRight, "AI shot spread multiplier:",2, 1, config["overallDifficultyMultipliers"]["aiShotSpreadMult"], "Original config name: aiShotSpreadMult\n\nThis determines how far the AI's shots will 'scatter' from where they're trying to aim. Higher values make the AI less accurate, while lower values make them more accurate.")
aiVisionSpeedMult = makeLabelEntry(mainDiffSettingsRight, "AI spotting time multiplier:",3, 1, config["overallDifficultyMultipliers"]["aiVisionSpeedMult"], "Original config name: aiVisionSpeedMult\n\nThis determines how quickly the AI can spot you. Higher values make them take longer to spot you (I think. If you experience something different, please let me know. This one is a little weird.))")
sniperBotAccuracyMult =makeLabelEntry(mainDiffSettingsRight, "Sniper accuracy multiplier:",4, 1, config["overallDifficultyMultipliers"]["sniperBotAccuracyMult"], "Original config name: sniperBotAccuracyMult\n\nThis determines how accurate sniper bots are. Higher values should make them less accurate, while lower values make them more accurate.")
visibleDistanceMult =makeLabelEntry(mainDiffSettingsRight, "AI visible distance multiplier:",5, 1, config["overallDifficultyMultipliers"]["visibleDistanceMult"], "Original config name: visibleDistanceMult\n\nThis determines how far the AI can see. Higher values let them see further.")
semiAutoFireRateMult =makeLabelEntry(mainDiffSettingsRight, "Semi auto fire rate multiplier:",6, 1, config["overallDifficultyMultipliers"]["semiAutoFireRateMult"], "Original config name: semiAutoFireRateMult\n\nThis determines how quickly the AI will fire semi-automatic weapons. Higher values make them fire more quickly.")
aiRecoilMult =makeLabelEntry(mainDiffSettingsRight, "AI recoil multiplier:",7, 1, config["overallDifficultyMultipliers"]["aiRecoilMult"], "placeholOriginal config name: aiRecoilMult\n\nThis determines how much recoil the AI will 'feel'. Higher values make them experience more recoil, while lower values make them experience less.")
aiHearingMult =makeLabelEntry(mainDiffSettingsRight, "AI hearing multiplier:",8, 1, config["overallDifficultyMultipliers"]["aiHearingMult"], "Original config name: aiHearingMult\n\nThis determines how well the AI can hear. Higher values allow them to hear things further away. Note: Testing indicates that when a bot hears a hostile entity, they go into an 'ambush mode', in which they mostly sit still and wait for their target to come to them. Because of this, lower hearing values tend to make the AI feel more active.")
visibleAngleMult =makeLabelEntry(mainDiffSettingsRight, "AI visible angle multiplier:",9, 1, config["overallDifficultyMultipliers"]["visibleAngleMult"], "Original config name: visibleAngleMult\n\nThis determines how wide the AI's cone of vision is. Higher values make it wider.")
visibleAngleMax =makeLabelEntry(mainDiffSettingsRight, "Max AI angle of vision:",10, 1, config["overallDifficultyMultipliers"]["visibleAngleMax"], "Original config name: visibleAngleMax\n\nThis determines what the AI's maximum cone of vision is. No matter what this is set to, it cannot go above 360 degrees, for reasons that should be obvious.")

ttk.Label(overallDiffSettings, text="Grenades: ", font=("Arial", 15)).grid(column=0, row=0, padx=30, pady=10, sticky=N)
allowGrenades = tk.BooleanVar()
makeRadioLabel(overallDiffSettings, "Allow the AI to throw grenades:", 1, allowGrenades, config["overallDifficultyMultipliers"]["allowGrenades"], "Original config name: allowGrenades\n\nThis determines whether the AI are allowed to throw grenades. Note: This will not apply to any bots not listed as a low, medium or high level AI, so by default bosses and cultists can still throw grenades no matter what this is set to.")
grenadePrecisionMult = makeLabelEntry(overallDiffSettings, "AI grenade scattering multiplier:",2, 1, config["overallDifficultyMultipliers"]["grenadePrecisionMult"], "Original config name: grenadePrecisionMult\n\nThis determines how far the AI's grenades will 'scatter' off target. Higher values make them less accurate, while lower values make them more accurate.")
grenadeThrowRangeMax = makeLabelEntry(overallDiffSettings, "Max AI grenade range:",3, 1, config["overallDifficultyMultipliers"]["grenadeThrowRangeMax"], "Original config name: grenadeThrowRangeMax\n\nThis determines what the maximum range is at which the AI can throw grenades, regardless of how high the throwing multiplier is set.")
grenadeThrowRangeMult = makeLabelEntry(overallDiffSettings, "AI grenade range multiplier:",4, 1, config["overallDifficultyMultipliers"]["grenadeThrowRangeMult"], "Original config name: grenadeThrowRangeMult\n\nThis multiplies the maximum range at which the AI can throw grenades, which seems to start at about 15m for the easiest settings, and goes up by about 3m per difficulty level (I say about because this setting is measured in 'power' instead of actual meters, so I can't be sure)")

ttk.Label(mainDiffSettingsRight, text="Miscellaneous: ", font=("Arial", 15)).grid(column=0, row=11, padx=30, pady=10, sticky=N)
allowAimAtHead =tk.BooleanVar()
makeRadioLabel(mainDiffSettingsRight, "Allow the AI to aim at heads:", 12, allowAimAtHead, config["overallDifficultyMultipliers"]["allowAimAtHead"], "Original config name: allowAimAtHead\n\nThis determines whether or not the AI is allowed to deliberately aim at heads. Note: Even if this is disabled, headshots are still possible due to recoil and random shot scattering. In particular, a heavily-armored AI being shot at close range will often 'jerk' their shots into your head purely due to flinch.")
infiniteAIAmmo =tk.BooleanVar()
makeRadioLabel(mainDiffSettingsRight, "Infinite ammo for AIs:", 13, infiniteAIAmmo, config["overallDifficultyMultipliers"]["infiniteAIAmmo"], "Original config name: infiniteAIAmmo\n\nThis determines whether or not the AI is given 100x as much ammunition as usual in their secure container, which they will use to refill their magazines. This ammunition is NOT accessible to the player. This also places special items in their secure container that will negate the extra weight caused from carring more ammunition.")
infiniteAIHealing_PMCs_All_false =makeLabelEntryCUSTOM(mainDiffSettingsRight, W, W, 30, 30, "Infinite healing items for AIs (PMCs, all, or false):",14, 2, config["overallDifficultyMultipliers"]["infiniteAIHealing_PMCs_All_false"], 10, "Original config name: infiniteAIHealing_PMCs_All_false\n\nThe available entries for this are either PMCs, all, or false. No quotation marks are needed for any of the options. The affected bots will be given Sanitar's OP medkit in their secure container, which effectively gives them an infinite healing resource.\n\nNote: Bots cannot heal when in combat. There is a delay (Default 5 seconds) before they can start healing, and a minimum distance they must think they are from the player (default 30m). Healing also is not instant, and they go through the same animations that a player does.")
cultistsVisibleOnThermals =tk.BooleanVar()
makeRadioLabel(mainDiffSettingsRight, "Cultists visible on thermals:", 15, cultistsVisibleOnThermals, config["overallDifficultyMultipliers"]["cultistsVisibleOnThermals"], "Original config name: cultistsVisibleOnThermals\n\nThis determines whether the cultist's body temperature is high enough to be picked up on thermals. In default EFT, they CANNOT be seen on thermals.")
enableAutomaticDifficulty =tk.BooleanVar()
makeRadioLabel(mainDiffSettingsRight, "Enable automatic difficulty adjustments:", 16, enableAutomaticDifficulty, config["overallDifficultyMultipliers"]["enableAutomaticDifficulty"], "Original config name: enableAutomaticDifficulty\n\nThis automatically adjusts the difficulty up or down based on how often you die. This is still an experimental option, but is unlikely to cause bugs. It is experimental in the sense that the adjusting values used are still being tested, and may need some more fine-tuning.\n\nNOTE: THESE ADJUSTMENTS ONLY TAKE EFFECT WHEN YOU RESTART THE SERVER. Until the server is restarted, the game's difficulty will not change, but your wins and losses WILL still be recorded. For the best experience, you should restart your game and server after every raid.")

ttk.Label(overallDiffSettings, text="Healing: ", font=("Arial", 15)).grid(column=0, row=5, padx=30, pady=10, sticky=N)
changeHeadHPValues =tk.BooleanVar()
makeRadioLabel(overallDiffSettings, "Allow health multipliers to affect heads:", 6, changeHeadHPValues, config["overallDifficultyMultipliers"]["changeHeadHPValues"], "Original config name: changeHeadHPValues\n\nThis determines whether the health multipliers below will be allowed to affect the AI's head health. IE: If set to false, no matter how high or low the other values are set, the AI's head will only ever have its vanilla health value.")
setTheseBotsToDefaultPlayerHPBeforeMultsAreUsed = makeTextEntry(overallDiffSettings, "Give these bots player HP values before multipliers:", 7, 5, 50, 3, config["overallDifficultyMultipliers"]["setTheseBotsToDefaultPlayerHPBeforeMultsAreUsed"], "Original config name: setTheseBotsToDefaultPlayerHPBeforeMultsAreUsed\n\nNot every bot has the same HP as a PMC. If this value is set to true, any bot in this list will be given the same default HP values as a player. This occurs BEFORE the multipliers below are applied.")
PMCHealthMult =makeLabelEntry(overallDiffSettings, "AI PMC health multiplier:",8, 1, config["overallDifficultyMultipliers"]["PMCHealthMult"], "Original config name: PMCHealthMult\n\nThis multiplies the health of AI PMCs by the entered value. Setting this to zero or a negative number is probably a bad idea. This affects BEARs, USECs, and cursed assaults. Note: In vanilla AKI, PMCs have approximately twice the HP of a standard player.")
scavHealthMult =makeLabelEntry(overallDiffSettings, "Scav health multiplier:",9, 1, config["overallDifficultyMultipliers"]["scavHealthMult"], "Original config name: scavHealthMult\n\nThis multiplies the health of AI scavs by the entered value. Setting this to zero or a negative number is probably a bad idea. This affects scavs and sniper scavs.")
raiderHealthMult =makeLabelEntry(overallDiffSettings, "Raider health multiplier:",10, 1, config["overallDifficultyMultipliers"]["raiderHealthMult"], "Original config name: raiderHealthMult\n\nThis multiplies the health of raiders by the entered value. Setting this to zero or a negative number is probably a bad idea. This affects raiders and gluhar's raiders.")
bossHealthMult =makeLabelEntry(overallDiffSettings, "Boss health multiplier:",11, 1, config["overallDifficultyMultipliers"]["bossHealthMult"], "Original config name: bossHealthMult\n\nThis multiplies the health of bosses by the entered value. Setting this to zero or a negative number is probably a bad idea. This affects killa, reshala, shturman, gluhar and sanitar.")
cultistHealthMult =makeLabelEntry(overallDiffSettings, "Cultist health multiplier:",12, 1, config["overallDifficultyMultipliers"]["cultistHealthMult"], "Original config name: cultistHealthMult\n\nThis multiplies the health of cultists by the entered value. Setting this to zero or a negative number is probably a bad idea. This affects cultists and cultist priests.")


#Gear tab

gearOptionNames = ["changeGear","moreModVariety","modQuality0_10","modFrequency0_2","magQuality0_10","magDowngradeChance","removeHatsPCT0_100","armorRarityMinMax0_50","primaryWeaponRarityMinMax0_50","sidearmRarityMinMax0_50","armorLevelMin_Max","armorLevelWeights1_6","weaponMaxDurabilityMin_Max","weaponDurabilityMin_Max","ammoRemovePCTGood_Bad","healingItemsMin_Max","magazinesInVestMin_Max","backpackSizeMin_Max","maxTacticalDevices","maxPrimaryOptics","useWeaponsFromOtherMods","removeWeaponTypesSemiAutoPumpBolt","specificWeapons"]
gearOptionName2 = ["Change gear for this group:","Add new mods to this group's weapons:","Mod quality (0 - 10):","Mod frequency (0 - 2):","Magazine size (0 - 10):","Chance to shrink secondary magazines:","Remove x% of non-helmet headwear:","Armor value, Min and Max (0 - 50):","Primary weapon value, Min and Max (0 - 50):","Sidearm value, Min and Max (0 - 50):","Min and Max armor class available:","Relative chance to use each armor class (Same value = same chance):","Min and Max maximum weapon durability:","Min and Max current weapon durability:","Percent of Hi-Pen and Low-Pen ammo to remove from gear list:","Min and Max healing items (Functionality moved to inventory config):","Min and Max magazines in their vest:","Min and Max backpack size:","Maximum tactical devices on primary weapon (Buggy):","Maximum optics on primary weapon (Buggy):","Allow bot to use weapon from other mods:", "Remove weapons from gear list by type, 'semi', 'auto', 'bolt' 'pump':","Weapon IDs to force onto gear list:"]
gearTooltips = ["Original config name: changeGear\n\nIf set to false, this category of AIs will NOT have any changes made to their gear by the options in this tab. Note, however, that they can still be given identifying armbands, or affected by any gear-changing options found in other tabs.","Original config name: moreModVariety\n\nIf set to false, this category of AIs will NOT be given any extra mods for their weapons. Other changes in this tab can still affect them, however, such as giving them new weapons and armor.","Original config name: modQuality0_10\n\nThis value should be from 0 to 10. A value of 10 means all mods are made available, and 0 means only most common of mods will be added to the AI's potential loadout.","Original config name: modFrequency0_2\n\nThis value should be from 0 to 2. It determines how likely the AI is to have different types of mods by multiplying their default 'chance' values. Higher values mean they'll be more likely to have mods.","Original config name: magQuality0_10\n\nThis value should be from 0 to 10. The higher this value is, the more likely it is that the AI will be given high-capacity magazines. Lower values will prevent them from having access to high-capacity magazines. Note: This does not affect preset weapons.","Original config name: magDowngradeChance\n\nThis is the chance that the magazines in a bot's vest (NOT the magazine that they start with loaded in their weapon) will be changed to another magazine of the same size or smaller.","Original config name: removeHatsPCT0_100\n\nThis value should be from 0 to 100. Whatever value is entered, this will remove that percent of non-armor headwear from the AI's potential loadouts, making them more likely to have a helmet instead of other non-armor headwear.","Original config name: armorRarityMinMax0_50\n\nThis entry should have two values, placed inside square brackets and separated by a comma. The first value is the minimum 'rarity' of armor that will be present in the AI's potential loadouts, and the second value is the maximum value. 'Rarity' is a linear distribution based off of its base price in the item files, and its listed spawn chance in the item files. If you enter [0,25], the AI will be limited to the bottom 50% of armors, in terms of rarity. If you enter [25,50], they will instead be limited to the top 50%.\n\nNote: I personally recommend this be left at [0,50], becase armor is better suited to being limited by its class.","Original config name: primaryWeaponRarityMinMax0_50\n\nThis entry should have two values, placed inside square brackets and separated by a comma. The first value is the minimum 'rarity' of weapons that will be present in the AI's potential loadouts, and the second value is the maximum value. 'Rarity' is a linear distribution based off of its base price in the item files, and its listed spawn chance in the item files. If you enter [0,25], the AI will be limited to the bottom 50% of weapons, in terms of rarity. If you enter [25,50], they will instead be limited to the top 50%.","Original config name: sidearmRarityMinMax0_50\n\nThis entry should have two values, placed inside square brackets and separated by a comma. The first value is the minimum 'rarity' of weapons that will be present in the AI's potential loadouts, and the second value is the maximum value. 'Rarity' is a linear distribution based off of its base price in the item files, and its listed spawn chance in the item files. If you enter [0,25], the AI will be limited to the bottom 50% of weapons, in terms of rarity. If you enter [25,50], they will instead be limited to the top 50%.","Original config name: armorLevelMin_Max\n\nThis entry should have two values, placed inside square brackets and separated by a comma. The first value determines the minimum class of armor that the AI can use, and the second value determines the maximum class of armor that the AI can use.","Original config name: armorLevelWeights1_6\n\nThis entry should have six values, placed inside square brackets and separated by commas. This allows you to 'weight' the AI towards certain classes of armor. [1,2,4,1,1,1] would make the AI 2x as likely to be equipped with class 2 armor, and 4x as likely to equip class 3 armor *compared to the default*. This isn't perfect for various technical reasons, but if you want an AI to lean more towards a certain class of armor, increase its value. If you want it to lean away from a certain class of armor, increase the *other* values. [5,5,1,5,5,5] is much less likely to be equipped with class 3 armor, while all other classes are equally lilely.\n\nNote: This does NOT override the above entry. If an AI has a minimum armor class of 4, then it will never be given any class 3 armor no matter how highly class 3 armor is weighted. This option also interacts strangely with rigs vs. vests. Due to the order the game assigns these items, unless AIs are able to use both rigs and vests, the vests will take priority regardless of weight settings. This means that, since there are no class 3 or lower vests, AIs will often have a class 4 rig more often than this setting implies they should.","Original config name: weaponMaxDurabilityMin_Max\n\nThis setting determines the maximum durability of AI weapons. The maximum durability is the durability that a weapon CANNOT be repaired above. e.g.: A weapon with 80% maximum durability will only ever be repairable up to 80% durability","Original config name: weaponDurabilityMin_Max\n\nThis entry controlls the current durability of AI weapons. Current durability is determined as a percentage of maximum durability, so a weapon with a current durability of 50% and a maximum durability of 80% will appear at 40% durability ingame, but can be repaired up to its maximum durability value (80%) by vendors.","Original config name: ammoRemovePCTGood_Bad\n\nThis entry should have two values, placed inside square brackets and separated by a comma. Values entered are treated as percentages, IE: 50 = 50%. It then looks at the lowest-penetration round available for a given caliber, and the highest-penetration round for that caliber. Using 5.56x45 as an example, this would be Warmage and SSA AP, with penetration values of 3 and 56, respectively. If you set this option to [30, 50], it would  look for anything in the top 30% of that penetration range (Anything above 37.1 penetration), and would remove it from the bot's inventory. Then it would look for anything in the bottom 50% of that penetration range (anything below 26.5) and remove that, as well, leaving only ammunition with penetration values between 26.5 and 37.1, which in this example would be 855 and 856A1. If there are no ammunition options within this range, the mod will pick the highest-penetration round available that is still below the maximum allowed penetration. If there are no such rounds (Such as if you set it to [100, whatever]), then it will select the lowest-penetration round.","Original config name: healingItemsMin_Max\n\nThis entry should have two values, placed inside square brackets and separated by a comma. This entry is disabled by default. If you want this entry to function, you'll need to open the advanced inventory config and disable medical_inventory_changes_enabled.\n\nIf this entry (The one you're looking at right now, not the one in the other config) is enabled, then the first value is the minimum number of medical items that will appear in this bot type's inventory. The second value is the maximum number of medical items that will appear in this bot type's inventory.\n\nIf this entry is NOT enabled, and the advanced config is still responsible for generating the AI's medical loadout, then please check the advanced inventory config for more information. In particular, the print_out_medical_odds_on_startup function has been specifically made to help users configure those settings.","Original config name: magazinesInVestMin_Max\n\nThis entry should have two values, placed inside square brackets and separated by a comma. If this entry is enabled, then the first value is the minimum number of magazines that will appear in this bot type's inventory. The second value is the maximum number of magazines items that will appear in this bot type's inventory.\n\nNote: If an AI is spawned without a magazine in its weapon (As is generally the case with preset weapons, and some occasional errors) and they're only set to spawn with one magazine in their inventory, then they will not be able to refil this magazine after it's emptied, and will therefore 'run out of ammo' after that one magazine is used up.","Original config name: backpackSizeMin_Max\n\nThis entry should have two values, placed inside square brackets and separated by a comma. The first value determines the minimum size of backpack that this type of AI is able to spawn with. If the first value is set to -1, then the minimum size will be set to whatever the smallest backpack is in their default inventory. The second value determines the maximum size of backpack that this type of AI is able to spawn with.","Original config name: maxTacticalDevices\n\nThis entry determines how many tactical devices are allowed to spawn on the AI's primary weapon. This does not affect sidearms. This option isn't perfect, and sometimes an AI will slip through with more than the maximum number of tactical devices.","Original config name: maxPrimaryOptics\n\nThis entry determines how many optic devices are allowed to spawn on the AI's primary weapon. This does not affect sidearms. This option isn't perfect, and sometimes an AI will slip through with more than the maximum number of optic devices. This does not affect 'secondary' optics, such as those mounted ontop of other optics (IE: A reflex sight mounted ontop of a Bravo scope, etc.).","Original config name: useWeaponsFromOtherMods\n\nIf set to true, then this type of AI will be allowed to spawn with any mod-added weapons that are within its 'rarity' settings. If set to false, then this type of AI will be limited to spawning with only vanilla weapons.","Original config name: removeWeaponTypesSemiAutoPumpBolt\n\nThis entry should be enclosed in square brackets, with the values inside contained inside of quotes (Either single or double), and seperated by commas. There should not be a comma after the final value.\n\nThe available options are 'pump', 'bolt', 'semi', and 'auto', and all weapons of that type will be removed from that bot type's inventory.","Original config name: specificWeapons\n\nThis entry should be enclosed in square brackets, with the values inside contained inside of quotes (Either single or double), and seperated by commas.\n\nAny weapon IDs entered here will be added to the AI's potential inventory, regardless of any settings other than 'Change gear for this group'."]
specialGearOptions = []

mainGearSettings = ttk.Frame(tab3)
mainGearSettings.grid(column=0, row=0)
ttk.Label(mainGearSettings, text="Scavs:", font=("Arial", 15)).grid(column=1, row=0, padx=30, pady=5, sticky=E)
ttk.Label(mainGearSettings, text="Raiders:", font=("Arial", 15)).grid(column=2, row=0, padx=30, pady=5, sticky=E)
ttk.Label(mainGearSettings, text="PMCs:", font=("Arial", 15)).grid(column=3, row=0, padx=30, pady=5, sticky=E)
ttk.Label(mainGearSettings, text="AI Categories:", font=("Arial", 15)).grid(column=4, row=0, padx=30, pady=5)
gearSettingsNames = ttk.Frame(mainGearSettings)
gearSettingsNames.grid(column=0, row=1)
scavGearSettings = ttk.Frame(mainGearSettings)
scavGearSettings.grid(column=1, row=1)
raiderGearSettings = ttk.Frame(mainGearSettings)
raiderGearSettings.grid(column=2, row=1)
pmcGearSettings = ttk.Frame(mainGearSettings)
pmcGearSettings.grid(column=3, row=1)
botGearTypeSettings = ttk.Frame(mainGearSettings)
botGearTypeSettings.grid(column=4, row=1)

scavGearOpt = []
raidGearOpt = []
pmcGearOpt = []

row = 0
for opt in gearOptionNames:
    if opt in gearOptionNames: #unused
        optName = gearOptionName2[gearOptionNames.index(opt)]
    else:
        continue
    thisisatempvariable = ttk.Label(gearSettingsNames, text=optName)
    thisisatempvariable.grid(column=0, row=row, padx=30, pady=5, sticky=E)
    CreateToolTip(thisisatempvariable, gearTooltips[gearOptionNames.index(opt)])
    if opt in specialGearOptions:
        scavGearOpt.append(Text(scavGearSettings, wrap = "word", width=15, height = 3))
    else:
        scavGearOpt.append(Entry(scavGearSettings, width=15))
    scavGearOpt[len(scavGearOpt) - 1].grid(column=1, columnspan=1, row=row, padx=30, pady=5, sticky=W)
    scavGearOpt[len(scavGearOpt) - 1].insert(END, parseArray(config["AIgearChanges"]["scavs"][opt]))
    if opt in specialGearOptions:
        raidGearOpt.append(Text(raiderGearSettings, wrap = "word", width=15, height = 3))
    else:
        raidGearOpt.append(Entry(raiderGearSettings, width=15))
    raidGearOpt[len(raidGearOpt) - 1].grid(column=1, columnspan=1, row=row, padx=30, pady=5, sticky=W)
    raidGearOpt[len(raidGearOpt) - 1].insert(END, parseArray(config["AIgearChanges"]["raiders"][opt]))
    if opt in specialGearOptions:
        pmcGearOpt.append(Text(pmcGearSettings, wrap = "word", width=15, height = 3))
    else:
        pmcGearOpt.append(Entry(pmcGearSettings, width=15))
    pmcGearOpt[len(pmcGearOpt) - 1].grid(column=1, columnspan=1, row=row, padx=30, pady=5, sticky=W)
    pmcGearOpt[len(pmcGearOpt) - 1].insert(END, parseArray(config["AIgearChanges"]["PMCs"][opt]))
    row += 1
row = 0

botGearTypeSubSettings = ttk.Frame(botGearTypeSettings)
botGearTypeSubSettings.grid(column=0, row=0)
#row += 1
#ttk.Label(botGearTypeSubSettings,
#          text="AI categories:", font=("Arial", 15)).grid(column=0, row=row, padx=30, pady=5)
scavGearCat = makeTextEntry(botGearTypeSubSettings, "Scav bots:", 1, 2, 35, 8, config["AIgearChanges"]["scavBots"], "Original config name: scavBots\n\nThis entry should be enclosed in square brackets, with the values inside contained inside of quotes (Either single or double), and seperated by commas.\n\nAll AIs listed here will be considerd 'scavs' for the purposes of any options in this tab. Any given AI should not be present in more than one of these lists.")
raiderGearCat = makeTextEntry(botGearTypeSubSettings, "Raider bots:", 2, 2, 35, 8, config["AIgearChanges"]["raiderBots"], "Original config name: raiderBots\n\nThis entry should be enclosed in square brackets, with the values inside contained inside of quotes (Either single or double), and seperated by commas.\n\nAll AIs listed here will be considerd 'raiders' for the purposes of any options in this tab. Any given AI should not be present in more than one of these lists.")
pmcGearCat = makeTextEntry(botGearTypeSubSettings, "PMC bots:", 3, 2, 35, 8, config["AIgearChanges"]["pmcBots"], "Original config name: pmcBots\n\nThis entry should be enclosed in square brackets, with the values inside contained inside of quotes (Either single or double), and seperated by commas.\n\nAll AIs listed here will be considerd 'PMCs' for the purposes of any options in this tab. Any given AI should not be present in more than one of these lists.")
ttk.Label(botGearTypeSubSettings, text="Other:", font=("Arial", 15)).grid(column=0, columnspan = 3, row=4, padx=30, pady=5)
blackListGear = makeTextEntry(botGearTypeSubSettings, "Blacklisted items:", 5, 2, 35, 8, config["AIgearChanges"]["allAI"]["botsWillNotSpawnWithTheseThings"], "Original config name: botsWillNotSpawnWithTheseThings\n\nThis entry should be enclosed in square brackets, with the values inside contained inside of quotes (Either single or double), and seperated by commas.\n\nAny item IDs entered here will be prevented from spawning in AI inventories.")



botMiscGearSettings = ttk.Frame(botGearTypeSubSettings)
botMiscGearSettings.grid(column=0, columnspan = 3, row=15)

enableProgressiveGear =tk.BooleanVar()
makeRadioLabel(botMiscGearSettings, "Enable progressive gear:", 1, enableProgressiveGear, config["miscChanges"]["enableProgressiveGear"], "Original config name: enableProgressiveGear\n\nIf set to true, bot gear will automatically increase in quality as the player increases in level.\n\nNote: This overrides MANY of the other gear settings, and is intended to create a more punishing, scavenger-y experience.")
noVOGs =tk.BooleanVar()
makeRadioLabel(botMiscGearSettings, "Remove AI vog grenades:", 2, noVOGs, config["AIgearChanges"]["allAI"]["removeAIVogGrenades"], "Original config name: removeAIVogGrenades\n\nIf set to true, all VOG grenades will be removed from potential AI inventories")
noHats =tk.BooleanVar()
makeRadioLabel(botMiscGearSettings, "Remove non-helmet headwear:", 3, noHats, config["AIgearChanges"]["allAI"]["noHatsOnlyHelmets"], "Original config name: noHatsOnlyHelmets\n\nIf set to true, all non-armored pieces of headwear will be removed from potential AI inventories")
removeVanillaWeps =tk.BooleanVar()
makeRadioLabel(botMiscGearSettings, "Clear vanilla weapon loadouts:", 4, removeVanillaWeps, config["AIgearChanges"]["allAI"]["removeVanillaBotWeapons"], "Original config name: removeVanillaBotWeapons\n\nIf set to true, all weapons will be removed from potential AI inventories before other gear changes take effect. -This option gives you more control over precisely what weapons an AI can be equipped with, and it is recommended you leave this set to true.")
removeVanillaArmor =tk.BooleanVar()
makeRadioLabel(botMiscGearSettings, "Clear vanilla armor loadouts:", 5, removeVanillaArmor, config["AIgearChanges"]["allAI"]["removeVanillaBotArmor"], "Original config name: removeVanillaBotArmor\n\nIf set to true, all armor will be removed from potential AI inventories before other gear changes take effect. -This option gives you more control over precisely what armor an AI can be equipped with, and it is recommended you leave this set to true.")


#Spawns tab

mainSpawnSettings = ttk.Frame(tab4)
mainSpawnSettings.grid(column=0, row=0)

extraWaveSettings = ttk.Frame(mainSpawnSettings)
extraWaveSettings.grid(column=0, row=0)
spawnControlOptions = ttk.Frame(mainSpawnSettings)
spawnControlOptions.grid(column=1, row=0)

mainGearSettings.grid(column=0, row=0)
ttk.Label(extraWaveSettings, text="Extra waves: ", font=("Arial", 15)).grid(column=0, row=0, padx=30, pady=5, sticky=N)
ttk.Label(extraWaveSettings, text=" ").grid(column=0, row=1, padx=30, pady=5, sticky=N)
scavWaves = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Scav waves:",2, 1, config["spawnChanges"]["extraWaves"]["scavWaves"], 5, "Original config name: scavWaves\n\nThis is the number of extra scav waves that will be added to your raids.")
scavWavesMin = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Minimum wave size: ",3, 1, config["spawnChanges"]["extraWaves"]["scavWaveSizeMin"], 5, "Original config name: scavWaveSizeMin\n\nThis is the minimum number of scavs that will be in the extra waves of scavs.")
scavWavesMax = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Maximum wave size: ",4, 1, config["spawnChanges"]["extraWaves"]["scavWaveSizeMax"], 5, "Original config name: scavWaveSizeMax\n\nThis is the maximum number of scavs that will be in the extra waves of scavs")
ttk.Label(extraWaveSettings, text=" ").grid(column=0, row=5, padx=30, pady=5, sticky=N)

raiderWaves = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Raider waves:",6, 1, config["spawnChanges"]["extraWaves"]["raiderWaves"], 5, "Original config name: raiderWaves\n\nThis is the number of extra raider waves that will be added to your raids.")
raiderWavesMin = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Minimum wave size: ",7, 1, config["spawnChanges"]["extraWaves"]["raiderWaveSizeMin"], 5, "Original config name: raiderWaveSizeMin\n\nThis is the minimum number of raiders that will be in the extra waves of raiders.")
raiderWavesMax = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Maximum wave size: ",8, 1, config["spawnChanges"]["extraWaves"]["raiderWaveSizeMax"], 5, "Original config name: raiderWaveSizeMax\n\nThis is the maximum number of raiders that will be in the extra waves of raiders.")
ttk.Label(extraWaveSettings, text=" ").grid(column=0, row=9, padx=30, pady=5, sticky=N)

pmcWaves = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Pmc waves:",10, 1, config["spawnChanges"]["extraWaves"]["PMCWaves"], 5, "Original config name: PMCWaves\n\nThis is the number of extra PMC waves that will be added to your raids.")
pmcWavesMin = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Minimum wave size: ",11, 1, config["spawnChanges"]["extraWaves"]["PMCWaveSizeMin"], 5, "Original config name: PMCWaveSizeMin\n\nThis is the minimum number of PMCs that will be in the extra waves of PMCs.")
pmcWavesMax = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Maximum wave size: ",12, 1, config["spawnChanges"]["extraWaves"]["PMCWaveSizeMax"], 5, "Original config name: PMCWaveSizeMax\n\nThis is the maximum number of PMCs that will be in the extra waves of PMCs.")
ttk.Label(extraWaveSettings, text=" ").grid(column=0, row=13, padx=30, pady=5, sticky=N)

gluharWaves = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Gluhar waves:",14, 1, config["spawnChanges"]["extraWaves"]["gluharRaiderWaves"], 5, "Original config name: gluharRaiderWaves\n\nThis is the number of extra gluhar's raider waves that will be added to your raids.")
gluharWavesMin = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Minimum wave size: ",15, 1, config["spawnChanges"]["extraWaves"]["gluharRaiderWaveSizeMin"], 5, "Original config name: gluharRaiderWaveSizeMin\n\nThis is the minimum number of gluhar's raiders that will be in the extra waves of gluhar's raiders.")
gluharWavesMax = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Maximum wave size: ",16, 1, config["spawnChanges"]["extraWaves"]["gluharRaiderWaveSizeMax"], 5, "Original config name: gluharRaiderWaveSizeMax\n\nThis is the maximum number of gluhar's raiders that will be in the extra waves of gluhar's raiders.")
ttk.Label(extraWaveSettings, text=" ").grid(column=0, row=17, padx=30, pady=5, sticky=N)

cultistWaves = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Cultist waves:",18, 1, config["spawnChanges"]["extraWaves"]["OnlyWorksProperlyWhenTaggedAndCursedIsEnabled"]["cultistWaves"], 5, "Original config name: cultistWaves\n\nThis is the number of extra cultist waves that will be added to your raids.")
cultistWavesMin = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Minimum wave size: ",19, 1, config["spawnChanges"]["extraWaves"]["OnlyWorksProperlyWhenTaggedAndCursedIsEnabled"]["cultistWaveSizeMin"], 5, "Original config name: cultistWaveSizeMin\n\nThis is the minimum number of cultists that will be in the extra waves of cultists.")
cultistWavesMax = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Maximum wave size: ",20, 1, config["spawnChanges"]["extraWaves"]["OnlyWorksProperlyWhenTaggedAndCursedIsEnabled"]["cultistWaveSizeMax"], 5, "Original config name: cultistWaveSizeMax\n\nThis is the maximum number of cultists that will be in the extra waves of cultists.")
cultistWavesChance = makeLabelEntryCUSTOM(extraWaveSettings, E, W, 30, 30, "Spawn chance per wave: ",21, 1, config["spawnChanges"]["extraWaves"]["OnlyWorksProperlyWhenTaggedAndCursedIsEnabled"]["waveSpawnChancePCT0_100"], 5, "Original config name: waveSpawnChancePCT0_100\n\nThis is the percent chance for each of the extra cultist waves to actually appear in your raids.")
ttk.Label(extraWaveSettings, text=" ").grid(column=0, row=22, padx=30, pady=5, sticky=N)

ttk.Label(spawnControlOptions, text="Spawning options: ", font=("Arial", 15)).grid(column=0, row=0, padx=30, pady=5, sticky=N)
ttk.Label(spawnControlOptions, text="  ").grid(column=0, row=1, padx=30, pady=5, sticky=N)

#changeHeadHPValues =tk.BooleanVar()
#makeRadioLabel(overallDiffSettings, "Allow health multipliers to affect heads:", 6, changeHeadHPValues, config["overallDifficultyMultipliers"]["changeHeadHPValues"])
#setTheseBotsToDefaultPlayerHPBeforeMultsAreUsed = makeTextEntry(overallDiffSettings, "Give these bots player HP values before multipliers:", 7, 5, 50, 3, config["overallDifficultyMultipliers"]["setTheseBotsToDefaultPlayerHPBeforeMultsAreUsed"])
#PMCHealthMult =makeLabelEntry(overallDiffSettings, "AI PMC health multiplier:",8, 1, config["overallDifficultyMultipliers"]["PMCHealthMult"])
row = 2
allBears =tk.BooleanVar()
makeRadioLabel(spawnControlOptions, "All PMCs are BEARs: ", row, allBears, config["spawnChanges"]["controlOptions"]["allPMCsareBEARs"], "Original config name: allPMCsareBEARs\n\nIf enabled, all PMCs will be spawned as BEARs.")
row += 1

allUsecs =tk.BooleanVar()
makeRadioLabel(spawnControlOptions, "All PMCs are USECs: ", row, allUsecs, config["spawnChanges"]["controlOptions"]["allPMCsareUSECs"], "Original config name: \n\nIf enabled, all PMCs will be spawned as USECs.\n\nNote: If both this option and the above option are set to 'True', then the BEARs setting will take priority, and all PMCs will be spawned as BEARs.")
row += 1

#pmcsAsBosses =tk.BooleanVar()
#makeRadioLabel(spawnControlOptions, "Spawn PMCs as bosses: ", row, pmcsAsBosses, config["spawnChanges"]["controlOptions"]["spawnPMCsAsBosses"], "Original config name: spawnPMCsAsBosses\n\nSetting this to true forces PMCs to be spawned via the boss spawning system. This was originally used as a fix for certain issues, but is mostly obsolete in the current version.\n\nI recommend this option be left on 'False' unless you have a good reason to do otherwise.")
#row += 1

breakUpWaves =tk.BooleanVar()
makeRadioLabel(spawnControlOptions, "Break up spawn waves: ", row, breakUpWaves, config["spawnChanges"]["controlOptions"]["breakUpSpawnWaves"], "Original config name: breakUpSpawnWaves\n\nSetting this option to true will break every wave of bots into multiple waves of just one bot. IE: Instead of spawning one wave of X many bots at location Y, and time Z, it will instead spawn X many waves of 1 bot at location Y, and time Z. This option exists to give you better control over the size of waves, because Tarkov will actually spawn approximately 50% more bots that you're expecting in a given wave.\n\nBy default, with 'As Online' rate of spawns, the game will spawn extra waves of any bot scheduled to spawn in the 'waves' entry of a given map. Specifically, it will spawn a wave with the same min and max spawn times, bot type, and potential locations, but with a wave size equal to the parent wave's size times 0.5, truncated. IE: One wave of 5 scavs will actually be spawned in as a wave of 5, and a wave of 2. 10 would become 10 and 5. 3 would become 3 and 1. Etc. This option prevents that behaviour.")
row += 1

labrats =tk.BooleanVar()
makeRadioLabel(spawnControlOptions, "Replace scavs in labs with raiders: ", row, labrats, config["spawnChanges"]["controlOptions"]["noScavsInLabs"], "Original config name: noScavsInLabs\n\nSetting this option to true will replace all scavs that would be spawned in labs (Note: In vanilla AKI, there are only raiders in labs. All scavs appearing in labs are there because a mod added them) will instead spawn as raiders.")
row += 1

clearVanillaSpawns =tk.BooleanVar()
makeRadioLabel(spawnControlOptions, "Remove default non-PMC spawns: ", row, clearVanillaSpawns, config["spawnChanges"]["controlOptions"]["removeVanillaSpawns"], "Original config name: removeVanillaSpawns\n\nSetting this option to true will remove all vanilla non-sniper non-PMC spawns (IE: Scavs, raiders, etc.), before the spawns in the 'Extra Waves' section are added. This option exists to give the player more control over the exact number of bots in their raids, but it is generally recommended that this be left set to 'False', and the advanced spawn config be used instead. The advanced spawn config is more complex, but it's also a better tool for the task.")
row += 1

clearVanillaPMCSpawns =tk.BooleanVar()
makeRadioLabel(spawnControlOptions, "Remove default PMC spawns: ", row, clearVanillaPMCSpawns, config["spawnChanges"]["controlOptions"]["removeVanillaPMCSpawns"], "Original config name: removeVanillaPMCSpawns\n\nSetting this option to true will remove all vanilla PMC spawns, before the spawns in the 'Extra Waves' section are added. This option exists to give the player more control over the exact number of bots in their raids, but it is generally recommended that this be left set to 'False', and the advanced spawn config be used instead. The advanced spawn config is more complex, but it's also a better tool for the task.")
row += 1

clearSniperScavs =tk.BooleanVar()
makeRadioLabel(spawnControlOptions, "Remove sniper scavs: ", row, clearSniperScavs, config["spawnChanges"]["controlOptions"]["removeSniperBots"], "Original config name: removeSniperBots\n\nSetting this option to true will remove all sniper scavs from your raids.")
row += 1

autoPMCs =tk.BooleanVar()
makeRadioLabel(spawnControlOptions, "Automatically add PMCs: ", row, autoPMCs, config["spawnChanges"]["controlOptions"]["autoAddPMCsBasedOnMapSize"], "Original config name: autoAddPMCsBasedOnMapSize\n\nSetting this option to true will automatically add PMCs to your raids based on the size of the map.")
row += 1

evenSpreadSpawns =tk.BooleanVar()
makeRadioLabel(spawnControlOptions, "Evenly spread spawn locations: ", row, evenSpreadSpawns, config["spawnChanges"]["controlOptions"]["evenlySpreadAllSpawnLocations"], "Original config name: evenlySpreadAllSpawnLocations\n\nSetting this option to true spreads all spawns evenly across the map's various zones. This helps to ensure an even distribution of enemies, and avoid 'clumping' behaviour.")
row += 1

ttk.Label(spawnControlOptions, text="").grid(column=0, row=row, padx=30, pady=5, sticky=E)
row += 1


scavReplace = makeLabelEntry(spawnControlOptions, "Replace X% of scavs with raiders: ",row , 3, config["spawnChanges"]["controlOptions"]["scavReplaceWithRaidersPCT0_100"], "Original config name: scavReplaceWithRaidersPCT0_100\n\nThis option gives every wave of scavs a percentage chance to be replaced by raiders.\n\nNote: This isn't precise. If you enter '50', then it won't replace 50% of scavs with raiders. Instead, each wave is given a 50% chance to be converted. So for example, if there are only three waves of scavs set to spawn, then there is a 1/8 chance that ALL the waves will be converted, a 3/8 chance that two of the waves will be converted, a 3/8 chance that only one of the waves will be converted, and 1/8 chance that NONE of the waves will be converted.")
row += 1

scavReplacePMC = makeLabelEntry(spawnControlOptions, "Replace X% of scavs with PMCs: ",row , 3, config["spawnChanges"]["controlOptions"]["scavReplaceWithPMCsPCT0_100"], "Original config name: scavReplaceWithPMCsPCT0_100\n\nThis option gives every wave of scavs a percentage chance to be replaced by PMCs.\n\nNote: This isn't precise. If you enter '50', then it won't replace 50% of scavs with PMCs. Instead, each wave is given a 50% chance to be converted. So for example, if there are only three waves of scavs set to spawn, then there is a 1/8 chance that ALL the waves will be converted, a 3/8 chance that two of the waves will be converted, a 3/8 chance that only one of the waves will be converted, and 1/8 chance that NONE of the waves will be converted.\n\nNote: This chance is applied AFTER the above entry. This means that it will only be applied to the scavs that were not converted to raiders. IE: If you set both entires to 50, then on average you'd have raids that were 50% raiders, 25% scavs, and 25% PMCs.")
row += 1

replaceVanillaOnly =tk.BooleanVar()
makeRadioLabel(spawnControlOptions, "Replace only vanilla scavs: ", row, replaceVanillaOnly, config["spawnChanges"]["controlOptions"]["scavReplaceWith___AffectsOnlyVanillaWaves"], "Original config name: scavReplaceWith___AffectsOnlyVanillaWaves\n\nIf set to true, then the above two options are only applied to vanilla scav waves, and NOT to waves of scavs spawned via this mod's 'Extra Waves' section.")
row += 1

extraVanillaScavs = makeLabelEntry(spawnControlOptions, "Extra scavs added to vanilla waves: ",row , 3, config["spawnChanges"]["controlOptions"]["extraScavsPerVanillaWave"], "Original config name: extraScavsPerVanillaWave\n\nThis entry alters the size of vanilla scav waves, either adding or subtracting the entered value. Note: This cannot take a wave below a minimum size of 1.")
row += 1

pSC = ttk.Label(spawnControlOptions, text="Prevent spawn changes to maps: ", font=("Arial", 12))
pSC.grid(column=0, columnspan = 2, row=50, padx=30, pady=5, sticky=W)
CreateToolTip(pSC, "Original config name: doNotChangeTheseSPECIFICMaps\n\nThis entry should be enclosed in square brackets, with the values inside contained inside of quotes (Either single or double), and seperated by commas. There should not be a comma after the final value.\n\nAny map names entered here will NOT have their spawns altered in any way by this mod, unless the advanced spawn config is enabled.\n\nMap names are:\nCustoms = bigmap\nWoods = woods\nReserve = rezervbase\nShoreline = shoreline\nFactory (day) = factory4_day\nFactory (night) = factory4_night\nInterchange = interchange\nLabs = laboratory")

doNotChangeMaps = Text(spawnControlOptions, wrap = "word", width=50, height = 3)
doNotChangeMaps.grid(column=0, columnspan = 2, row=51, padx=30, pady=5, sticky = E)
doNotChangeMaps.insert(END, parseArray(config["spawnChanges"]["controlOptions"]["doNotChangeTheseSPECIFICMaps"]))

spawnTimingOptions = ttk.Frame(mainSpawnSettings)
spawnTimingOptions.grid(column=2, row=0)

ttk.Label(spawnTimingOptions, text="Spawn timing options: ", font=("Arial", 15)).grid(column=0, columnspan = 1, row=0, padx=30, pady=5, sticky=N)

evenSpreadSpawnTimes =tk.BooleanVar()
makeRadioLabel(spawnTimingOptions, "Evenly space spawn times over raid duration: ", 1, evenSpreadSpawnTimes, config["spawnChanges"]["spawnTiming"]["evenlySpaceAllSpawnTimes"], "Original config name: evenlySpaceAllSpawnTimes\n\nIf set to true, this option will evenly spread the spawn times of all waves across the duration of your raid. If you had 45 waves set to spawn, then it would spawn one every minute. -90 waves, and it would spawn one every thirty seconds (This is independant of the actual ingame length of a raid. It's set to assume all raids are 45 minutes long, except for Factory which is set for about 15)")
spawnImmediately =tk.BooleanVar()
makeRadioLabel(spawnTimingOptions, "Spawn bots in 'Extra Waves' at the start of the raid: ", 2, spawnImmediately, config["spawnChanges"]["spawnTiming"]["spawnExtraWavesImmediately"], "Original config name: spawnExtraWavesImmediately\n\nIf set to true, this option sets all waves in the 'Extra Waves' section of the config to spawn at the start of the raid, instead of at a random time")
spawnImmediately2 =tk.BooleanVar()
makeRadioLabel(spawnTimingOptions, "Spawn ALL bots at the start of the raid: ", 3, spawnImmediately2, config["spawnChanges"]["spawnTiming"]["spawn_ALL_WavesImmediately"], "Original config name: spawn_ALL_WavesImmediately\n\nIf set to true, this option sets ALL waves to spawn at the start of the raid, instead of at a random time.")

speedFactorCap = makeLabelEntryCUSTOM(spawnTimingOptions, W, W, 30, 30, "Bias spawns towards max spawn time of X seconds:",4, 3, config["spawnChanges"]["spawnTiming"]["extraWaveSpawnSpeedFactorMaxTime"], 8, "Original config name: extraWaveSpawnSpeedFactorMaxTime\n\nThis determines the maximum time at which a bot could spawn if extraWaveSpawnSpeedFactor were set to an infinitely high value. In practical terms, this is where the absolute max time will 'cap out' at. Actual spawn times will average out to approximately 1/4 of this value.")
speedFactor = makeLabelEntryCUSTOM(spawnTimingOptions, W, W, 30, 30, "Level of bias (-1 to disable): ",5, 3, config["spawnChanges"]["spawnTiming"]["extraWaveSpawnSpeedFactor"], 8, "Original config name: extraWaveSpawnSpeedFactor\n\nThis value causes spawn times to be biased towards the spawn time set in the above entry. This value's effect is not linear (IE: There's a greater difference between 1 and 2 than there is between 10 and 11), but the higher it is the earlier extra waves of bots will spawn in your raids, with the average spawn time moving asymptotically towards a value approximately 1/3rd of the above entry's value.\n\nNote: If you set this to a negative number, this effect is completely disabled. This option is also disabled is spawnExtraWavesImmediately or spawnALLwavesImmediately are enabled.")

#Misc tab

mainMiscSettings = ttk.Frame(tab5)
mainMiscSettings.grid(column=0, row=0)
talkSettings = ttk.Frame(mainMiscSettings)
talkSettings.grid(column=0, row=0)

ttk.Label(talkSettings, text="Allow bots to talk: ", font=("Arial", 15), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=0, padx=30, pady=3, sticky=W)
ttk.Label(talkSettings,
          text="False:").grid(column=1, row=1, padx=30, pady=5)
ttk.Label(talkSettings,
          text="True:").grid(column=2, row=1, padx=30, pady=5)
sTalk = ttk.Label(talkSettings, text="Scavs: ")
sTalk.grid(column=0, row=2, padx=30, pady=5)
rTalk = ttk.Label(talkSettings, text="Raiders:")
rTalk.grid(column=0, row=3, padx=30, pady=5)
pTalk = ttk.Label(talkSettings, text="PMCs:")
pTalk.grid(column=0, row=4, padx=30, pady=5)
CreateToolTip(sTalk, "Original config name: allowBotsToTalk: scavs\n\nIf set to false, scavs will not be allowed to talk. Note: Bots cannot talk and shoot at the same time, so enabling this will also make them take longer to attack.")
CreateToolTip(rTalk, "Original config name: allowBotsToTalk: raiders\n\nIf set to false, raiders will not be allowed to talk. Note: Bots cannot talk and shoot at the same time, so enabling this will also make them take longer to attack.")
CreateToolTip(pTalk, "Original config name: allowBotsToTalk: PMCs\n\nIf set to false, PMCs will not be allowed to talk. Note: Bots cannot talk and shoot at the same time, so enabling this will also make them take longer to attack.")
scavTalk = tk.BooleanVar()
raiderTalk = tk.BooleanVar()
pmcTalk = tk.BooleanVar()
ttk.Radiobutton(talkSettings,
            text="", variable = scavTalk, value = False
            ).grid(column=1, row=2, padx=30, pady=5)
ttk.Radiobutton(talkSettings,
            text="", variable = scavTalk, value = True
            ).grid(column=2, row=2, padx=30, pady=5)
ttk.Radiobutton(talkSettings,
            text="", variable = raiderTalk, value = False
            ).grid(column=1, row=3, padx=30, pady=5)
raiderTalkRad2 = ttk.Radiobutton(talkSettings,
            text="", variable = raiderTalk, value = True
            ).grid(column=2, row=3, padx=30, pady=5)
ttk.Radiobutton(talkSettings,
            text="", variable = pmcTalk, value = False
            ).grid(column=1, row=4, padx=30, pady=5)
ttk.Radiobutton(talkSettings,
            text="", variable = pmcTalk, value = True
            ).grid(column=2, row=4, padx=30, pady=5)
if config["allowBotsToTalk"]["scavs"] is False:
    scavTalk.set(False)
else:
    scavTalk.set(True)
if config["allowBotsToTalk"]["raiders"] is False:
    raiderTalk.set(False)
else:
    raiderTalk.set(True)
if config["allowBotsToTalk"]["PMCs"] is False:
    pmcTalk.set(False)
else:
    pmcTalk.set(True)

loadSettings = ttk.Frame(mainMiscSettings)
loadSettings.grid(column=0, row=1)

ttk.Label(loadSettings, text="Gear file options: ", font=("Arial", 15), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=0, padx=30, pady=3, sticky=W)
ttk.Label(loadSettings,
          text="False:").grid(column=1, row=1, padx=30, pady=5)
ttk.Label(loadSettings,
          text="True:").grid(column=2, row=1, padx=30, pady=5)
oL1 = ttk.Label(loadSettings, text="optimized loading (overrides other options): ")
oL1.grid(column=0, row=2, padx=30, pady=5)
oL2 = ttk.Label(loadSettings, text="Save gear to file (for manual editing):")
oL2.grid(column=0, row=3, padx=30, pady=5)
oL3 = ttk.Label(loadSettings, text="Load gear from file (for manual editing):")
oL3.grid(column=0, row=4, padx=30, pady=5)
CreateToolTip(oL1, "Original config name: optimizedLoading\n\nIf set to true, this option tells the mod to automatically check to see if either the gear section of the config, the player's list of mods (By the names of the folders in their mod directory), or the bot's main (non-mod) inventory has changed, and if they haven't it loads the bot from its file instead of generating it from scratch.\n\nIf you're customizing the weapon mods a bot can use, turn this off, but otherwise it is recommended this option be left set to 'true'.")
CreateToolTip(oL2, "Original config name: saveGearToFile\n\nIf set to true, this option will set the mod to, once it has finished generating the inventories of its bots, save their inventories to files in bot_inventories.\n\nNote: If 'Load gear from file' is set to true, this option is disabled internally for ease-of-use purposes.")
CreateToolTip(oL3, "Original config name: loadGearFromFile\n\nIf set to true, this option tells the mod to access the saved gear files, and skip the usual steps in generating a bot's inventory. This allows you to make manual changes to the generated inventories, such as removing specific weapons or mods, or can be used if you want to skip bot generation for some other reason. If all nine (Currently. More may be added if I set the mod to alter more bot inventories) inventory files are not present and loadGearFromFile is enabled, the mod will not function properly.")
optimizeLoad = tk.BooleanVar()
loadFile = tk.BooleanVar()
saveFile = tk.BooleanVar()
ttk.Radiobutton(loadSettings,
            text="", variable = optimizeLoad, value = False
            ).grid(column=1, row=2, padx=30, pady=5)
ttk.Radiobutton(loadSettings,
            text="", variable = optimizeLoad, value = True
            ).grid(column=2, row=2, padx=30, pady=5)
ttk.Radiobutton(loadSettings,
            text="", variable = saveFile, value = False
            ).grid(column=1, row=3, padx=30, pady=5)
raiderTalkRad2 = ttk.Radiobutton(loadSettings,
            text="", variable = saveFile, value = True
            ).grid(column=2, row=3, padx=30, pady=5)
ttk.Radiobutton(loadSettings,
            text="", variable = loadFile, value = False
            ).grid(column=1, row=4, padx=30, pady=5)
ttk.Radiobutton(loadSettings,
            text="", variable = loadFile, value = True
            ).grid(column=2, row=4, padx=30, pady=5)
if config["AIgearChanges"]["CheckREADMEforInfo"]["optimizedLoading"] is False:
    optimizeLoad.set(False)
else:
    optimizeLoad.set(True)
if config["AIgearChanges"]["CheckREADMEforInfo"]["saveGearToFile"] is False:
    saveFile.set(False)
else:
    saveFile.set(True)
if config["AIgearChanges"]["CheckREADMEforInfo"]["loadGearFromFile"] is False:
    loadFile.set(False)
else:
    loadFile.set(True)

miscSettings = ttk.Frame(mainMiscSettings)
miscSettings.grid(column=1, row=0)
ttk.Label(miscSettings, text="Miscellaneous options: ", font=("Arial", 15), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=0, padx=30, pady=3, sticky=W)

ttk.Label(miscSettings,
          text="False:").grid(column=1, row=1, padx=30, pady=5)
ttk.Label(miscSettings,
          text="True:").grid(column=2, row=1, padx=30, pady=5)
fID = ttk.Label(miscSettings, text="Identify factions with armbands: ")
fID.grid(column=0, row=2, padx=30, pady=5)
aIORig = ttk.Label(miscSettings, text="Allow bots to use both armor rigs and vests:")
aIORig.grid(column=0, row=3, padx=30, pady=5)
CreateToolTip(fID, "Original config name: factionIdentifiers\n\nIf set to true, this gives each different type of bot a different colour of armband.\n\nNote: Some pieces of clothing, particularly raider's clothing, can hide armbands from view.\n\nUSEC = blue\n\nBEAR = red\n\nScav = green\n\nRaider = yellow\n\nGluhar's Raiders = white\n\nOther boss minions = purple")
CreateToolTip(aIORig, "Original config name: AllowAIToUseBothArmoredVestsAndArmoredRigs\n\nThis option is intended to be used in conjunction with AIO's option that allows both armored rigs and armored vests to be used at the same time. If set to true, this option prevents AIs from using both armored rigs and armored vests (By removing their armored vest, if they spawn with an armored rig and an armored vest), while still allowing players to use both armored rigs and armored vests.")
factionIdentifiers = tk.BooleanVar()
rigsAndVests = tk.BooleanVar()
ttk.Radiobutton(miscSettings,
            text="", variable = factionIdentifiers, value = False
            ).grid(column=1, row=2, padx=30, pady=5)
ttk.Radiobutton(miscSettings,
            text="", variable = factionIdentifiers, value = True
            ).grid(column=2, row=2, padx=30, pady=5)
ttk.Radiobutton(miscSettings,
            text="", variable = rigsAndVests, value = False
            ).grid(column=1, row=3, padx=30, pady=5)
raiderTalkRad2 = ttk.Radiobutton(miscSettings,
            text="", variable = rigsAndVests, value = True
            ).grid(column=2, row=3, padx=30, pady=5)
if config["AIgearChanges"]["allAI"]["factionIdentifiers"] is False:
    factionIdentifiers.set(False)
else:
    factionIdentifiers.set(True)
if config["AIgearChanges"]["miscChanges"]["requiresOtherMods"]["AIO"]["AllowAIToUseBothArmoredVestsAndArmoredRigs"] is False:
    rigsAndVests.set(False)
else:
    rigsAndVests.set(True)

sillySettings = ttk.Frame(mainMiscSettings)
sillySettings.grid(column=1, row=1)

ttk.Label(sillySettings, text="Silly options: ", font=("Arial", 15), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=0, padx=30, pady=3, sticky=W)

ttk.Label(sillySettings,
          text="False:").grid(column=1, row=1, padx=30, pady=5)
ttk.Label(sillySettings,
          text="True:").grid(column=2, row=1, padx=30, pady=5)

tracerBullets = tk.BooleanVar()
makeRadioButton(sillySettings, "All bullets are tracers: ", 2, tracerBullets, config["sillyChanges"]["allBulletsAreTracers"], "Original config name: allBulletsAreTracers\n\nIf set to true, this option adds randomly coloured tracers to all bullets.")
tracerGrenades = tk.BooleanVar()
makeRadioButton(sillySettings, "Grenade shrapnel has tracers: ", 3, tracerGrenades, config["sillyChanges"]["tracerGrenades"], "Original config name: tracerGrenades\n\nIf set to true, this option adds randomly coloured tracers to all grenade shrapnel. It's basically fireworks, and if you turn this off I'll be sad.")
pumpkinCultists = tk.BooleanVar()
makeRadioButton(sillySettings, "Pumpkin cultists enabled: ", 4, pumpkinCultists, config["sillyChanges"]["pumpkinCultists"], "Original config name: pumpkinCultists\n\nIf set to true, this option forces every cultist to wear the halloween pumpkin helmet. It's awesome, but it does completely ruin their ability to hide from you.")
codMode = tk.BooleanVar()
makeRadioButton(sillySettings, "COD mode enabled: ", 5, codMode, config["sillyChanges"]["COD_mode"], "Original config name: COD_mode\n\nIf set to true, this option sets the HP of all your bodyparts to 200, and gives you a special injector that will reduce all non-headshot damage taken by 50% and heal you for 25 HP per second for 24 IRL hours. This injector is placed in your inventory at the end of a raid, or when you start the game up. You can only have one injector in your inventory or stash at any given time.\n\nNote: COD mode cannot be applied to newly-created characters. Once you've made your character (Picked a name, face, voice and faction), you need to exit the game and restart the server for everything to work properly.")
codModeHP = makeLabelEntryCUSTOM(sillySettings, W, W, 30, 30, "COD mode HP per limb: ",6, 3, config["sillyChanges"]["COD_modeMaxHPPerLimb"], 5, "Original config name: COD_modeMaxHPPerLimb\n\nIf the above option is enabled, then this value will be used to set the HP each of your limbs will have. \n\nNOTE: If you want to modify player health by editing your profile, you will need to set this value to -1 in order for those changes to be applied. ..Yes, I know this is a weird place to put that information, but I don't have a better one at the moment.")
gravestone = tk.BooleanVar()
makeRadioButton(sillySettings, "Gravestone enabled (Visible in the server): ", 7, gravestone, config["sillyChanges"]["gravestone"], "Original config name: gravestone\n\nIf enabled, this option will print a randomly generated ASCII gravestone in your server window when you die in a raid. At the moment the text is unrelated to your actual cause of death, but I hope to expand on it in the future. I think this option is terribly cute, so it's enabled by default. If you have any fun ideas for more epitaphs, feel free to let me know either in the mod's comments, or on the Discord!")

serverSettings = ttk.Frame(mainMiscSettings)
serverSettings.grid(column=0, row=2)

ttk.Label(serverSettings, text="Server options: ", font=("Arial", 15), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=0, padx=30, pady=3, sticky=W)

overrideServerTime =tk.BooleanVar()
makeRadioLabel(serverSettings, "Start server at a specific time: ", 1, overrideServerTime, config["miscChanges"]["startServerAtSpecificTime"], "Original config name: startServerAtSpecificTime\n\nIf enabled, this option will force the server to start at a specific time, instead of at a time based on your computer's clock.")
startTime = makeLabelEntryCUSTOM(serverSettings, W, W, 30, 30, "Server start time  (Must be in 23:10:59 format): ",2, 2, config["miscChanges"]["startTime"], 15, "Original config name: startTime\n\nIf the above option is enabled, then this value will be used as your game's starting time.")
timeSpeed = makeLabelEntryCUSTOM(serverSettings, W, W, 30, 30, "Time acceleration (Default is 7): ",3, 2, config["miscChanges"]["timeAcceleration"], 15, "Original config name: timeAcceleration\n\nThis entry determines how quickly ingame time will move. EFT's default value is 7, meaning that time will move 7 times faster than it will IRL.")

mapMultsMenu = ttk.Frame(mainMiscSettings)
mapMultsMenu.grid(column=1, row=2)

mSM = ttk.Label(mapMultsMenu, text="Map spawn multipliers: ", font=("Arial", 15), anchor = N, justify = LEFT)
mSM.grid(column=0, columnspan = 1, row=0, padx=30, pady=3, sticky=W)
CreateToolTip(mSM, "Original config name: mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact\n\nThese values multiply the spawns on any given map. They can be used to either increase or decrease the number of bots that will be present.\n\nNote: These values aren't exact. The entered value is used to give every bot a random chance to not appear (This is oversimplifying, but if you really want details you'll need to crack open mod.js and look at the function yourself.). What this means in a practical sense is that a multiplier of 0.5 will probably actually give you somewhere between 45%-55% of the 'standard' bot count, not a perfect 50%. These multipliers cannot stop bosses from appearing, but they can reduce or increase their follower count.")

interchangeMult = makeLabelEntry(mapMultsMenu, "Interchange multiplier: ",1, 1, config["spawnChanges"]["controlOptions"]["mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact"][0], "This is the multiplier for Interchange.")
customsMult = makeLabelEntry(mapMultsMenu, "Customs multiplier: ",2, 1, config["spawnChanges"]["controlOptions"]["mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact"][1], "This is the multiplier for Customs.")
reserveMult = makeLabelEntry(mapMultsMenu, "Reserve multiplier: ",3, 1, config["spawnChanges"]["controlOptions"]["mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact"][2], "This is the multiplier for Reserve.")
woodsMult = makeLabelEntry(mapMultsMenu, "Woods multiplier: ",4, 1, config["spawnChanges"]["controlOptions"]["mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact"][3], "This is the multiplier for Woods.")
shoreMult = makeLabelEntry(mapMultsMenu, "Shoreline multiplier: ",5, 1, config["spawnChanges"]["controlOptions"]["mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact"][4], "This is the multiplier for Shore.")
labsMult = makeLabelEntry(mapMultsMenu, "Labs multiplier: ",6, 1, config["spawnChanges"]["controlOptions"]["mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact"][5], "This is the multiplier for Labs.")
factoryMult = makeLabelEntry(mapMultsMenu, "Factory multiplier: ",7, 1, config["spawnChanges"]["controlOptions"]["mapMultipliers_Inter_Cust_Resrv_Woods_Shore_Lab_Fact"][6], "This is the multiplier for Factory (Both day and night).")


#Debug tab

mainDebugSettings = ttk.Frame(tab6)
mainDebugSettings.grid(column=0, row=0)

ttk.Label(mainDebugSettings, text="Debug options: ", font=("Arial", 15), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=0, padx=30, pady=3, sticky=W)

ttk.Label(mainDebugSettings, text="Available options are: A valid map name (bigmap, etc.), 'all', or 'false'", anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=1, padx=30, pady=3, sticky=W)
showSpawns = makeLabelEntryCUSTOM(mainDebugSettings, W, W, 30, 30, "Show non-boss spawns for ___ map: ",2, 1, config["debug"]["showSpawns"], 15, "Original config name: showSpawns\n\nThis option determines which (If any) maps will have their base wave information either saved to the debug files, or printed in the server, depending on what 'Save debug files' is set to.\n\nThe available entries for this option are either a valid map name, all, or false (No quotes are needed for any entries).\n\nMap names are:\nCustoms = bigmap\nWoods = woods\nReserve = rezervbase\nShoreline = shoreline\nFactory (day) = factory4_day\nFactory (night) = factory4_night\nInterchange = interchange\nLabs = laboratory")
showBosses = makeLabelEntryCUSTOM(mainDebugSettings, W, W, 30, 30, "Show boss spawns for ___ map: ",3, 1, config["debug"]["showBosses"], 15, "Original config name: showBosses\n\nThis option determines which (If any) maps will have their boss spawn information either saved to the debug files, or printed in the server, depending on what 'Save debug files' is set to.\n\nThe available entries for this option are either a valid map name, all, or false (No quotes are needed for any entries).\n\nMap names are:\nCustoms = bigmap\nWoods = woods\nReserve = rezervbase\nShoreline = shoreline\nFactory (day) = factory4_day\nFactory (night) = factory4_night\nInterchange = interchange\nLabs = laboratory")
showBossPMCs = makeLabelEntryCUSTOM(mainDebugSettings, W, W, 30, 30, "Show boss-style PMC spawns for ___ map: ",4, 1, config["debug"]["showBossPMCs"], 15, "Original config name: showBossPMCs\n\nThis option determines which (If any) maps will have their PMC wave information for PMCs set to spoawn as bosses (If the relevant option has been enabled. Which is probably hasn't.) either saved to the debug files, or printed in the server, depending on what 'Save debug files' is set to.\n\nThe available entries for this option are either a valid map name, all, or false (No quotes are needed for any entries).\n\nMap names are:\nCustoms = bigmap\nWoods = woods\nReserve = rezervbase\nShoreline = shoreline\nFactory (day) = factory4_day\nFactory (night) = factory4_night\nInterchange = interchange\nLabs = laboratory")
ttk.Label(mainDebugSettings, text="Available options are: A valid bot name (assault, etc.), 'all', or 'false'", anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=5, padx=30, pady=3, sticky=W)
showGuns = makeLabelEntryCUSTOM(mainDebugSettings, W, W, 30, 30, "Show available weapons for ___ bots: ",6, 1, config["debug"]["showGuns"], 15, "Original config name: showGuns\n\nThis option determines which (If any) bots will have their weapon information either saved to the debug files, or printed in the server, depending on what 'Save debug files' is set to.\n\nThe available entries for this option are either a valid bot name, all, or false (No quotes are needed for any entries).\n\nBot names are:\nScav = assault\nSniper = marksman\nRaider = pmcbot\nCultist leader = sectantpriest\nCultist = sectantwarrior\nPMC = assaultgroup\nReshala = bossbully\nGluhar = bossgluhar\nKilla = bosskilla\nShturman = bosskojaniy\nSanitar = bosssanitar\n\nBoss minions = followerbully, followergluharassault, followergluharscout, followergluharsecurity, followergluharsnipe, followerkojaniy, followersanitar")
showArmor = makeLabelEntryCUSTOM(mainDebugSettings, W, W, 30, 30, "Show available armor for ___ bots: ",7, 1, config["debug"]["showArmor"], 15, "Original config name: showArmor\n\nThis option determines which (If any) bots will have their armor information either saved to the debug files, or printed in the server, depending on what 'Save debug files' is set to.\n\nThe available entries for this option are either a valid bot name, all, or false (No quotes are needed for any entries).\n\nBot names are:\nScav = assault\nSniper = marksman\nRaider = pmcbot\nCultist leader = sectantpriest\nCultist = sectantwarrior\nPMC = assaultgroup\nReshala = bossbully\nGluhar = bossgluhar\nKilla = bosskilla\nShturman = bosskojaniy\nSanitar = bosssanitar\n\nBoss minions = followerbully, followergluharassault, followergluharscout, followergluharsecurity, followergluharsnipe, followerkojaniy, followersanitar")
showAmmo = makeLabelEntryCUSTOM(mainDebugSettings, W, W, 30, 30, "Show available ammo for ___ bots (Not working ATM): ",8, 1, config["debug"]["showAmmo"], 15, "Original config name: showAmmo\n\nThis option isn't properly functioning at the moment.")
ttk.Label(mainDebugSettings, text="", anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=9, padx=30, pady=3, sticky=W)
saveDebugFiles = makeLabelEntryCUSTOM(mainDebugSettings, W, W, 30, 30, "Save debug files: ",10, 1, config["debug"]["saveDebugFiles"], 10, "Original config name: saveDebugFiles\n\nIf set to true, debug files will be saved to the doNotTouch\\debug folder. If set to false, debug information will be printed in the server.")
reportWavesBeforeRaid = makeLabelEntryCUSTOM(mainDebugSettings, W, W, 30, 30, "Print waves in server before raid: ",11, 1, config["debug"]["reportWavesBeforeRaid"], 10, "Original config name: reportWavesBeforeRaid\n\nIf set to true, this option will give you a brief printout in the server of the waves set to spawn in your raid, when you load in to it.")

#FAQ tab

mainFAQ = ttk.Frame(tab7)
mainFAQ.grid(column=0, row=0)

row = 1
ttk.Label(mainFAQ, text="FAQ: ", font=("Arial", 15), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=0, padx=30, pady=3, sticky=W)
ttk.Label(mainFAQ, font=("Arial", 14), justify = LEFT, wraplength = 1000, text="This mod hurts my framerate.").grid(column=0, columnspan = 1, row=row, padx=30, pady=3, sticky=W)
ttk.Label(mainFAQ, font=("Arial", 11), justify = LEFT, wraplength = 1000, text="     The more bots you have on a map at once, the lower your framerate tends to be.").grid(column=0, columnspan = 1, row=row+1, padx=30, pady=3, sticky=W)
row+=2
ttk.Label(mainFAQ, font=("Arial", 14), justify = LEFT, wraplength = 1000, text="I think something is wrong with the spawns, and I'm getting the wrong types of bots in my raids.").grid(column=0, columnspan = 1, row=row, padx=30, pady=3, sticky=W)
ttk.Label(mainFAQ, font=("Arial", 11), justify = LEFT, wraplength = 1000, text="     Enable faction identifiers and check their armband colours to verify what type of bots you're seeing, and then open the donottouch/debug/~spawnOverview.JSON file to double-check that there's an error. If these both show that something is wrong, feel free to leave me a comment on the site, or DM / ping me on Guilded (Though I check Guilded less regularly).").grid(column=0, columnspan = 1, row=row+1, padx=30, pady=3, sticky=W)
row+=2
ttk.Label(mainFAQ, font=("Arial", 14), justify = LEFT, wraplength = 1000, text="The AI is too [easy/hard]").grid(column=0, columnspan = 1, row=row, padx=30, pady=3, sticky=W)
ttk.Label(mainFAQ, font=("Arial", 11), justify = LEFT, wraplength = 1000, text="     Play around with the settings in the difficulty tab. I recommend adjusting the numbers up or down every time you beat a raid too easily / die in a fight that was too hard, until you find a comfortable value. Note that each level of ingame difficulty effectively adds 1 to the main difficulty value, so bots are at X + 1 on easy, X + 3 on hard, etc.. Check the readme for more details.").grid(column=0, columnspan = 1, row=row+1, padx=30, pady=3, sticky=W)
row+=2
ttk.Label(mainFAQ, font=("Arial", 14), justify = LEFT, wraplength = 1000, text="I'm getting a server error that says something like 'cannot read property blahblahblah of undefined'").grid(column=0, columnspan = 1, row=row, padx=30, pady=3, sticky=W)
ttk.Label(mainFAQ, font=("Arial", 11), justify = LEFT, wraplength = 1000, text="     This is usually due to a bad item somewhere in the game. This can be due to a whole bunch of reasons, but before you report it you should see if (A) the error occurs on a fresh character, (B) see if the error occurs if you remove this mod, and then (C) see if the error occurs if you remove ALL your OTHER mods (So this mod is the only one in your mods folder). Doing these three things should give you a better idea of the error's source: If it's a problem with your profile (If A fixes it), with my mod (If B fixes it but C does not), or with another mod adding a bad item or interacting with this mod in a weird way (Any other combination)").grid(column=0, columnspan = 1, row=row+1, padx=30, pady=3, sticky=W)
row+=2
ttk.Label(mainFAQ, font=("Arial", 14), justify = LEFT, wraplength = 1000, text="How do I get more spawns earlier in the raid?").grid(column=0, columnspan = 1, row=row, padx=30, pady=3, sticky=W)
ttk.Label(mainFAQ, font=("Arial", 11), justify = LEFT, wraplength = 1000, text="     Check out the spawn timing options. If you want even more control, check out the advanced spawn config.").grid(column=0, columnspan = 1, row=row+1, padx=30, pady=3, sticky=W)
row+=2
ttk.Label(mainFAQ, font=("Arial", 14), justify = LEFT, wraplength = 1000, text="Something happened and now the config is broken / I'm getting an error like 'Unexpected token , in JSON at position blahblah'.").grid(column=0, columnspan = 1, row=row, padx=30, pady=3, sticky=W)
ttk.Label(mainFAQ, font=("Arial", 11), justify = LEFT, wraplength = 1000, text="     You've entered a value into the config that's causing problems. This could be quotation marks in a non-array entry or missing square brackets in an array entry. If you can't figure out what's wrong, you'll need to replace your config with a fresh copy of the default config.").grid(column=0, columnspan = 1, row=row+1, padx=30, pady=3, sticky=W)
row+=2
ttk.Label(mainFAQ, font=("Arial", 14), justify = LEFT, wraplength = 1000, text="Something else is wrong.").grid(column=0, columnspan = 1, row=row, padx=30, pady=3, sticky=W)
ttk.Label(mainFAQ, font=("Arial", 11), justify = LEFT, wraplength = 1000, text="     Check out the readme to see if your problem is mentioned there. If not, feel free to post your question on the site.").grid(column=0, columnspan = 1, row=row+1, padx=30, pady=3, sticky=W)
row+=2
ttk.Label(mainFAQ, font=("Arial", 14), justify = LEFT, wraplength = 1000, text="I'm having trouble navigating the readme.").grid(column=0, columnspan = 1, row=row, padx=30, pady=3, sticky=W)
ttk.Label(mainFAQ, font=("Arial", 11), justify = LEFT, wraplength = 1000, text="     The readme was designed for use with the manually-edited config file, and so it refers to the somewhat clunkier entry names found there. This may be updated in the future, but in the meantime ctrl-f is the best way to navigate the readme.").grid(column=0, columnspan = 1, row=row+1, padx=30, pady=3, sticky=W)
row+=2

#Behaviour tab

mainBehaviour = ttk.Frame(tab8)
mainBehaviour.grid(column=0, row=0)

ttk.Label(mainBehaviour, text="Behaviour options: ", font=("Arial", 15), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=0, padx=30, pady=15, sticky=W)

behaviourExplanation = ttk.Frame(mainBehaviour)
behaviourExplanation.grid(column=0, columnspan=10, row=1)

pmcBehaviour = ttk.Frame(mainBehaviour)
pmcBehaviour.grid(column=0, row=2)

raiderBehaviour = ttk.Frame(mainBehaviour)
raiderBehaviour.grid(column=1, row=2)

scavBehaviour = ttk.Frame(mainBehaviour)
scavBehaviour.grid(column=2, row=2)

enableBehaviourChanges =tk.BooleanVar()
makeRadioLabelCUSTOM(behaviourExplanation, "Enable behaviour changes: ", ("Arial", 12), 1, enableBehaviourChanges, config["AIbehaviourChanges"]["enabled"], "Original config name: AIbehaviourChanges > highLevelAIs > enabled\n\nIf disabled, all AIs will use their default behaviour.")
ttk.Label(behaviourExplanation, text="Please note: Enabling this option interferes with the ability of Usecs and Bears to properly fight eachother.\nIt can also occasionaly make them friendly to scavs. This isn't a bug in the mod, and it can't (As far as I know) be fixed.\nIt's just a an aspect of this feature, due to how things are done behind the scenes in AKI.\n\nIn addition, this option is very new, and still has bugs such as AIs occasionally becoming unresponsive.\n\nNevertheless, I recommend this option be left enabled, as I feel it significantly improves gameplay, despite its occasional bugs.", anchor = N, justify = LEFT).grid(column=0, columnspan = 10, row=0, padx=30, pady=5, sticky=W)

ttk.Label(pmcBehaviour, text="High level AI behaviour weights: ", font=("Arial", 15), anchor = N, justify = LEFT).grid(column=0, columnspan = 2, row=1, padx=30, pady=8, sticky=W)
#hLB = CreateToolTip(dA3, "These behaviour changes use the same 'weight' system as ")

ttk.Label(pmcBehaviour, text=" ", font=("Arial", 10), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=0, padx=30, pady=5, sticky=W)
ttk.Label(pmcBehaviour, text=" ", anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=2, padx=30, pady=8, sticky=W)
pmcDefault = makeLabelSpinboxCUSTOM(pmcBehaviour, W, "Default: ",3, 1, config["AIbehaviourChanges"]["highLevelAIs"]["default"], 5, "Original config name: AIbehaviourChanges > highLevelAIs > default\n\nThis is the relative chance that this category of bot will retain its default behaviour.")
pmcBalanced = makeLabelSpinboxCUSTOM(pmcBehaviour, W, "Balanced: ",4, 1, config["AIbehaviourChanges"]["highLevelAIs"]["balanced"], 5, "Original config name: AIbehaviourChanges > highLevelAIs > balanced\n\nThis is the relative chance that this category of bot will be given the behaviour of a standard raider.")
pmcAggressive = makeLabelSpinboxCUSTOM(pmcBehaviour, W, "Aggressive: ",5, 1, config["AIbehaviourChanges"]["highLevelAIs"]["aggressive"], 5, "Original config name: AIbehaviourChanges > highLevelAIs > aggressive\n\nThis is the relative chance that this category of bot will be given more aggressive behaviour.")
pmcSniper = makeLabelSpinboxCUSTOM(pmcBehaviour, W, "Sniper: ",6, 1, config["AIbehaviourChanges"]["highLevelAIs"]["ranged"], 5, "Original config name: AIbehaviourChanges > highLevelAIs > sniper\n\nThis is the relative chance that this category of bot will be given 'sniper-y' behaviour. This one is weird, and doesn't work particularly well since it can be assigned to bots with weapons that do not suit the 'sniper' role.")
#pmcPatroller = makeLabelSpinboxCUSTOM(pmcBehaviour, W, "Patroller: ",7, 1, config["AIbehaviourChanges"]["highLevelAIs"]["patroller"], 5, "Original config name: AIbehaviourChanges > highLevelAIs > patroller\n\nThis is the relative chance that this category of bot will rapidly and continuously patrol its zone.")
#pmcScout = makeLabelSpinboxCUSTOM(pmcBehaviour, W, "Scout: ",8, 1, config["AIbehaviourChanges"]["highLevelAIs"]["scout"], 5, "Original config name: AIbehaviourChanges > highLevelAIs > scout\n\nThis is the relative chance that this category of bot will be given 'scout' behaviour. This one needs testing, not 100% what this does.")
#pmcCursed = makeLabelSpinboxCUSTOM(pmcBehaviour, W, "Super aggressive: ",9, 1, config["AIbehaviourChanges"]["highLevelAIs"]["super_aggressive"], 5, "Original config name: AIbehaviourChanges > highLevelAIs > super_aggressive\n\nThis is the relative chance that this category of bot will be given behaviour that makes them incredibly aggressive.")

ttk.Label(raiderBehaviour, text="Mid level AI behaviour weights: ", font=("Arial", 15), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=1, padx=30, pady=8, sticky=W)

ttk.Label(raiderBehaviour, text=" ", font=("Arial", 10), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=0, padx=30, pady=5, sticky=W)
ttk.Label(raiderBehaviour, text=" ", anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=2, padx=30, pady=8, sticky=W)
raiderDefault = makeLabelSpinboxCUSTOM(raiderBehaviour, W, "Default: ",3, 1, config["AIbehaviourChanges"]["midLevelAIs"]["default"], 5, "Original config name: AIbehaviourChanges > midLevelAIs > default\n\nThis is the relative chance that this category of bot will retain its default behaviour.")
raiderBalanced = makeLabelSpinboxCUSTOM(raiderBehaviour, W, "Balanced: ",4, 1, config["AIbehaviourChanges"]["midLevelAIs"]["balanced"], 5, "Original config name: AIbehaviourChanges > midLevelAIs > balanced\n\nThis is the relative chance that this category of bot will be given the behaviour of a standard raider.")
raiderAggressive = makeLabelSpinboxCUSTOM(raiderBehaviour, W, "Aggressive: ",5, 1, config["AIbehaviourChanges"]["midLevelAIs"]["aggressive"], 5, "Original config name: AIbehaviourChanges > midLevelAIs > aggressive\n\nThis is the relative chance that this category of bot will be given more aggressive behaviour.")
raiderSniper = makeLabelSpinboxCUSTOM(raiderBehaviour, W, "Sniper: ",6, 1, config["AIbehaviourChanges"]["midLevelAIs"]["ranged"], 5, "Original config name: AIbehaviourChanges > midLevelAIs > sniper\n\nThis is the relative chance that this category of bot will be given 'sniper-y' behaviour. This one is weird, and doesn't work particularly well since it can be assigned to bots with weapons that do not suit the 'sniper' role.")
#raiderPatroller = makeLabelSpinboxCUSTOM(raiderBehaviour, W, "Patroller: ",7, 1, config["AIbehaviourChanges"]["midLevelAIs"]["patroller"], 5, "Original config name: AIbehaviourChanges > midLevelAIs > patroller\n\nThis is the relative chance that this category of bot will rapidly and continuously patrol its zone.")
#raiderScout = makeLabelSpinboxCUSTOM(raiderBehaviour, W, "Scout: ",8, 1, config["AIbehaviourChanges"]["midLevelAIs"]["scout"], 5, "Original config name: AIbehaviourChanges > midLevelAIs > scout\n\nThis is the relative chance that this category of bot will be given 'scout' behaviour. This one needs testing, not 100% what this does.")
#raiderCursed = makeLabelSpinboxCUSTOM(raiderBehaviour, W, "Super aggressive: ",9, 1, config["AIbehaviourChanges"]["midLevelAIs"]["super_aggressive"], 5, "Original config name: AIbehaviourChanges > midLevelAIs > super_aggressive\n\nThis is the relative chance that this category of bot will be given behaviour that makes them incredibly aggressive.")

ttk.Label(scavBehaviour, text="Low level AI behaviour weights: ", font=("Arial", 15), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=1, padx=30, pady=8, sticky=W)

ttk.Label(scavBehaviour, text=" ", font=("Arial", 10), anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=0, padx=30, pady=5, sticky=W)
ttk.Label(scavBehaviour, text=" ", anchor = N, justify = LEFT).grid(column=0, columnspan = 1, row=2, padx=30, pady=8, sticky=W)
scavDefault = makeLabelSpinboxCUSTOM(scavBehaviour, W, "Default: ",3, 1, config["AIbehaviourChanges"]["lowLevelAIs"]["default"], 5, "Original config name: AIbehaviourChanges > lowLevelAIs > default\n\nThis is the relative chance that this category of bot will retain its default behaviour.")
scavBalanced = makeLabelSpinboxCUSTOM(scavBehaviour, W, "Balanced: ",4, 1, config["AIbehaviourChanges"]["lowLevelAIs"]["balanced"], 5, "Original config name: AIbehaviourChanges > lowLevelAIs > balanced\n\nThis is the relative chance that this category of bot will be given the behaviour of a standard raider.")
scavAggressive = makeLabelSpinboxCUSTOM(scavBehaviour, W, "Aggressive: ",5, 1, config["AIbehaviourChanges"]["lowLevelAIs"]["aggressive"], 5, "Original config name: AIbehaviourChanges > lowLevelAIs > aggressive\n\nThis is the relative chance that this category of bot will be given more aggressive behaviour.")
scavSniper = makeLabelSpinboxCUSTOM(scavBehaviour, W, "Sniper: ",6, 1, config["AIbehaviourChanges"]["lowLevelAIs"]["ranged"], 5, "Original config name: AIbehaviourChanges > lowLevelAIs > sniper\n\nThis is the relative chance that this category of bot will be given 'sniper-y' behaviour. This one is weird, and doesn't work particularly well since it can be assigned to bots with weapons that do not suit the 'sniper' role.")
#scavPatroller = makeLabelSpinboxCUSTOM(scavBehaviour, W, "Patroller: ",7, 1, config["AIbehaviourChanges"]["lowLevelAIs"]["patroller"], 5, "Original config name: AIbehaviourChanges > lowLevelAIs > patroller\n\nThis is the relative chance that this category of bot will rapidly and continuously patrol its zone.")
#scavScout = makeLabelSpinboxCUSTOM(scavBehaviour, W, "Scout: ",8, 1, config["AIbehaviourChanges"]["lowLevelAIs"]["scout"], 5, "Original config name: AIbehaviourChanges > lowLevelAIs > scout\n\nThis is the relative chance that this category of bot will be given 'scout' behaviour. This one needs testing, not 100% what this does.")
#scavCursed = makeLabelSpinboxCUSTOM(scavBehaviour, W, "Super aggressive: ",9, 1, config["AIbehaviourChanges"]["lowLevelAIs"]["super_aggressive"], 5, "Original config name: AIbehaviourChanges > lowLevelAIs > super_aggressive\n\nThis is the relative chance that this category of bot will be given behaviour that makes them incredibly aggressive.")

####################################################
tabControl.pack(expand=1, fill="both")

menu = Menu(root)
root.config(menu=menu)

savemenu = Menu(menu)
menu.add_cascade(label = "File", menu=savemenu)
savemenu.add_command(label = "Save", command = lambda: saveValues("config/config.json"))
savemenu.add_command(label = "Backup config", command = backupConfig)
savemenu.add_command(label = "Restore backup config", command = restoreBackup)
savemenu.add_command(label = "Restore default config", command = restoreDefault)

root.mainloop()